DO NOT MERGE - Merge pie-platform-release (PPRL.190705.004) into master

Bug: 136196576
Change-Id: I127ae61add2e34184b3c65ca64693cccf62b8a6d
diff --git a/.clang-format-2 b/.clang-format-2
deleted file mode 100644
index 41591ce..0000000
--- a/.clang-format-2
+++ /dev/null
@@ -1,10 +0,0 @@
-BasedOnStyle: Google
-AllowShortFunctionsOnASingleLine: Inline
-ColumnLimit: 100
-CommentPragmas: NOLINT:.*
-DerivePointerAlignment: false
-IndentWidth: 2
-PointerAlignment: Left
-TabWidth: 2
-UseTab: Never
-PenaltyExcessCharacter: 32
diff --git a/.clang-format-2 b/.clang-format-2
new file mode 120000
index 0000000..7ab20d4
--- /dev/null
+++ b/.clang-format-2
@@ -0,0 +1 @@
+../../build/soong/scripts/system-clang-format-2
\ No newline at end of file
diff --git a/.clang-format-4 b/.clang-format-4
deleted file mode 100644
index ae4a451..0000000
--- a/.clang-format-4
+++ /dev/null
@@ -1,11 +0,0 @@
-BasedOnStyle: Google
-AccessModifierOffset: -2
-AllowShortFunctionsOnASingleLine: Inline
-ColumnLimit: 100
-CommentPragmas: NOLINT:.*
-DerivePointerAlignment: false
-IndentWidth: 4
-PointerAlignment: Left
-TabWidth: 4
-UseTab: Never
-PenaltyExcessCharacter: 32
diff --git a/.clang-format-4 b/.clang-format-4
new file mode 120000
index 0000000..ddcf5a2
--- /dev/null
+++ b/.clang-format-4
@@ -0,0 +1 @@
+../../build/soong/scripts/system-clang-format
\ No newline at end of file
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index 7c57258..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,18 +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.
-#
-LOCAL_PATH := $(my-dir)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/CleanSpec.mk b/CleanSpec.mk
index e6f8716..f6ef906 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -77,3 +77,14 @@
 $(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/)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/sbin/charger)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sbin/charger)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/sbin)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sbin)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product_services)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product_services.img)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product_services)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/product_services)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/product_services)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/debug_ramdisk/product_services)
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..66d0c92
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,47 @@
+{
+  "presubmit": [
+    {
+      "name": "adbd_test"
+    },
+    {
+      "name": "debuggerd_test"
+    },
+    {
+      "name": "fs_mgr_unit_test"
+    },
+    {
+      "name": "fs_mgr_vendor_overlay_test"
+    },
+    {
+      "name": "init_tests"
+    },
+    {
+      "name": "libbase_test"
+    },
+    {
+      "name": "libprocinfo_test"
+    },
+    {
+      "name": "libutils_test"
+    },
+    {
+      "name": "memunreachable_test"
+    },
+    {
+      "name": "memunreachable_unit_test"
+    },
+    {
+      "name": "memunreachable_unit_test",
+      "host": true
+    },
+    {
+      "name": "memunreachable_binder_test"
+    },
+    {
+      "name": "propertyinfoserializer_tests"
+    },
+    {
+      "name": "ziparchive-tests"
+    }
+  ]
+}
diff --git a/adb/Android.bp b/adb/Android.bp
index 0858a6c..b6aff3e 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -12,26 +12,632 @@
 // 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",
+cc_defaults {
+    name: "adb_defaults",
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wexit-time-destructors",
+        "-Wno-unused-parameter",
+        "-Wno-missing-field-initializers",
+        "-Wthread-safety",
+        "-Wvla",
+        "-DADB_HOST=1",         // overridden by adbd_defaults
+        "-DALLOW_ADBD_ROOT=0",  // overridden by adbd_defaults
+        "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION=1",
     ],
-    libs: [
-        "adb_py",
-    ],
-    version: {
-        py2: {
-            enabled: true,
+    cpp_std: "experimental",
+
+    use_version_lib: true,
+    compile_multilib: "first",
+
+    target: {
+        darwin: {
+            host_ldlibs: [
+                "-lpthread",
+                "-framework CoreFoundation",
+                "-framework IOKit",
+                "-lobjc",
+            ],
         },
-        py3: {
+
+        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",
+
+                // Unlike on Linux, -std=gnu++ doesn't set _GNU_SOURCE on Windows.
+                "-D_GNU_SOURCE",
+
+                // MinGW hides some things behind _POSIX_SOURCE.
+                "-D_POSIX_SOURCE",
+
+                // libusb uses __stdcall on a variadic function, which gets ignored.
+                "-Wno-ignored-attributes",
+
+                // Not supported yet.
+                "-Wno-thread-safety",
+            ],
+
+            host_ldlibs: [
+                "-lws2_32",
+                "-lgdi32",
+                "-luserenv",
+            ],
+        },
+    },
+}
+
+cc_defaults {
+    name: "adbd_defaults",
+    defaults: ["adb_defaults"],
+
+    cflags: ["-UADB_HOST", "-DADB_HOST=0"],
+    product_variables: {
+        debuggable: {
+            cflags: [
+                "-UALLOW_ADBD_ROOT",
+                "-DALLOW_ADBD_ROOT=1",
+                "-DALLOW_ADBD_DISABLE_VERITY",
+                "-DALLOW_ADBD_NO_AUTH",
+            ],
+        },
+    },
+}
+
+cc_defaults {
+    name: "host_adbd_supported",
+
+    host_supported: true,
+    target: {
+        linux: {
+            enabled: true,
+            host_ldlibs: [
+                "-lresolv", // b64_pton
+                "-lutil", // forkpty
+            ],
+        },
+        darwin: {
+            enabled: false,
+        },
+        windows: {
             enabled: false,
         },
     },
 }
 
-python_binary_host {
+// 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",
+    ],
+
+    generated_headers: ["platform_tools_version"],
+
+    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",
+        "libutils",
+        "liblog",
+        "libcutils",
+    ],
+}
+
+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/adb_install.cpp",
+        "client/line_printer.cpp",
+        "shell_service_protocol.cpp",
+    ],
+
+    static_libs: [
+        "libadb_host",
+        "libbase",
+        "libcutils",
+        "libcrypto_utils",
+        "libcrypto",
+        "libdiagnose_usb",
+        "liblog",
+        "libmdnssd",
+        "libusb",
+        "libutils",
+        "liblog",
+        "libcutils",
+    ],
+
+    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: [],
+
+    required: [
+        "deploypatchgenerator",
+    ],
+
+    // Archive adb, adb.exe.
+    dist: {
+        targets: [
+            "dist_files",
+            "sdk",
+            "win_sdk",
+        ],
+    },
+
+    target: {
+        darwin: {
+            cflags: [
+                "-Wno-sizeof-pointer-memaccess",
+            ],
+        },
+        windows: {
+            enabled: true,
+            ldflags: ["-municode"],
+            shared_libs: ["AdbWinApi"],
+            required: [
+                "AdbWinUsbApi",
+            ],
+        },
+    },
+}
+
+// libadbd_core contains the common sources to build libadbd and libadbd_services.
+cc_library_static {
+    name: "libadbd_core",
+    defaults: ["adbd_defaults", "host_adbd_supported"],
+    recovery_available: true,
+
+    // libminadbd wants both, as it's used to build native tests.
+    compile_multilib: "both",
+
+    srcs: libadb_srcs + libadb_posix_srcs + [
+        "daemon/auth.cpp",
+        "daemon/jdwp_service.cpp",
+    ],
+
+    local_include_dirs: [
+        "daemon/include",
+    ],
+
+    generated_headers: ["platform_tools_version"],
+
+    static_libs: [
+        "libadbconnection_server",
+        "libdiagnose_usb",
+    ],
+
+    shared_libs: [
+        "libasyncio",
+        "libbase",
+        "libcrypto",
+        "libcrypto_utils",
+        "libcutils",
+        "liblog",
+    ],
+
+    target: {
+        android: {
+            whole_static_libs: [
+                "libqemu_pipe",
+            ],
+            srcs: [
+                "daemon/transport_qemu.cpp",
+                "daemon/usb.cpp",
+                "daemon/usb_ffs.cpp",
+                "daemon/usb_legacy.cpp",
+            ]
+        },
+        linux_glibc: {
+            srcs: [
+                "daemon/usb_dummy.cpp",
+            ]
+        }
+    },
+}
+
+cc_library {
+    name: "libadbd_services",
+    defaults: ["adbd_defaults", "host_adbd_supported"],
+    recovery_available: true,
+    compile_multilib: "both",
+
+    srcs: [
+        "daemon/file_sync_service.cpp",
+        "daemon/services.cpp",
+        "daemon/shell_service.cpp",
+        "shell_service_protocol.cpp",
+    ],
+
+    cflags: [
+        "-D_GNU_SOURCE",
+        "-Wno-deprecated-declarations",
+    ],
+
+    static_libs: [
+        "libadbconnection_server",
+        "libadbd_core",
+        "libdiagnose_usb",
+    ],
+
+    shared_libs: [
+        "libasyncio",
+        "libbase",
+        "libcrypto",
+        "libcrypto_utils",
+        "libcutils",
+        "liblog",
+    ],
+
+    product_variables: {
+        debuggable: {
+            required: [
+                "remount",
+            ],
+        },
+    },
+
+    target: {
+        android: {
+            srcs: [
+                "daemon/abb_service.cpp",
+                "daemon/framebuffer_service.cpp",
+                "daemon/mdns.cpp",
+                "daemon/reboot_service.cpp",
+                "daemon/remount_service.cpp",
+                "daemon/restart_service.cpp",
+                "daemon/set_verity_enable_state_service.cpp",
+            ],
+            static_libs: [
+                "libavb_user",
+            ],
+            shared_libs: [
+                "libbootloader_message",
+                "libmdnssd",
+                "libext4_utils",
+                "libfec",
+                "libfs_mgr",
+                "libselinux",
+            ],
+        },
+        recovery: {
+            exclude_srcs: [
+                "daemon/abb_service.cpp",
+            ],
+        },
+    },
+}
+
+cc_library {
+    name: "libadbd",
+    defaults: ["adbd_defaults", "host_adbd_supported"],
+    recovery_available: true,
+
+    // Avoid getting duplicate symbol of android::build::GetBuildNumber().
+    use_version_lib: false,
+
+    // libminadbd wants both, as it's used to build native tests.
+    compile_multilib: "both",
+
+    // libadbd doesn't build any additional source, but to expose libadbd_core as a shared library.
+    whole_static_libs: [
+        "libadbd_core",
+    ],
+
+    shared_libs: [
+        "libadbd_services",
+        "libasyncio",
+        "libbase",
+        "libcrypto",
+        "libcrypto_utils",
+        "libcutils",
+        "liblog",
+    ],
+
+    export_include_dirs: [
+        "daemon/include",
+    ],
+}
+
+cc_binary {
+    name: "adbd",
+    defaults: ["adbd_defaults", "host_adbd_supported"],
+    recovery_available: true,
+
+    srcs: [
+        "daemon/main.cpp",
+    ],
+
+    cflags: [
+        "-D_GNU_SOURCE",
+        "-Wno-deprecated-declarations",
+    ],
+
+    strip: {
+        keep_symbols: true,
+    },
+
+    shared_libs: [
+        "libadbd",
+        "libadbd_services",
+        "libbase",
+        "libcap",
+        "libcrypto",
+        "libcutils",
+        "liblog",
+        "libminijail",
+        "libselinux",
+    ],
+}
+
+cc_binary {
+    name: "static_adbd",
+    defaults: ["adbd_defaults", "host_adbd_supported"],
+
+    recovery_available: false,
+    static_executable: true,
+    host_supported: false,
+
+    srcs: [
+        "daemon/main.cpp",
+    ],
+
+    cflags: [
+        "-D_GNU_SOURCE",
+        "-Wno-deprecated-declarations",
+    ],
+
+    strip: {
+        keep_symbols: true,
+    },
+
+    static_libs: [
+        "libadbconnection_server",
+        "libadbd",
+        "libadbd_services",
+        "libasyncio",
+        "libavb_user",
+        "libbase",
+        "libbootloader_message",
+        "libcap",
+        "libcrypto",
+        "libcrypto_utils",
+        "libcutils",
+        "libdiagnose_usb",
+        "libext4_utils",
+        "libfec",
+        "libfec_rs",
+        "libfs_mgr",
+        "liblog",
+        "liblp",
+        "libmdnssd",
+        "libminijail",
+        "libselinux",
+        "libsquashfs_utils",
+    ],
+}
+
+cc_binary {
+    name: "abb",
+
+    defaults: ["adbd_defaults"],
+    recovery_available: false,
+
+    srcs: [
+        "daemon/abb.cpp",
+    ],
+
+    cflags: [
+        "-D_GNU_SOURCE",
+        "-Wno-deprecated-declarations",
+    ],
+
+    strip: {
+        keep_symbols: true,
+    },
+
+    static_libs: [
+        "libadbd_core",
+        "libadbd_services",
+        "libcmd",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "liblog",
+        "libutils",
+        "libselinux",
+    ],
+}
+
+cc_test {
+    name: "adbd_test",
+    defaults: ["adbd_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_test_host {
+    name: "adb_integration_test_adb",
+    main: "test_adb.py",
+    srcs: [
+        "test_adb.py",
+    ],
+    test_config: "adb_integration_test_adb.xml",
+    test_suites: ["general-tests"],
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+        },
+    },
+}
+
+python_test_host {
     name: "adb_integration_test_device",
     main: "test_device.py",
     srcs: [
@@ -40,6 +646,8 @@
     libs: [
         "adb_py",
     ],
+    test_config: "adb_integration_test_device.xml",
+    test_suites: ["general-tests"],
     version: {
         py2: {
             enabled: true,
diff --git a/adb/Android.mk b/adb/Android.mk
deleted file mode 100644
index c473ed2..0000000
--- a/adb/Android.mk
+++ /dev/null
@@ -1,388 +0,0 @@
-# Copyright 2005 The Android Open Source Project
-#
-# Android.mk for adb
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(LOCAL_PATH)/../platform_tools_tool_version.mk
-
-adb_host_sanitize :=
-adb_target_sanitize :=
-
-ADB_COMMON_CFLAGS := \
-    -frtti \
-    -Wall -Wextra -Werror \
-    -Wno-unused-parameter \
-    -Wno-missing-field-initializers \
-    -Wvla \
-    -DADB_VERSION="\"$(tool_version)\"" \
-
-ADB_COMMON_posix_CFLAGS := \
-    -Wexit-time-destructors \
-    -Wthread-safety \
-
-ADB_COMMON_linux_CFLAGS := \
-    $(ADB_COMMON_posix_CFLAGS) \
-
-ADB_COMMON_darwin_CFLAGS := \
-    $(ADB_COMMON_posix_CFLAGS) \
-
-# Define windows.h and tchar.h Unicode preprocessor symbols so that
-# CreateFile(), _tfopen(), etc. map to versions that take wchar_t*, breaking the
-# build if you accidentally pass char*. Fix by calling like:
-#   std::wstring path_wide;
-#   if (!android::base::UTF8ToWide(path_utf8, &path_wide)) { /* error handling */ }
-#   CreateFileW(path_wide.c_str());
-ADB_COMMON_windows_CFLAGS := \
-    -DUNICODE=1 -D_UNICODE=1 \
-    -D_POSIX_SOURCE
-
-# libadb
-# =========================================================
-
-# Much of adb is duplicated in bootable/recovery/minadb and fastboot. Changes
-# made to adb rarely get ported to the other two, so the trees have diverged a
-# bit. We'd like to stop this because it is a maintenance nightmare, but the
-# divergence makes this difficult to do all at once. For now, we will start
-# small by moving common files into a static library. Hopefully some day we can
-# get enough of adb in here that we no longer need minadb. https://b/17626262
-LIBADB_SRC_FILES := \
-    adb.cpp \
-    adb_io.cpp \
-    adb_listeners.cpp \
-    adb_trace.cpp \
-    adb_utils.cpp \
-    fdevent.cpp \
-    sockets.cpp \
-    socket_spec.cpp \
-    sysdeps/errno.cpp \
-    transport.cpp \
-    transport_local.cpp \
-    transport_usb.cpp \
-
-LIBADB_TEST_SRCS := \
-    adb_io_test.cpp \
-    adb_listeners_test.cpp \
-    adb_utils_test.cpp \
-    fdevent_test.cpp \
-    socket_spec_test.cpp \
-    socket_test.cpp \
-    sysdeps_test.cpp \
-    sysdeps/stat_test.cpp \
-    transport_test.cpp \
-
-LIBADB_CFLAGS := \
-    $(ADB_COMMON_CFLAGS) \
-    -fvisibility=hidden \
-
-LIBADB_linux_CFLAGS := \
-    $(ADB_COMMON_linux_CFLAGS) \
-
-LIBADB_darwin_CFLAGS := \
-    $(ADB_COMMON_darwin_CFLAGS) \
-
-LIBADB_windows_CFLAGS := \
-    $(ADB_COMMON_windows_CFLAGS) \
-
-LIBADB_darwin_SRC_FILES := \
-    sysdeps_unix.cpp \
-    sysdeps/posix/network.cpp \
-    client/usb_dispatch.cpp \
-    client/usb_libusb.cpp \
-    client/usb_osx.cpp \
-
-LIBADB_linux_SRC_FILES := \
-    sysdeps_unix.cpp \
-    sysdeps/posix/network.cpp \
-    client/usb_dispatch.cpp \
-    client/usb_libusb.cpp \
-    client/usb_linux.cpp \
-
-LIBADB_windows_SRC_FILES := \
-    sysdeps_win32.cpp \
-    sysdeps/win32/errno.cpp \
-    sysdeps/win32/stat.cpp \
-    client/usb_dispatch.cpp \
-    client/usb_libusb.cpp \
-    client/usb_windows.cpp \
-
-LIBADB_TEST_windows_SRCS := \
-    sysdeps/win32/errno_test.cpp \
-    sysdeps_win32_test.cpp \
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libadbd_usb
-LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=0
-LOCAL_SRC_FILES := daemon/usb.cpp
-
-LOCAL_SANITIZE := $(adb_target_sanitize)
-
-# Even though we're building a static library (and thus there's no link step for
-# this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase libasyncio
-
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libadbd
-LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=0
-LOCAL_SRC_FILES := \
-    $(LIBADB_SRC_FILES) \
-    adbd_auth.cpp \
-    jdwp_service.cpp \
-    sysdeps/posix/network.cpp \
-
-LOCAL_SANITIZE := $(adb_target_sanitize)
-
-# Even though we're building a static library (and thus there's no link step for
-# this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libqemu_pipe libbase
-
-LOCAL_WHOLE_STATIC_LIBRARIES := libadbd_usb
-
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libadb
-LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=1
-LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
-LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
-LOCAL_CFLAGS_darwin := $(LIBADB_darwin_CFLAGS)
-LOCAL_SRC_FILES := \
-    $(LIBADB_SRC_FILES) \
-    adb_auth_host.cpp \
-    transport_mdns.cpp \
-
-LOCAL_SRC_FILES_darwin := $(LIBADB_darwin_SRC_FILES)
-LOCAL_SRC_FILES_linux := $(LIBADB_linux_SRC_FILES)
-LOCAL_SRC_FILES_windows := $(LIBADB_windows_SRC_FILES)
-
-LOCAL_SANITIZE := $(adb_host_sanitize)
-
-# Even though we're building a static library (and thus there's no link step for
-# this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase libmdnssd libusb
-
-LOCAL_C_INCLUDES_windows := development/host/windows/usb/api/
-LOCAL_MULTILIB := first
-
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := adbd_test
-LOCAL_CFLAGS := -DADB_HOST=0 $(LIBADB_CFLAGS)
-LOCAL_SRC_FILES := \
-    $(LIBADB_TEST_SRCS) \
-    $(LIBADB_TEST_linux_SRCS) \
-    shell_service.cpp \
-    shell_service_protocol.cpp \
-    shell_service_protocol_test.cpp \
-    shell_service_test.cpp \
-
-LOCAL_SANITIZE := $(adb_target_sanitize)
-LOCAL_STATIC_LIBRARIES := libadbd libcrypto_utils libcrypto libusb libmdnssd
-LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
-include $(BUILD_NATIVE_TEST)
-
-# libdiagnose_usb
-# =========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libdiagnose_usb
-LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_CFLAGS := $(LIBADB_CFLAGS)
-LOCAL_SRC_FILES := diagnose_usb.cpp
-# Even though we're building a static library (and thus there's no link step for
-# this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libbase
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-# adb_test
-# =========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := adb_test
-LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
-LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
-LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
-LOCAL_CFLAGS_darwin := $(LIBADB_darwin_CFLAGS)
-LOCAL_SRC_FILES := \
-    $(LIBADB_TEST_SRCS) \
-    adb_client.cpp \
-    bugreport.cpp \
-    bugreport_test.cpp \
-    line_printer.cpp \
-    services.cpp \
-    shell_service_protocol.cpp \
-    shell_service_protocol_test.cpp \
-
-LOCAL_SRC_FILES_linux := $(LIBADB_TEST_linux_SRCS)
-LOCAL_SRC_FILES_darwin := $(LIBADB_TEST_darwin_SRCS)
-LOCAL_SRC_FILES_windows := $(LIBADB_TEST_windows_SRCS)
-LOCAL_SANITIZE := $(adb_host_sanitize)
-LOCAL_STATIC_LIBRARIES := \
-    libadb \
-    libbase \
-    libcrypto_utils \
-    libcrypto \
-    libcutils \
-    libdiagnose_usb \
-    libmdnssd \
-    libgmock_host \
-    libusb \
-
-# Set entrypoint to wmain from sysdeps_win32.cpp instead of main
-LOCAL_LDFLAGS_windows := -municode
-LOCAL_LDLIBS_linux := -lrt -ldl -lpthread
-LOCAL_LDLIBS_darwin := -framework CoreFoundation -framework IOKit -lobjc
-LOCAL_LDLIBS_windows := -lws2_32 -luserenv
-LOCAL_SHARED_LIBRARIES_windows := AdbWinApi
-
-LOCAL_MULTILIB := first
-
-include $(BUILD_HOST_NATIVE_TEST)
-
-# adb host tool
-# =========================================================
-include $(CLEAR_VARS)
-
-LOCAL_LDLIBS_linux := -lrt -ldl -lpthread
-
-LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon -lobjc
-
-# Use wmain instead of main
-LOCAL_LDFLAGS_windows := -municode
-LOCAL_LDLIBS_windows := -lws2_32 -lgdi32
-LOCAL_SHARED_LIBRARIES_windows := AdbWinApi
-LOCAL_REQUIRED_MODULES_windows := AdbWinUsbApi
-
-LOCAL_SRC_FILES := \
-    adb_client.cpp \
-    bugreport.cpp \
-    client/main.cpp \
-    console.cpp \
-    commandline.cpp \
-    file_sync_client.cpp \
-    line_printer.cpp \
-    services.cpp \
-    shell_service_protocol.cpp \
-
-LOCAL_CFLAGS += \
-    $(ADB_COMMON_CFLAGS) \
-    -D_GNU_SOURCE \
-    -DADB_HOST=1 \
-
-LOCAL_CFLAGS_windows := \
-    $(ADB_COMMON_windows_CFLAGS)
-
-LOCAL_CFLAGS_linux := \
-    $(ADB_COMMON_linux_CFLAGS) \
-
-LOCAL_CFLAGS_darwin := \
-    $(ADB_COMMON_darwin_CFLAGS) \
-    -Wno-sizeof-pointer-memaccess -Wno-unused-parameter \
-
-LOCAL_MODULE := adb
-LOCAL_MODULE_TAGS := debug
-LOCAL_MODULE_HOST_OS := darwin linux windows
-
-LOCAL_SANITIZE := $(adb_host_sanitize)
-LOCAL_STATIC_LIBRARIES := \
-    libadb \
-    libbase \
-    libcrypto_utils \
-    libcrypto \
-    libdiagnose_usb \
-    liblog \
-    libmdnssd \
-    libusb \
-
-# Don't use libcutils on Windows.
-LOCAL_STATIC_LIBRARIES_darwin := libcutils
-LOCAL_STATIC_LIBRARIES_linux := libcutils
-
-LOCAL_CXX_STL := libc++_static
-
-# Don't add anything here, we don't want additional shared dependencies
-# on the host adb tool, and shared libraries that link against libc++
-# will violate ODR
-LOCAL_SHARED_LIBRARIES :=
-
-include $(BUILD_HOST_EXECUTABLE)
-
-$(call dist-for-goals,dist_files sdk win_sdk,$(LOCAL_BUILT_MODULE))
-ifdef HOST_CROSS_OS
-# Archive adb.exe for win_sdk build.
-$(call dist-for-goals,win_sdk,$(ALL_MODULES.host_cross_adb.BUILT))
-endif
-
-
-# adbd device daemon
-# =========================================================
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-    daemon/main.cpp \
-    daemon/mdns.cpp \
-    services.cpp \
-    file_sync_service.cpp \
-    framebuffer_service.cpp \
-    remount_service.cpp \
-    set_verity_enable_state_service.cpp \
-    shell_service.cpp \
-    shell_service_protocol.cpp \
-
-LOCAL_CFLAGS := \
-    $(ADB_COMMON_CFLAGS) \
-    $(ADB_COMMON_linux_CFLAGS) \
-    -DADB_HOST=0 \
-    -D_GNU_SOURCE \
-    -Wno-deprecated-declarations \
-
-LOCAL_CFLAGS += -DALLOW_ADBD_NO_AUTH=$(if $(filter userdebug eng,$(TARGET_BUILD_VARIANT)),1,0)
-
-ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
-LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
-LOCAL_CFLAGS += -DALLOW_ADBD_ROOT=1
-endif
-
-LOCAL_MODULE := adbd
-
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-
-LOCAL_SANITIZE := $(adb_target_sanitize)
-LOCAL_STRIP_MODULE := keep_symbols
-LOCAL_STATIC_LIBRARIES := \
-    libadbd \
-    libasyncio \
-    libavb_user \
-    libbase \
-    libqemu_pipe \
-    libbootloader_message \
-    libfs_mgr \
-    libfec \
-    libfec_rs \
-    libselinux \
-    liblog \
-    libext4_utils \
-    libsquashfs_utils \
-    libcutils \
-    libbase \
-    libcrypto_utils \
-    libcrypto \
-    libminijail \
-    libmdnssd \
-    libdebuggerd_handler \
-
-include $(BUILD_EXECUTABLE)
-
-# adb integration test
-# =========================================================
-$(call dist-for-goals,sdk,$(ALL_MODULES.adb_integration_test_adb.BUILT))
-$(call dist-for-goals,sdk,$(ALL_MODULES.adb_integration_test_device.BUILT))
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/adb/CPPLINT.cfg b/adb/CPPLINT.cfg
deleted file mode 100644
index f496490..0000000
--- a/adb/CPPLINT.cfg
+++ /dev/null
@@ -1,2 +0,0 @@
-set noparent
-filter=-build/header_guard,-build/include,-readability/function,-whitespace/indent
diff --git a/adb/NOTICE b/adb/NOTICE
index ff47c95..9ffcc08 100644
--- a/adb/NOTICE
+++ b/adb/NOTICE
@@ -189,63 +189,3 @@
 
    END OF TERMS AND CONDITIONS
 
-------------------------------------------------------------
-libwinpthread license:
-------------------------------------------------------------
-Copyright (c) 2011 mingw-w64 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.
-
-
-/*
- * Parts of this library are derived by:
- *
- * Posix Threads library for Microsoft Windows
- *
- * Use at own risk, there is no implied warranty to this code.
- * It uses undocumented features of Microsoft Windows that can change
- * at any time in the future.
- *
- * (C) 2010 Lockless 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:
- *
- *
- *  * 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 Lockless 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" AN
- * 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 HOLDER 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/adb/OVERVIEW.TXT b/adb/OVERVIEW.TXT
index 29a6992..f0b184c 100644
--- a/adb/OVERVIEW.TXT
+++ b/adb/OVERVIEW.TXT
@@ -103,9 +103,6 @@
            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.
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/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 c791c7b..24d4292 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -44,6 +44,8 @@
 #include <android-base/parsenetaddress.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <build/version.h>
+#include <platform_tools_version.h>
 
 #include "adb_auth.h"
 #include "adb_io.h"
@@ -64,45 +66,13 @@
     // Don't change the format of this --- it's parsed by ddmlib.
     return android::base::StringPrintf(
         "Android Debug Bridge version %d.%d.%d\n"
-        "Version %s\n"
+        "Version %s-%s\n"
         "Installed as %s\n",
-        ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_VERSION,
+        ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
+        PLATFORM_TOOLS_VERSION, android::build::GetBuildNumber().c_str(),
         android::base::GetExecutablePath().c_str());
 }
 
-void fatal(const char *fmt, ...) {
-    va_list ap;
-    va_start(ap, fmt);
-    char buf[1024];
-    vsnprintf(buf, sizeof(buf), fmt, ap);
-
-#if ADB_HOST
-    fprintf(stderr, "error: %s\n", buf);
-#else
-    LOG(ERROR) << "error: " << buf;
-#endif
-
-    va_end(ap);
-    abort();
-}
-
-void fatal_errno(const char* fmt, ...) {
-    int err = errno;
-    va_list ap;
-    va_start(ap, fmt);
-    char buf[1024];
-    vsnprintf(buf, sizeof(buf), fmt, ap);
-
-#if ADB_HOST
-    fprintf(stderr, "error: %s: %s\n", buf, strerror(err));
-#else
-    LOG(ERROR) << "error: " << buf << ": " << strerror(err);
-#endif
-
-    va_end(ap);
-    abort();
-}
-
 uint32_t calculate_apacket_checksum(const apacket* p) {
     uint32_t sum = 0;
     for (size_t i = 0; i < p->msg.data_length; ++i) {
@@ -115,7 +85,7 @@
 {
     apacket* p = new apacket();
     if (p == nullptr) {
-      fatal("failed to allocate an apacket");
+        LOG(FATAL) << "failed to allocate an apacket";
     }
 
     memset(&p->msg, 0, sizeof(p->msg));
@@ -131,12 +101,21 @@
 {
     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
@@ -248,21 +227,12 @@
                    << connection_str.length() << ")";
     }
 
-    cp->payload = std::move(connection_str);
+    cp->payload.assign(connection_str.begin(), connection_str.end());
     cp->msg.data_length = cp->payload.size();
 
     send_packet(cp, t);
 }
 
-// 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());
 
@@ -286,11 +256,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);
             }
@@ -310,6 +280,9 @@
     } else if (type == "sideload") {
         D("setting connection_state to kCsSideload");
         t->SetConnectionState(kCsSideload);
+    } else if (type == "rescue") {
+        D("setting connection_state to kCsRescue");
+        t->SetConnectionState(kCsRescue);
     } else {
         D("setting connection_state to kCsHost");
         t->SetConnectionState(kCsHost);
@@ -317,13 +290,11 @@
 }
 
 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);
-    parse_banner(p->payload, t);
+    std::string banner(p->payload.begin(), p->payload.end());
+    parse_banner(banner, t);
 
 #if ADB_HOST
     handle_online(t);
@@ -349,19 +320,6 @@
     CHECK_EQ(p->payload.size(), p->msg.data_length);
 
     switch(p->msg.command){
-    case A_SYNC:
-        if (p->msg.arg0){
-            send_packet(p, t);
-#if ADB_HOST
-            send_connect(t);
-#endif
-        } else {
-            t->SetConnectionState(kCsOffline);
-            handle_offline(t);
-            send_packet(p, t);
-        }
-        return;
-
     case A_CNXN:  // CONNECT(version, maxdata, "system-id-string")
         handle_new_connection(t, p);
         break;
@@ -370,14 +328,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->payload.data(), p->msg.data_length, t);
                 break;
 #else
-            case ADB_AUTH_SIGNATURE:
-                if (adbd_auth_verify(t->token, sizeof(t->token), p->payload)) {
+            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 {
@@ -385,6 +345,7 @@
                     send_auth_request(t);
                 }
                 break;
+            }
 
             case ADB_AUTH_RSAPUBLICKEY:
                 adbd_auth_confirm_key(p->payload.data(), p->msg.data_length, t);
@@ -399,7 +360,14 @@
 
     case A_OPEN: /* OPEN(local-id, 0, "destination") */
         if (t->online && p->msg.arg0 != 0 && p->msg.arg1 == 0) {
-            asocket* s = create_local_service_socket(p->payload.c_str(), t);
+            std::string_view address(p->payload.begin(), p->payload.size());
+
+            // Historically, we received service names as a char*, and stopped at the first NUL
+            // byte. The client sent strings with null termination, which post-string_view, start
+            // being interpreted as part of the string, unless we explicitly strip them.
+            address = StripTrailingNulls(address);
+
+            asocket* s = create_local_service_socket(address, t);
             if (s == nullptr) {
                 send_close(0, p->msg.arg0, t);
             } else {
@@ -415,7 +383,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;
@@ -424,8 +392,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
@@ -451,8 +419,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);
                 }
@@ -640,11 +608,11 @@
     fprintf(stderr, "Full server startup log: %s\n", GetLogFilePath().c_str());
     fprintf(stderr, "Server had pid: %d\n", pid);
 
-    unique_fd fd(adb_open(GetLogFilePath().c_str(), O_RDONLY));
+    android::base::unique_fd fd(unix_open(GetLogFilePath(), O_RDONLY));
     if (fd == -1) return;
 
     // Let's not show more than 128KiB of log...
-    adb_lseek(fd, -128 * 1024, SEEK_END);
+    unix_lseek(fd, -128 * 1024, SEEK_END);
     std::string content;
     if (!android::base::ReadFdToString(fd, &content)) return;
 
@@ -834,7 +802,7 @@
                 memcmp(temp, expected, expected_length) == 0) {
                 got_ack = true;
             } else {
-                ReportServerStartupFailure(GetProcessId(process_handle.get()));
+                ReportServerStartupFailure(pinfo.dwProcessId);
                 return -1;
             }
         } else {
@@ -885,9 +853,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;
     }
@@ -899,11 +866,14 @@
 
     if (pid == 0) {
         // child side of the fork
+        pipe_read.reset();
 
-        adb_close(fd[0]);
+        // android::base::Pipe unconditionally opens the pipe with O_CLOEXEC.
+        // Undo this manually.
+        fcntl(pipe_write.get(), F_SETFD, 0);
 
         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);
@@ -913,10 +883,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;
@@ -931,17 +901,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, atransport* transport, 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")) {
@@ -951,12 +927,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:")) {
@@ -976,17 +959,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;
         InstallStatus r;
         int resolved_tcp_port = 0;
         if (kill_forward) {
@@ -1007,7 +989,7 @@
                 SendProtocolString(reply_fd, android::base::StringPrintf("%d", resolved_tcp_port));
             }
 
-            return 1;
+            return true;
         }
 
         std::string message;
@@ -1026,9 +1008,10 @@
             break;
         }
         SendFail(reply_fd, message);
-        return 1;
+        return true;
     }
-    return 0;
+
+    return false;
 }
 
 #if ADB_HOST
@@ -1038,9 +1021,10 @@
     return 0;
 }
 
-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) {
+HostRequestResult handle_host_request(std::string_view service, TransportType type,
+                                      const char* serial, TransportId transport_id, int reply_fd,
+                                      asocket* s) {
+    if (service == "kill") {
         fprintf(stderr, "adb server killed by remote request\n");
         fflush(stdout);
 
@@ -1052,29 +1036,49 @@
         exit(0);
     }
 
-    // "transport:" is used for switching transport with a specified serial number
-    // "transport-usb:" is used for switching transport to the only USB transport
-    // "transport-local:" is used for switching transport to the only local transport
-    // "transport-any:" is used for switching transport to the only transport
-    if (!strncmp(service, "transport", strlen("transport"))) {
+    LOG(DEBUG) << "handle_host_request(" << service << ")";
+
+    // Transport selection:
+    if (service.starts_with("transport") || service.starts_with("tport:")) {
         TransportType type = kTransportAny;
 
-        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;
+        std::string serial_storage;
+        bool legacy = true;
+
+        // New transport selection protocol:
+        // This is essentially identical to the previous version, except it returns the selected
+        // transport id to the caller as well.
+        if (android::base::ConsumePrefix(&service, "tport:")) {
+            legacy = false;
+            if (android::base::ConsumePrefix(&service, "serial:")) {
+                serial_storage = service;
+                serial = serial_storage.c_str();
+            } else if (service == "usb") {
+                type = kTransportUsb;
+            } else if (service == "local") {
+                type = kTransportLocal;
+            } else if (service == "any") {
+                type = kTransportAny;
             }
-        } else if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
-            type = kTransportUsb;
-        } else if (!strncmp(service, "transport-local", strlen("transport-local"))) {
-            type = kTransportLocal;
-        } else if (!strncmp(service, "transport-any", strlen("transport-any"))) {
-            type = kTransportAny;
-        } else if (!strncmp(service, "transport:", strlen("transport:"))) {
-            service += strlen("transport:");
-            serial = service;
+
+            // Selection by id is unimplemented, since you obviously already know the transport id
+            // you're connecting to.
+        } else {
+            if (android::base::ConsumePrefix(&service, "transport-id:")) {
+                if (!ParseUint(&transport_id, service)) {
+                    SendFail(reply_fd, "invalid transport id");
+                    return HostRequestResult::Handled;
+                }
+            } else if (service == "transport-usb") {
+                type = kTransportUsb;
+            } else if (service == "transport-local") {
+                type = kTransportLocal;
+            } else if (service == "transport-any") {
+                type = kTransportAny;
+            } else if (android::base::ConsumePrefix(&service, "transport:")) {
+                serial_storage = service;
+                serial = serial_storage.c_str();
+            }
         }
 
         std::string error;
@@ -1082,44 +1086,46 @@
         if (t != nullptr) {
             s->transport = t;
             SendOkay(reply_fd);
+
+            if (!legacy) {
+                // Nothing we can do if this fails.
+                WriteFdExactly(reply_fd, &t->id, sizeof(t->id));
+            }
+
+            return HostRequestResult::SwitchedTransport;
         } else {
             SendFail(reply_fd, error);
+            return HostRequestResult::Handled;
         }
-        return 1;
     }
 
     // return a list of all connected devices
-    if (!strncmp(service, "devices", 7)) {
-        bool long_listing = (strcmp(service+7, "-l") == 0);
-        if (long_listing || service[7] == 0) {
-            D("Getting device list...");
-            std::string device_list = list_transports(long_listing);
-            D("Sending device list...");
-            return SendOkay(reply_fd, device_list);
-        }
-        return 1;
+    if (service == "devices" || service == "devices-l") {
+        bool long_listing = service == "devices-l";
+        D("Getting device list...");
+        std::string device_list = list_transports(long_listing);
+        D("Sending device list...");
+        SendOkay(reply_fd, device_list);
+        return HostRequestResult::Handled;
     }
 
-    if (!strcmp(service, "reconnect-offline")) {
+    if (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;
+        }, true);
         if (!response.empty()) {
             response.resize(response.size() - 1);
         }
         SendOkay(reply_fd, response);
-        return 0;
+        return HostRequestResult::Handled;
     }
 
-    if (!strcmp(service, "features")) {
+    if (service == "features") {
         std::string error;
         atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t != nullptr) {
@@ -1127,10 +1133,10 @@
         } else {
             SendFail(reply_fd, error);
         }
-        return 0;
+        return HostRequestResult::Handled;
     }
 
-    if (!strcmp(service, "host-features")) {
+    if (service == "host-features") {
         FeatureSet features = supported_features();
         // Abuse features to report libusb status.
         if (should_use_libusb()) {
@@ -1138,97 +1144,114 @@
         }
         features.insert(kFeaturePushSync);
         SendOkay(reply_fd, FeatureSetToString(features));
-        return 0;
+        return HostRequestResult::Handled;
     }
 
     // remove TCP transport
-    if (!strncmp(service, "disconnect:", 11)) {
-        const std::string address(service + 11);
+    if (service.starts_with("disconnect:")) {
+        std::string address(service.substr(11));
         if (address.empty()) {
             kick_all_tcp_devices();
-            return SendOkay(reply_fd, "disconnected everything");
+            SendOkay(reply_fd, "disconnected everything");
+            return HostRequestResult::Handled;
         }
 
         std::string serial;
         std::string host;
         int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
         std::string error;
-        if (!android::base::ParseNetAddress(address, &host, &port, &serial, &error)) {
-            return SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s",
-                                                                  address.c_str(), error.c_str()));
+        if (address.starts_with("vsock:")) {
+            serial = address;
+        } else if (!android::base::ParseNetAddress(address, &host, &port, &serial, &error)) {
+            SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s",
+                                                           address.c_str(), error.c_str()));
+            return HostRequestResult::Handled;
         }
         atransport* t = find_transport(serial.c_str());
         if (t == nullptr) {
-            return SendFail(reply_fd, android::base::StringPrintf("no such device '%s'",
-                                                                  serial.c_str()));
+            SendFail(reply_fd, android::base::StringPrintf("no such device '%s'", serial.c_str()));
+            return HostRequestResult::Handled;
         }
         kick_transport(t);
-        return SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
+        SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
+        return HostRequestResult::Handled;
     }
 
     // Returns our value for ADB_SERVER_VERSION.
-    if (!strcmp(service, "version")) {
-        return SendOkay(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
+    if (service == "version") {
+        SendOkay(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
+        return HostRequestResult::Handled;
     }
 
     // These always report "unknown" rather than the actual error, for scripts.
-    if (!strcmp(service, "get-serialno")) {
+    if (service == "get-serialno") {
         std::string error;
         atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
-            return SendOkay(reply_fd, t->serial ? t->serial : "unknown");
+            SendOkay(reply_fd, !t->serial.empty() ? t->serial : "unknown");
         } else {
-            return SendFail(reply_fd, error);
+            SendFail(reply_fd, error);
         }
+        return HostRequestResult::Handled;
     }
-    if (!strcmp(service, "get-devpath")) {
+    if (service == "get-devpath") {
         std::string error;
         atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
-            return SendOkay(reply_fd, t->devpath ? t->devpath : "unknown");
+            SendOkay(reply_fd, !t->devpath.empty() ? t->devpath : "unknown");
         } else {
-            return SendFail(reply_fd, error);
+            SendFail(reply_fd, error);
         }
+        return HostRequestResult::Handled;
     }
-    if (!strcmp(service, "get-state")) {
+    if (service == "get-state") {
         std::string error;
         atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
-            return SendOkay(reply_fd, t->connection_state_name());
+            SendOkay(reply_fd, t->connection_state_name());
         } else {
-            return SendFail(reply_fd, error);
+            SendFail(reply_fd, error);
         }
+        return HostRequestResult::Handled;
     }
 
     // Indicates a new emulator instance has started.
-    if (!strncmp(service, "emulator:", 9)) {
-        int  port = atoi(service+9);
-        local_connect(port);
+    if (android::base::ConsumePrefix(&service, "emulator:")) {
+        unsigned int port;
+        if (!ParseUint(&port, service)) {
+          LOG(ERROR) << "received invalid port for emulator: " << service;
+        } else {
+          local_connect(port);
+        }
+
         /* we don't even need to send a reply */
-        return 0;
+        return HostRequestResult::Handled;
     }
 
-    if (!strcmp(service, "reconnect")) {
+    if (service == "reconnect") {
         std::string response;
         atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &response, true);
         if (t != nullptr) {
-            kick_transport(t);
+            kick_transport(t, true);
             response =
-                "reconnecting " + t->serial_name() + " [" + t->connection_state_name() + "]\n";
+                    "reconnecting " + t->serial_name() + " [" + t->connection_state_name() + "]\n";
         }
-        return SendOkay(reply_fd, response);
+        SendOkay(reply_fd, response);
+        return HostRequestResult::Handled;
     }
 
-    std::string error;
-    atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
-    if (!t) {
-        return -1;
+    // TODO: Switch handle_forward_request to string_view.
+    std::string service_str(service);
+    if (handle_forward_request(
+                service_str.c_str(),
+                [=](std::string* error) {
+                    return acquire_one_transport(type, serial, transport_id, nullptr, error);
+                },
+                reply_fd)) {
+        return HostRequestResult::Handled;
     }
 
-    int ret = handle_forward_request(service, t, reply_fd);
-    if (ret >= 0)
-      return ret - 1;
-    return -1;
+    return HostRequestResult::Unhandled;
 }
 
 static auto& init_mutex = *new std::mutex();
diff --git a/adb/adb.h b/adb/adb.h
index 65b5fc0..3a6f059 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -28,6 +28,7 @@
 #include "adb_trace.h"
 #include "fdevent.h"
 #include "socket.h"
+#include "types.h"
 #include "usb.h"
 
 constexpr size_t MAX_PAYLOAD_V1 = 4 * 1024;
@@ -58,25 +59,11 @@
 std::string adb_version();
 
 // Increment this when we want to force users to start a new adb server.
-#define ADB_SERVER_VERSION 40
+#define ADB_SERVER_VERSION 41
 
 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 {
-    amessage msg;
-    std::string payload;
-};
-
 uint32_t calculate_apacket_checksum(const apacket* packet);
 
 /* the adisconnect structure is used to record a callback that
@@ -108,22 +95,36 @@
 
 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,
+    kCsRescue,
 };
 
-void print_packet(const char* label, apacket* p);
+inline bool ConnectionStateIsOnline(ConnectionState state) {
+    switch (state) {
+        case kCsBootloader:
+        case kCsDevice:
+        case kCsHost:
+        case kCsRecovery:
+        case kCsSideload:
+        case kCsRescue:
+            return true;
+        default:
+            return false;
+    }
+}
 
-// 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 print_packet(const char* label, apacket* p);
 
 void handle_packet(apacket* p, atransport* t);
 
@@ -131,7 +132,7 @@
 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 */
-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);
@@ -140,24 +141,35 @@
 atransport* find_emulator_transport_by_console_port(int console_port);
 #endif
 
-int service_to_fd(const char* name, atransport* transport);
+unique_fd service_to_fd(std::string_view name, atransport* transport);
+#if !ADB_HOST
+unique_fd daemon_service_to_fd(std::string_view name, atransport* transport);
+#endif
+
 #if ADB_HOST
-asocket* host_service_to_socket(const char* name, const char* serial, TransportId transport_id);
+asocket* host_service_to_socket(std::string_view name, std::string_view serial,
+                                TransportId transport_id);
+#endif
+
+#if !ADB_HOST
+asocket* daemon_service_to_socket(std::string_view name);
+#endif
+
+#if !ADB_HOST
+unique_fd execute_abb_command(std::string_view command);
 #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);
+unique_fd create_jdwp_connection_fd(int jdwp_pid);
 #endif
 
-int handle_forward_request(const char* service, atransport* transport, 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);
@@ -197,6 +209,9 @@
 
 #define CHUNK_SIZE (64 * 1024)
 
+// Argument delimeter for adb abb command.
+#define ABB_ARG_DELIMETER ('\0')
+
 #if !ADB_HOST
 #define USB_FFS_ADB_PATH "/dev/usb-ffs/adb/"
 #define USB_FFS_ADB_EP(x) USB_FFS_ADB_PATH #x
@@ -206,8 +221,15 @@
 #define USB_FFS_ADB_IN USB_FFS_ADB_EP(ep2)
 #endif
 
-int handle_host_request(const char* service, TransportType type, const char* serial,
-                        TransportId transport_id, int reply_fd, asocket* s);
+enum class HostRequestResult {
+    Handled,
+    SwitchedTransport,
+    Unhandled,
+};
+
+HostRequestResult handle_host_request(std::string_view service, TransportType type,
+                                      const char* serial, TransportId transport_id, int reply_fd,
+                                      asocket* s);
 
 void handle_online(atransport* t);
 void handle_offline(atransport* t);
diff --git a/adb/adb_auth.h b/adb/adb_auth.h
index 715e04f..2fc8478 100644
--- a/adb/adb_auth.h
+++ b/adb/adb_auth.h
@@ -36,6 +36,7 @@
 void adb_auth_init();
 
 int adb_auth_keygen(const char* filename);
+int adb_auth_pubkey(const char* filename);
 std::string adb_auth_get_userkey();
 std::deque<std::shared_ptr<RSA>> adb_auth_get_private_keys();
 
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
deleted file mode 100644
index c3aef16..0000000
--- a/adb/adb_auth_host.cpp
+++ /dev/null
@@ -1,488 +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 std::string adb_auth_sign(RSA* key, const char* token, size_t token_size) {
-    if (token_size != TOKEN_SIZE) {
-        D("Unexpected token size %zd", token_size);
-        return 0;
-    }
-
-    std::string result;
-    result.resize(MAX_PAYLOAD);
-
-    unsigned int len;
-    if (!RSA_sign(NID_sha1, reinterpret_cast<const uint8_t*>(token), token_size,
-                  reinterpret_cast<uint8_t*>(&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;
-
-    p->payload = std::move(key);
-
-    // adbd expects a null-terminated string.
-    p->payload.push_back('\0');
-    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.
-        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 = std::move(result);
-    p->msg.data_length = p->payload.size();
-    send_packet(p, t);
-}
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
deleted file mode 100644
index 849a6e7..0000000
--- a/adb/adb_client.cpp
+++ /dev/null
@@ -1,332 +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 <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 = NULL;
-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/adb_client.h b/adb/adb_client.h
deleted file mode 100644
index fca435e..0000000
--- a/adb/adb_client.h
+++ /dev/null
@@ -1,67 +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, 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/adb_integration_test_adb.xml b/adb/adb_integration_test_adb.xml
new file mode 100644
index 0000000..e722956
--- /dev/null
+++ b/adb/adb_integration_test_adb.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+<configuration description="Config to run adb integration tests">
+    <option name="test-suite-tag" value="adb_tests" />
+    <option name="test-suite-tag" value="adb_integration" />
+    <target_preparer class="com.android.tradefed.targetprep.SemaphoreTokenTargetPreparer">
+        <option name="disable" value="false" />
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.adb.AdbStopServerPreparer" />
+    <test class="com.android.tradefed.testtype.python.PythonBinaryHostTest" >
+        <option name="par-file-name" value="adb_integration_test_adb" />
+        <option name="inject-android-serial" value="true" />
+        <option name="test-timeout" value="2m" />
+    </test>
+</configuration>
diff --git a/adb/adb_integration_test_device.xml b/adb/adb_integration_test_device.xml
new file mode 100644
index 0000000..b892377
--- /dev/null
+++ b/adb/adb_integration_test_device.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+<configuration description="Config to run adb integration tests for device">
+    <option name="test-suite-tag" value="adb_tests" />
+    <option name="test-suite-tag" value="adb_integration_device" />
+    <target_preparer class="com.android.tradefed.targetprep.SemaphoreTokenTargetPreparer">
+        <option name="disable" value="false" />
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.adb.AdbStopServerPreparer" />
+    <test class="com.android.tradefed.testtype.python.PythonBinaryHostTest" >
+        <option name="par-file-name" value="adb_integration_test_device" />
+        <option name="inject-android-serial" value="true" />
+        <option name="test-timeout" value="2m" />
+    </test>
+</configuration>
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 38e3116..bdb8efa 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -20,6 +20,11 @@
 
 #include <unistd.h>
 
+#if !ADB_HOST
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+
 #include <thread>
 
 #include <android-base/stringprintf.h>
@@ -29,7 +34,7 @@
 #include "adb_utils.h"
 #include "sysdeps.h"
 
-bool SendProtocolString(int fd, const std::string& s) {
+bool SendProtocolString(borrowed_fd fd, std::string_view s) {
     unsigned int length = s.size();
     if (length > MAX_PAYLOAD - 4) {
         errno = EMSGSIZE;
@@ -38,10 +43,11 @@
 
     // The cost of sending two strings outweighs the cost of formatting.
     // "adb sync" performance is affected by this.
-    return WriteFdFmt(fd, "%04x%.*s", length, length, s.c_str());
+    auto str = android::base::StringPrintf("%04x", length).append(s);
+    return WriteFdExactly(fd, str);
 }
 
-bool ReadProtocolString(int fd, std::string* s, std::string* error) {
+bool ReadProtocolString(borrowed_fd fd, std::string* s, std::string* error) {
     char buf[5];
     if (!ReadFdExactly(fd, buf, 4)) {
         *error = perror_str("protocol fault (couldn't read status length)");
@@ -49,7 +55,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)");
@@ -59,57 +65,57 @@
     return true;
 }
 
-bool SendOkay(int fd) {
+bool SendOkay(borrowed_fd fd) {
     return WriteFdExactly(fd, "OKAY", 4);
 }
 
-bool SendFail(int fd, const std::string& reason) {
+bool SendFail(borrowed_fd fd, std::string_view reason) {
     return WriteFdExactly(fd, "FAIL", 4) && SendProtocolString(fd, reason);
 }
 
-bool ReadFdExactly(int fd, void* buf, size_t len) {
+bool ReadFdExactly(borrowed_fd fd, void* buf, size_t len) {
     char* p = reinterpret_cast<char*>(buf);
 
     size_t len0 = len;
 
-    D("readx: fd=%d wanted=%zu", fd, len);
+    D("readx: fd=%d wanted=%zu", fd.get(), len);
     while (len > 0) {
         int r = adb_read(fd, p, len);
         if (r > 0) {
             len -= r;
             p += r;
         } else if (r == -1) {
-            D("readx: fd=%d error %d: %s", fd, errno, strerror(errno));
+            D("readx: fd=%d error %d: %s", fd.get(), errno, strerror(errno));
             return false;
         } else {
-            D("readx: fd=%d disconnected", fd);
+            D("readx: fd=%d disconnected", fd.get());
             errno = 0;
             return false;
         }
     }
 
-    VLOG(RWX) << "readx: fd=" << fd << " wanted=" << len0 << " got=" << (len0 - len)
-              << " " << dump_hex(reinterpret_cast<const unsigned char*>(buf), len0);
+    VLOG(RWX) << "readx: fd=" << fd.get() << " wanted=" << len0 << " got=" << (len0 - len) << " "
+              << dump_hex(reinterpret_cast<const unsigned char*>(buf), len0);
 
     return true;
 }
 
-bool WriteFdExactly(int fd, const void* buf, size_t len) {
+bool WriteFdExactly(borrowed_fd fd, const void* buf, size_t len) {
     const char* p = reinterpret_cast<const char*>(buf);
     int r;
 
-    VLOG(RWX) << "writex: fd=" << fd << " len=" << len
-              << " " << dump_hex(reinterpret_cast<const unsigned char*>(buf), len);
+    VLOG(RWX) << "writex: fd=" << fd.get() << " len=" << len << " "
+              << dump_hex(reinterpret_cast<const unsigned char*>(buf), len);
 
     while (len > 0) {
         r = adb_write(fd, p, len);
         if (r == -1) {
-            D("writex: fd=%d error %d: %s", fd, errno, strerror(errno));
+            D("writex: fd=%d error %d: %s", fd.get(), errno, strerror(errno));
             if (errno == EAGAIN) {
                 std::this_thread::yield();
                 continue;
             } else if (errno == EPIPE) {
-                D("writex: fd=%d disconnected", fd);
+                D("writex: fd=%d disconnected", fd.get());
                 errno = 0;
                 return false;
             } else {
@@ -123,15 +129,15 @@
     return true;
 }
 
-bool WriteFdExactly(int fd, const char* str) {
+bool WriteFdExactly(borrowed_fd fd, const char* str) {
     return WriteFdExactly(fd, str, strlen(str));
 }
 
-bool WriteFdExactly(int fd, const std::string& str) {
+bool WriteFdExactly(borrowed_fd fd, const std::string& str) {
     return WriteFdExactly(fd, str.c_str(), str.size());
 }
 
-bool WriteFdFmt(int fd, const char* fmt, ...) {
+bool WriteFdFmt(borrowed_fd fd, const char* fmt, ...) {
     std::string str;
 
     va_list ap;
@@ -142,7 +148,7 @@
     return WriteFdExactly(fd, str);
 }
 
-bool ReadOrderlyShutdown(int fd) {
+bool ReadOrderlyShutdown(borrowed_fd fd) {
     char buf[16];
 
     // Only call this function if you're sure that the peer does
@@ -172,7 +178,7 @@
         // data. We don't repeatedly call adb_read() until we get zero because
         // we don't know how long that would take, but we do know that the
         // caller wants to close the socket soon.
-        VLOG(RWX) << "ReadOrderlyShutdown(" << fd << ") unexpectedly read "
+        VLOG(RWX) << "ReadOrderlyShutdown(" << fd.get() << ") unexpectedly read "
                   << dump_hex(buf, result);
         // Shutdown the socket to prevent the caller from reading or writing to
         // it which doesn't make sense if we just read and discarded some data.
diff --git a/adb/adb_io.h b/adb/adb_io.h
index aa550af..9628946 100644
--- a/adb/adb_io.h
+++ b/adb/adb_io.h
@@ -20,18 +20,21 @@
 #include <sys/types.h>
 
 #include <string>
+#include <string_view>
+
+#include "adb_unique_fd.h"
 
 // Sends the protocol "OKAY" message.
-bool SendOkay(int fd);
+bool SendOkay(borrowed_fd fd);
 
 // Sends the protocol "FAIL" message, with the given failure reason.
-bool SendFail(int fd, const std::string& reason);
+bool SendFail(borrowed_fd fd, std::string_view reason);
 
 // Writes a protocol-format string; a four hex digit length followed by the string data.
-bool SendProtocolString(int fd, const std::string& s);
+bool SendProtocolString(borrowed_fd fd, std::string_view s);
 
 // Reads a protocol-format string; a four hex digit length followed by the string data.
-bool ReadProtocolString(int fd, std::string* s, std::string* error);
+bool ReadProtocolString(borrowed_fd fd, std::string* s, std::string* error);
 
 // Reads exactly len bytes from fd into buf.
 //
@@ -39,7 +42,7 @@
 // were read. If EOF was found, errno will be set to 0.
 //
 // If this function fails, the contents of buf are undefined.
-bool ReadFdExactly(int fd, void* buf, size_t len);
+bool ReadFdExactly(borrowed_fd fd, void* buf, size_t len);
 
 // Given a client socket, wait for orderly/graceful shutdown. Call this:
 //
@@ -57,20 +60,19 @@
 // connect()s from the client to fail with WSAEADDRINUSE on Windows.
 // Returns true if it is sure that orderly/graceful shutdown has occurred with
 // no additional data read from the server.
-bool ReadOrderlyShutdown(int fd);
+bool ReadOrderlyShutdown(borrowed_fd fd);
 
 // Writes exactly len bytes from buf to fd.
 //
 // Returns false if there is an error or if the fd was closed before the write
 // completed. If the other end of the fd (such as in a socket, pipe, or fifo),
 // is closed, errno will be set to 0.
-bool WriteFdExactly(int fd, const void* buf, size_t len);
+bool WriteFdExactly(borrowed_fd fd, const void* buf, size_t len);
 
 // Same as above, but for strings.
-bool WriteFdExactly(int fd, const char* s);
-bool WriteFdExactly(int fd, const std::string& s);
+bool WriteFdExactly(borrowed_fd fd, const char* s);
+bool WriteFdExactly(borrowed_fd fd, const std::string& s);
 
 // Same as above, but formats the string to send.
-bool WriteFdFmt(int fd, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
-
+bool WriteFdFmt(borrowed_fd fd, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
 #endif /* ADB_IO_H */
diff --git a/adb/adb_io_test.cpp b/adb/adb_io_test.cpp
index 611b239..91b73a9 100644
--- a/adb/adb_io_test.cpp
+++ b/adb/adb_io_test.cpp
@@ -28,7 +28,6 @@
 #include <string>
 
 #include <android-base/file.h>
-#include <android-base/test_utils.h>
 
 // All of these tests fail on Windows because they use the C Runtime open(),
 // but the adb_io APIs expect file descriptors from adb_open(). This could
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index fecf452..29909a5 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -21,6 +21,7 @@
 
 #include <algorithm>
 #include <list>
+#include <memory>
 
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -29,7 +30,6 @@
 
 #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
@@ -42,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;
@@ -60,7 +60,7 @@
 
 alistener::~alistener() {
     // Closes the corresponding fd.
-    fdevent_remove(&fde);
+    fdevent_destroy(fde);
 
     if (transport) {
         transport->RemoveDisconnect(&disconnect);
@@ -75,41 +75,36 @@
 
 static void ss_listener_event_func(int _fd, unsigned ev, void *_l) {
     if (ev & FDE_READ) {
-        int fd = adb_socket_accept(_fd, nullptr, nullptr);
+        unique_fd fd(adb_socket_accept(_fd, nullptr, nullptr));
         if (fd < 0) return;
 
         int rcv_buf_size = CHUNK_SIZE;
-        adb_setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcv_buf_size, sizeof(rcv_buf_size));
+        adb_setsockopt(fd.get(), SOL_SOCKET, SO_RCVBUF, &rcv_buf_size, sizeof(rcv_buf_size));
 
-        asocket* s = create_local_socket(fd);
+        asocket* s = create_local_socket(std::move(fd));
         if (s) {
             connect_to_smartsocket(s);
             return;
         }
-
-        adb_close(fd);
     }
 }
 
 static void listener_event_func(int _fd, unsigned ev, void* _l)
 {
     alistener* listener = reinterpret_cast<alistener*>(_l);
-    asocket *s;
 
     if (ev & FDE_READ) {
-        int fd = adb_socket_accept(_fd, nullptr, nullptr);
+        unique_fd fd(adb_socket_accept(_fd, nullptr, nullptr));
         if (fd < 0) {
             return;
         }
 
-        s = create_local_socket(fd);
+        asocket* s = create_local_socket(std::move(fd));
         if (s) {
             s->transport = listener->transport;
-            connect_to_remote(s, listener->connect_to.c_str());
+            connect_to_remote(s, listener->connect_to);
             return;
         }
-
-        adb_close(fd);
     }
 }
 
@@ -136,9 +131,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;
 }
@@ -222,11 +218,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 a8ec5fb..80f146c 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -32,7 +32,9 @@
 
 #if !ADB_HOST
 const char* adb_device_banner = "device";
+#if defined(__ANDROID__)
 static android::base::LogdLogger gLogdLogger;
+#endif
 #else
 const char* adb_device_banner = "host";
 #endif
@@ -41,7 +43,12 @@
                const char* tag, const char* file, unsigned int line,
                const char* message) {
     android::base::StderrLogger(id, severity, tag, file, line, message);
-#if !ADB_HOST
+#if defined(_WIN32)
+    // stderr can be buffered on Windows (and setvbuf doesn't seem to work), so explicitly flush.
+    fflush(stderr);
+#endif
+
+#if !ADB_HOST && defined(__ANDROID__)
     // 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) {
@@ -67,8 +74,7 @@
 }
 
 void start_device_log(void) {
-    int fd = unix_open(get_log_file_name().c_str(),
-                       O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
+    int fd = unix_open(get_log_file_name(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
     if (fd == -1) {
         return;
     }
diff --git a/adb/adb_unique_fd.cpp b/adb/adb_unique_fd.cpp
new file mode 100644
index 0000000..dec73bc
--- /dev/null
+++ b/adb/adb_unique_fd.cpp
@@ -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 "adb_unique_fd.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include "sysdeps.h"
+
+#if defined(_WIN32)
+void AdbCloser::Close(int fd) {
+    adb_close(fd);
+}
+#endif
diff --git a/adb/adb_unique_fd.h b/adb/adb_unique_fd.h
index 34c1bbc..b6c910a 100644
--- a/adb/adb_unique_fd.h
+++ b/adb/adb_unique_fd.h
@@ -16,11 +16,24 @@
 
 #pragma once
 
+#include <errno.h>
+#include <unistd.h>
+
 #include <android-base/unique_fd.h>
 
+#if defined(_WIN32)
 // Helper to automatically close an FD when it goes out of scope.
 struct AdbCloser {
     static void Close(int fd);
 };
 
 using unique_fd = android::base::unique_fd_impl<AdbCloser>;
+#else
+using unique_fd = android::base::unique_fd;
+#endif
+
+using android::base::borrowed_fd;
+
+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..cf5fbc8 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -49,19 +49,19 @@
 
 
 #if defined(_WIN32)
-constexpr char kNullFileName[] = "NUL";
+static constexpr char kNullFileName[] = "NUL";
 #else
-constexpr char kNullFileName[] = "/dev/null";
+static constexpr char kNullFileName[] = "/dev/null";
 #endif
 
 void close_stdin() {
     int fd = unix_open(kNullFileName, O_RDONLY);
     if (fd == -1) {
-        fatal_errno("failed to open %s", kNullFileName);
+        PLOG(FATAL) << "failed to open " << kNullFileName;
     }
 
     if (TEMP_FAILURE_RETRY(dup2(fd, STDIN_FILENO)) == -1) {
-        fatal_errno("failed to redirect stdin to %s", kNullFileName);
+        PLOG(FATAL) << "failed to redirect stdin to " << kNullFileName;
     }
     unix_close(fd);
 }
@@ -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;
 }
@@ -184,21 +186,63 @@
     return line;
 }
 
+std::string dump_header(const amessage* msg) {
+    unsigned command = msg->command;
+    int len = msg->data_length;
+    char cmd[9];
+    char arg0[12], arg1[12];
+    int n;
+
+    for (n = 0; n < 4; n++) {
+        int b = (command >> (n * 8)) & 255;
+        if (b < 32 || b >= 127) break;
+        cmd[n] = (char)b;
+    }
+    if (n == 4) {
+        cmd[4] = 0;
+    } else {
+        // There is some non-ASCII name in the command, so dump the hexadecimal value instead
+        snprintf(cmd, sizeof cmd, "%08x", command);
+    }
+
+    if (msg->arg0 < 256U)
+        snprintf(arg0, sizeof arg0, "%d", msg->arg0);
+    else
+        snprintf(arg0, sizeof arg0, "0x%x", msg->arg0);
+
+    if (msg->arg1 < 256U)
+        snprintf(arg1, sizeof arg1, "%d", msg->arg1);
+    else
+        snprintf(arg1, sizeof arg1, "0x%x", msg->arg1);
+
+    return android::base::StringPrintf("[%s] arg0=%s arg1=%s (len=%d) ", cmd, arg0, arg1, len);
+}
+
+std::string dump_packet(const char* name, const char* func, const apacket* p) {
+    std::string result = name;
+    result += ": ";
+    result += func;
+    result += ": ";
+    result += dump_header(&p->msg);
+    result += dump_hex(p->payload.data(), p->payload.size());
+    return result;
+}
+
 std::string perror_str(const char* msg) {
     return android::base::StringPrintf("%s: %s", msg, strerror(errno));
 }
 
 #if !defined(_WIN32)
 // Windows version provided in sysdeps_win32.cpp
-bool set_file_block_mode(int fd, bool block) {
-    int flags = fcntl(fd, F_GETFL, 0);
+bool set_file_block_mode(borrowed_fd fd, bool block) {
+    int flags = fcntl(fd.get(), F_GETFL, 0);
     if (flags == -1) {
-        PLOG(ERROR) << "failed to fcntl(F_GETFL) for fd " << fd;
+        PLOG(ERROR) << "failed to fcntl(F_GETFL) for fd " << fd.get();
         return false;
     }
     flags = block ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
-    if (fcntl(fd, F_SETFL, flags) != 0) {
-        PLOG(ERROR) << "failed to fcntl(F_SETFL) for fd " << fd << ", flags " << flags;
+    if (fcntl(fd.get(), F_SETFL, flags) != 0) {
+        PLOG(ERROR) << "failed to fcntl(F_SETFL) for fd " << fd.get() << ", flags " << flags;
         return false;
     }
     return true;
@@ -249,6 +293,9 @@
     struct passwd pwent;
     struct passwd* result;
     int pwent_max = sysconf(_SC_GETPW_R_SIZE_MAX);
+    if (pwent_max == -1) {
+        pwent_max = 16384;
+    }
     std::vector<char> buf(pwent_max);
     int rc = getpwuid_r(getuid(), &pwent, buf.data(), buf.size(), &result);
     if (rc == 0 && result) {
@@ -265,29 +312,13 @@
     std::string android_dir = user_dir + OS_PATH_SEPARATOR + ".android";
     struct stat buf;
     if (stat(android_dir.c_str(), &buf) == -1) {
-        if (adb_mkdir(android_dir.c_str(), 0750) == -1) {
+        if (adb_mkdir(android_dir, 0750) == -1) {
             PLOG(FATAL) << "Cannot mkdir '" << android_dir << "'";
         }
     }
     return android_dir;
 }
 
-void AdbCloser::Close(int fd) {
-    adb_close(fd);
-}
-
-int syntax_error(const char* fmt, ...) {
-    fprintf(stderr, "adb: usage: ");
-
-    va_list ap;
-    va_start(ap, fmt);
-    vfprintf(stderr, fmt, ap);
-    va_end(ap);
-
-    fprintf(stderr, "\n");
-    return 1;
-}
-
 std::string GetLogFilePath() {
 #if defined(_WIN32)
     const char log_name[] = "adb.log";
@@ -297,13 +328,13 @@
     DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
     if (nchars >= arraysize(temp_path) || nchars == 0) {
         // If string truncation or some other error.
-        fatal("cannot retrieve temporary file path: %s\n",
-              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        LOG(FATAL) << "cannot retrieve temporary file path: "
+                   << android::base::SystemErrorCodeToString(GetLastError());
     }
 
     std::string temp_path_utf8;
     if (!android::base::WideToUTF8(temp_path, &temp_path_utf8)) {
-        fatal_errno("cannot convert temporary file path from UTF-16 to UTF-8");
+        PLOG(FATAL) << "cannot convert temporary file path from UTF-16 to UTF-8";
     }
 
     return temp_path_utf8 + log_name;
@@ -313,3 +344,33 @@
     return android::base::StringPrintf("%s/adb.%u.log", tmp_dir, getuid());
 #endif
 }
+
+[[noreturn]] static void error_exit_va(int error, const char* fmt, va_list va) {
+    fflush(stdout);
+    fprintf(stderr, "%s: ", android::base::Basename(android::base::GetExecutablePath()).c_str());
+
+    vfprintf(stderr, fmt, va);
+
+    if (error != 0) {
+        fprintf(stderr, ": %s", strerror(error));
+    }
+
+    putc('\n', stderr);
+    fflush(stderr);
+
+    exit(EXIT_FAILURE);
+}
+
+void error_exit(const char* fmt, ...) {
+    va_list va;
+    va_start(va, fmt);
+    error_exit_va(0, fmt, va);
+    va_end(va);
+}
+
+void perror_exit(const char* fmt, ...) {
+    va_list va;
+    va_start(va, fmt);
+    error_exit_va(errno, fmt, va);
+    va_end(va);
+}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index f764a0e..faad03d 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -14,17 +14,19 @@
  * limitations under the License.
  */
 
-#ifndef _ADB_UTILS_H_
-#define _ADB_UTILS_H_
+#pragma once
 
 #include <condition_variable>
 #include <mutex>
 #include <string>
+#include <string_view>
+#include <type_traits>
 #include <vector>
 
 #include <android-base/macros.h>
 
-int syntax_error(const char*, ...);
+#include "adb.h"
+#include "adb_unique_fd.h"
 
 void close_stdin();
 
@@ -42,12 +44,15 @@
 std::string escape_arg(const std::string& s);
 
 std::string dump_hex(const void* ptr, size_t byte_count);
+std::string dump_header(const amessage* msg);
+std::string dump_packet(const char* name, const char* func, const apacket* p);
 
 std::string perror_str(const char* msg);
 
-bool set_file_block_mode(int fd, bool block);
+[[noreturn]] void error_exit(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
+[[noreturn]] void perror_exit(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
 
-extern int adb_close(int fd);
+bool set_file_block_mode(borrowed_fd fd, bool block);
 
 // Given forward/reverse targets, returns true if they look sane. If an error is found, fills
 // |error| and returns false.
@@ -91,4 +96,49 @@
 
 std::string GetLogFilePath();
 
-#endif
+inline std::string_view StripTrailingNulls(std::string_view str) {
+    size_t n = 0;
+    for (auto it = str.rbegin(); it != str.rend(); ++it) {
+        if (*it != '\0') {
+            break;
+        }
+        ++n;
+    }
+
+    str.remove_suffix(n);
+    return str;
+}
+
+// Base-10 stroll on a string_view.
+template <typename T>
+inline bool ParseUint(T* result, std::string_view str, std::string_view* remaining = nullptr) {
+    if (str.empty() || !isdigit(str[0])) {
+        return false;
+    }
+
+    T value = 0;
+    std::string_view::iterator it;
+    constexpr T max = std::numeric_limits<T>::max();
+    for (it = str.begin(); it != str.end() && isdigit(*it); ++it) {
+        if (value > max / 10) {
+            return false;
+        }
+
+        value *= 10;
+
+        T digit = *it - '0';
+        if (value > max - digit) {
+            return false;
+        }
+
+        value += digit;
+    }
+    *result = value;
+    if (remaining) {
+        *remaining = str.substr(it - str.begin());
+    } else {
+      return it == str.end();
+    }
+
+    return true;
+}
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index e1b6287..cdca3aa 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -30,8 +30,8 @@
 
 #include "sysdeps.h"
 
+#include <android-base/file.h>
 #include <android-base/macros.h>
-#include <android-base/test_utils.h>
 
 #ifdef _WIN32
 static std::string subdir(const char* parent, const char* child) {
@@ -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) {
@@ -139,17 +147,16 @@
 
 #if !defined(_WIN32)
 TEST(adb_utils, set_file_block_mode) {
-  int fd = adb_open("/dev/null", O_RDWR | O_APPEND);
-  ASSERT_GE(fd, 0);
-  int flags = fcntl(fd, F_GETFL, 0);
-  ASSERT_EQ(O_RDWR | O_APPEND, (flags & (O_RDWR | O_APPEND)));
-  ASSERT_TRUE(set_file_block_mode(fd, false));
-  int new_flags = fcntl(fd, F_GETFL, 0);
-  ASSERT_EQ(flags | O_NONBLOCK, new_flags);
-  ASSERT_TRUE(set_file_block_mode(fd, true));
-  new_flags = fcntl(fd, F_GETFL, 0);
-  ASSERT_EQ(flags, new_flags);
-  ASSERT_EQ(0, adb_close(fd));
+    unique_fd fd(adb_open("/dev/null", O_RDWR | O_APPEND));
+    ASSERT_GE(fd, 0);
+    int flags = fcntl(fd.get(), F_GETFL, 0);
+    ASSERT_EQ(O_RDWR | O_APPEND, (flags & (O_RDWR | O_APPEND)));
+    ASSERT_TRUE(set_file_block_mode(fd, false));
+    int new_flags = fcntl(fd.get(), F_GETFL, 0);
+    ASSERT_EQ(flags | O_NONBLOCK, new_flags);
+    ASSERT_TRUE(set_file_block_mode(fd, true));
+    new_flags = fcntl(fd.get(), F_GETFL, 0);
+    ASSERT_EQ(flags, new_flags);
 }
 #endif
 
@@ -173,3 +180,56 @@
     EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:a", &error));
     EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:22x", &error));
 }
+
+void TestParseUint(std::string_view string, bool expected_success, uint32_t expected_value = 0) {
+    // Standalone.
+    {
+        uint32_t value;
+        std::string_view remaining;
+        bool success = ParseUint(&value, string, &remaining);
+        EXPECT_EQ(success, expected_success);
+        if (expected_success) {
+            EXPECT_EQ(value, expected_value);
+        }
+        EXPECT_TRUE(remaining.empty());
+    }
+
+    // With trailing text.
+    {
+        std::string text = std::string(string) + "foo";
+        uint32_t value;
+        std::string_view remaining;
+        bool success = ParseUint(&value, text, &remaining);
+        EXPECT_EQ(success, expected_success);
+        if (expected_success) {
+            EXPECT_EQ(value, expected_value);
+            EXPECT_EQ(remaining, "foo");
+        }
+    }
+
+    // With trailing text, without remaining.
+    {
+        std::string text = std::string(string) + "foo";
+        uint32_t value;
+        bool success = ParseUint(&value, text, nullptr);
+        EXPECT_EQ(success, false);
+    }
+}
+
+TEST(adb_utils, ParseUint) {
+    TestParseUint("", false);
+    TestParseUint("foo", false);
+    TestParseUint("foo123", false);
+    TestParseUint("-1", false);
+
+    TestParseUint("123", true, 123);
+    TestParseUint("9999999999999999999999999", false);
+    TestParseUint(std::to_string(UINT32_MAX), true, UINT32_MAX);
+    TestParseUint("0" + std::to_string(UINT32_MAX), true, UINT32_MAX);
+    TestParseUint(std::to_string(static_cast<uint64_t>(UINT32_MAX) + 1), false);
+    TestParseUint("0" + std::to_string(static_cast<uint64_t>(UINT32_MAX) + 1), false);
+
+    std::string x = std::to_string(UINT32_MAX) + "123";
+    std::string_view substr = std::string_view(x).substr(0, std::to_string(UINT32_MAX).size());
+    TestParseUint(substr, true, UINT32_MAX);
+}
diff --git a/adb/adbd_auth.cpp b/adb/adbd_auth.cpp
deleted file mode 100644
index 3fd2b31..0000000
--- a/adb/adbd_auth.cpp
+++ /dev/null
@@ -1,225 +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 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 = 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();
-    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/benchmark_device.py b/adb/benchmark_device.py
new file mode 100755
index 0000000..4d0cf49
--- /dev/null
+++ b/adb/benchmark_device.py
@@ -0,0 +1,156 @@
+#!/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 subprocess
+import tempfile
+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_sink(device=None, size_mb=100):
+    if device == None:
+        device = adb.get_device()
+
+    speeds = list()
+    cmd = device.adb_cmd + ["raw", "sink:%d" % (size_mb * 1024 * 1024)]
+
+    with tempfile.TemporaryFile() as tmpfile:
+        tmpfile.truncate(size_mb * 1024 * 1024)
+
+        for _ in range(0, 10):
+            tmpfile.seek(0)
+            begin = time.time()
+            subprocess.check_call(cmd, stdin=tmpfile)
+            end = time.time()
+            speeds.append(size_mb / float(end - begin))
+
+    analyze("sink %dMiB" % size_mb, speeds)
+
+def benchmark_source(device=None, size_mb=100):
+    if device == None:
+        device = adb.get_device()
+
+    speeds = list()
+    cmd = device.adb_cmd + ["raw", "source:%d" % (size_mb * 1024 * 1024)]
+
+    with open(os.devnull, 'w') as devnull:
+        for _ in range(0, 10):
+            begin = time.time()
+            subprocess.check_call(cmd, stdout=devnull)
+            end = time.time()
+            speeds.append(size_mb / float(end - begin))
+
+    analyze("source %dMiB" % size_mb, speeds)
+
+def benchmark_push(device=None, file_size_mb=100):
+    if device == None:
+        device = adb.get_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, 10):
+        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()
+
+    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, 10):
+        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()
+
+    speeds = list()
+    for _ in range(0, 10):
+        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():
+    device = adb.get_device()
+    unlock(device)
+    benchmark_sink(device)
+    benchmark_source(device)
+    benchmark_push(device)
+    benchmark_pull(device)
+
+if __name__ == "__main__":
+    main()
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
deleted file mode 100644
index abef86a..0000000
--- a/adb/bugreport.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 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(), 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/bugreport_test.cpp b/adb/bugreport_test.cpp
index 72ca59a..a6be203 100644
--- a/adb/bugreport_test.cpp
+++ b/adb/bugreport_test.cpp
@@ -136,7 +136,7 @@
 
     void ExpectBugreportzVersion(const std::string& version) {
         EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _))
-            .WillOnce(DoAll(WithArg<2>(WriteOnStderr(version.c_str())),
+            .WillOnce(DoAll(WithArg<2>(WriteOnStderr(version)),
                             WithArg<2>(ReturnCallbackDone(0))));
     }
 
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
new file mode 100644
index 0000000..7e408a8
--- /dev/null
+++ b/adb/client/adb_client.cpp
@@ -0,0 +1,408 @@
+/*
+ * 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 <optional>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/file.h>
+#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 std::optional<TransportId> switch_socket_transport(int fd, std::string* error) {
+    TransportId result;
+    bool read_transport = true;
+
+    std::string service;
+    if (__adb_transport_id) {
+        read_transport = false;
+        service += "host:transport-id:";
+        service += std::to_string(__adb_transport_id);
+        result = __adb_transport_id;
+    } else if (__adb_serial) {
+        service += "host:tport:serial:";
+        service += __adb_serial;
+    } else {
+        const char* transport_type = "???";
+        switch (__adb_transport) {
+          case kTransportUsb:
+              transport_type = "usb";
+              break;
+          case kTransportLocal:
+              transport_type = "local";
+              break;
+          case kTransportAny:
+              transport_type = "any";
+              break;
+          case kTransportHost:
+            // no switch necessary
+            return 0;
+        }
+        service += "host:tport:";
+        service += transport_type;
+    }
+
+    if (!SendProtocolString(fd, service)) {
+        *error = perror_str("write failure during connection");
+        return std::nullopt;
+    }
+
+    LOG(DEBUG) << "Switch transport in progress: " << service;
+
+    if (!adb_status(fd, error)) {
+        D("Switch transport failed: %s", error->c_str());
+        return std::nullopt;
+    }
+
+    if (read_transport) {
+        if (!ReadFdExactly(fd, &result, sizeof(result))) {
+            *error = "failed to read transport id from server";
+            return std::nullopt;
+        }
+    }
+
+    D("Switch transport success");
+    return result;
+}
+
+bool adb_status(borrowed_fd 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(std::string_view service, TransportId* transport, std::string* error) {
+    LOG(DEBUG) << "_adb_connect: " << service;
+    if (service.empty() || service.size() > MAX_PAYLOAD) {
+        *error = android::base::StringPrintf("bad service name length (%zd)", service.size());
+        return -1;
+    }
+
+    std::string reason;
+    unique_fd fd;
+    if (!socket_spec_connect(&fd, __adb_server_socket_spec, nullptr, nullptr, &reason)) {
+        *error = android::base::StringPrintf("cannot connect to daemon at %s: %s",
+                                             __adb_server_socket_spec, reason.c_str());
+        return -2;
+    }
+
+    if (!service.starts_with("host")) {
+        std::optional<TransportId> transport_result = switch_socket_transport(fd.get(), error);
+        if (!transport_result) {
+            return -1;
+        }
+
+        if (transport) {
+            *transport = *transport_result;
+        }
+    }
+
+    if (!SendProtocolString(fd.get(), service)) {
+        *error = perror_str("write failure during connection");
+        return -1;
+    }
+
+    if (!adb_status(fd.get(), error)) {
+        return -1;
+    }
+
+    D("_adb_connect: return fd %d", fd.get());
+    return fd.release();
+}
+
+bool adb_kill_server() {
+    D("adb_kill_server");
+    std::string reason;
+    unique_fd fd;
+    if (!socket_spec_connect(&fd, __adb_server_socket_spec, nullptr, nullptr, &reason)) {
+        fprintf(stderr, "cannot connect to daemon at %s: %s\n", __adb_server_socket_spec,
+                reason.c_str());
+        return true;
+    }
+
+    if (!SendProtocolString(fd.get(), "host:kill")) {
+        fprintf(stderr, "error: write failure during connection: %s\n", strerror(errno));
+        return false;
+    }
+
+    // The server might send OKAY, so consume that.
+    char buf[4];
+    ReadFdExactly(fd.get(), buf, 4);
+    // Now that no more data is expected, wait for socket orderly shutdown or error, indicating
+    // server death.
+    ReadOrderlyShutdown(fd.get());
+    return true;
+}
+
+int adb_connect(std::string_view service, std::string* error) {
+    return adb_connect(nullptr, service, error);
+}
+
+#if defined(__linux__)
+std::optional<std::string> adb_get_server_executable_path() {
+    int port;
+    std::string error;
+    if (!parse_tcp_socket_spec(__adb_server_socket_spec, nullptr, &port, nullptr, &error)) {
+        LOG(FATAL) << "failed to parse server socket spec: " << error;
+    }
+
+    return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adb." + std::to_string(port);
+}
+#endif
+
+static bool __adb_check_server_version(std::string* error) {
+    unique_fd fd(_adb_connect("host:version", nullptr, error));
+
+    bool local = is_local_socket_spec(__adb_server_socket_spec);
+    if (fd == -2 && !local) {
+        fprintf(stderr, "* cannot start server on remote host\n");
+        // error is the original network connection error
+        return false;
+    } 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 false;
+        } 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 = 0;
+
+        // If we have a file descriptor, then parse version result.
+        if (fd >= 0) {
+            std::string version_string;
+            if (!ReadProtocolString(fd, &version_string, error)) {
+                return false;
+            }
+
+            ReadOrderlyShutdown(fd);
+
+            if (sscanf(&version_string[0], "%04x", &version) != 1) {
+                *error = android::base::StringPrintf("cannot parse version string: %s",
+                                                     version_string.c_str());
+                return false;
+            }
+        } 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 false;
+            }
+        }
+
+        if (version != ADB_SERVER_VERSION) {
+#if defined(__linux__)
+            if (version > ADB_SERVER_VERSION && local) {
+                // Try to re-exec the existing adb server's binary.
+                constexpr const char* adb_reexeced = "adb (re-execed)";
+                if (strcmp(adb_reexeced, *__adb_argv) != 0) {
+                    __adb_argv[0] = adb_reexeced;
+                    std::optional<std::string> server_path_path = adb_get_server_executable_path();
+                    std::string server_path;
+                    if (server_path_path &&
+                        android::base::ReadFileToString(*server_path_path, &server_path)) {
+                        if (execve(server_path.c_str(), const_cast<char**>(__adb_argv),
+                                   const_cast<char**>(__adb_envp)) == -1) {
+                            LOG(ERROR) << "failed to exec newer version at " << server_path;
+                        }
+
+                        // Fall-through to restarting the server.
+                    }
+                }
+            }
+#endif
+
+            fprintf(stderr, "adb server version (%d) doesn't match this client (%d); killing...\n",
+                    version, ADB_SERVER_VERSION);
+            adb_kill_server();
+            goto start_server;
+        }
+    }
+
+    return true;
+}
+
+bool adb_check_server_version(std::string* error) {
+    // Only check the version once per process, since this isn't atomic anyway.
+    static std::once_flag once;
+    static bool result;
+    static std::string* err;
+    std::call_once(once, []() {
+        err = new std::string();
+        result = __adb_check_server_version(err);
+    });
+    *error = *err;
+    return result;
+}
+
+int adb_connect(TransportId* transport, std::string_view service, std::string* error) {
+    LOG(DEBUG) << "adb_connect: service: " << service;
+
+    // Query the adb server's version.
+    if (!adb_check_server_version(error)) {
+        return -1;
+    }
+
+    // if the command is start-server, we are done.
+    if (service == "host:start-server") {
+        return 0;
+    }
+
+    unique_fd fd(_adb_connect(service, transport, 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.get());
+
+    return fd.release();
+}
+
+bool adb_command(const std::string& service) {
+    std::string error;
+    unique_fd fd(adb_connect(service, &error));
+    if (fd < 0) {
+        fprintf(stderr, "error: %s\n", error.c_str());
+        return false;
+    }
+
+    if (!adb_status(fd.get(), &error)) {
+        fprintf(stderr, "error: %s\n", error.c_str());
+        return false;
+    }
+
+    ReadOrderlyShutdown(fd.get());
+    return true;
+}
+
+bool adb_query(const std::string& service, std::string* result, std::string* error) {
+    D("adb_query: %s", service.c_str());
+    unique_fd fd(adb_connect(service, error));
+    if (fd < 0) {
+        return false;
+    }
+
+    result->clear();
+    if (!ReadProtocolString(fd.get(), result, error)) {
+        return false;
+    }
+
+    ReadOrderlyShutdown(fd.get());
+    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..fe1e584
--- /dev/null
+++ b/adb/client/adb_client.h
@@ -0,0 +1,84 @@
+/*
+ * 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 <optional>
+#include <string>
+
+#include "adb.h"
+#include "adb_unique_fd.h"
+#include "sysdeps.h"
+#include "transport.h"
+
+// Explicitly check the adb server version.
+// All of the commands below do this implicitly.
+// Only the first invocation of this function will check the server version.
+bool adb_check_server_version(std::string* _Nonnull error);
+
+// 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(std::string_view service, std::string* _Nonnull error);
+
+// Same as above, except returning the TransportId for the service that we've connected to.
+int adb_connect(TransportId* _Nullable id, std::string_view 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(borrowed_fd 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);
+
+#if defined(__linux__)
+// Get the path of a file containing the path to the server executable, if the socket spec set via
+// adb_set_socket_spec is a local one.
+std::optional<std::string> adb_get_server_executable_path();
+#endif
+
+// Globally acccesible argv/envp, for the purpose of re-execing adb.
+extern const char* _Nullable * _Nullable __adb_argv;
+extern const char* _Nullable * _Nullable __adb_envp;
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
new file mode 100644
index 0000000..16fa215
--- /dev/null
+++ b/adb/client/adb_install.cpp
@@ -0,0 +1,744 @@
+/*
+ * 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_install.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <algorithm>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "adb.h"
+#include "adb_client.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "client/file_sync_client.h"
+#include "commandline.h"
+#include "fastdeploy.h"
+
+#if defined(ENABLE_FASTDEPLOY)
+static constexpr int kFastDeployMinApi = 24;
+#endif
+
+static bool can_use_feature(const char* feature) {
+    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, feature);
+}
+
+static bool use_legacy_install() {
+    return !can_use_feature(kFeatureCmd);
+}
+
+static bool is_apex_supported() {
+    return can_use_feature(kFeatureApex);
+}
+
+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_streamed(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 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) */
+    for (int 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);
+}
+
+int uninstall_app(int argc, const char** argv) {
+    if (use_legacy_install()) {
+        return uninstall_app_legacy(argc, argv);
+    }
+    return uninstall_app_streamed(argc, argv);
+}
+
+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';
+}
+
+#if defined(ENABLE_FASTDEPLOY)
+static int delete_device_patch_file(const char* apkPath) {
+    std::string patchDevicePath = get_patch_path(apkPath);
+    return delete_device_file(patchDevicePath);
+}
+#endif
+
+static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy,
+                                bool use_localagent) {
+    printf("Performing Streamed Install\n");
+
+    // The last argument must be the APK file
+    const char* file = argv[argc - 1];
+    if (!android::base::EndsWithIgnoreCase(file, ".apk") &&
+        !android::base::EndsWithIgnoreCase(file, ".apex")) {
+        error_exit("filename doesn't end .apk or .apex: %s", file);
+    }
+
+    bool is_apex = false;
+    if (android::base::EndsWithIgnoreCase(file, ".apex")) {
+        is_apex = true;
+    }
+    if (is_apex && !is_apex_supported()) {
+        error_exit(".apex is not supported on the target device");
+    }
+
+    if (is_apex && use_fastdeploy) {
+        error_exit("--fastdeploy doesn't support .apex files");
+    }
+
+    if (use_fastdeploy == true) {
+#if defined(ENABLE_FASTDEPLOY)
+        TemporaryFile metadataTmpFile;
+        std::string patchTmpFilePath;
+        {
+            TemporaryFile patchTmpFile;
+            patchTmpFile.DoNotRemove();
+            patchTmpFilePath = patchTmpFile.path;
+        }
+
+        FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
+        extract_metadata(file, metadataFile);
+        fclose(metadataFile);
+
+        create_patch(file, metadataTmpFile.path, patchTmpFilePath.c_str());
+        // pass all but 1st (command) and last (apk path) parameters through to pm for
+        // session creation
+        std::vector<const char*> pm_args{argv + 1, argv + argc - 1};
+        install_patch(file, patchTmpFilePath.c_str(), pm_args.size(), pm_args.data());
+        adb_unlink(patchTmpFilePath.c_str());
+        delete_device_patch_file(file);
+        return 0;
+#else
+        error_exit("fastdeploy is disabled");
+#endif
+    } else {
+        struct stat sb;
+        if (stat(file, &sb) == -1) {
+            fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
+            return 1;
+        }
+
+        unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC));
+        if (local_fd < 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));
+
+        if (is_apex) {
+            cmd += " --apex";
+        }
+
+        unique_fd remote_fd(adb_connect(cmd, &error));
+        if (remote_fd < 0) {
+            fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
+            return 1;
+        }
+
+        char buf[BUFSIZ];
+        copy_to_file(local_fd.get(), remote_fd.get());
+        read_status_line(remote_fd.get(), buf, sizeof(buf));
+
+        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_app_legacy(int argc, const char** argv, bool use_fastdeploy,
+                              bool use_localagent) {
+    printf("Performing Push Install\n");
+
+    // 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], ".apex")) {
+            error_exit("APEX packages are only compatible with Streamed Install");
+        }
+        if (android::base::EndsWithIgnoreCase(argv[i], ".apk")) {
+            last_apk = i;
+            break;
+        }
+    }
+
+    if (last_apk == -1) error_exit("need APK file on command line");
+
+    int result = -1;
+    std::vector<const char*> apk_file = {argv[last_apk]};
+    std::string apk_dest =
+            "/data/local/tmp/" + android::base::Basename(argv[last_apk]);
+
+    if (use_fastdeploy == true) {
+#if defined(ENABLE_FASTDEPLOY)
+        TemporaryFile metadataTmpFile;
+        TemporaryFile patchTmpFile;
+
+        FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
+        extract_metadata(apk_file[0], metadataFile);
+        fclose(metadataFile);
+
+        create_patch(apk_file[0], metadataTmpFile.path, patchTmpFile.path);
+        apply_patch_on_device(apk_file[0], patchTmpFile.path, apk_dest.c_str());
+#else
+        error_exit("fastdeploy is disabled");
+#endif
+    } else {
+        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:
+    if (use_fastdeploy == true) {
+#if defined(ENABLE_FASTDEPLOY)
+        delete_device_patch_file(apk_file[0]);
+#endif
+    }
+    delete_device_file(apk_dest);
+    return result;
+}
+
+int install_app(int argc, const char** argv) {
+    std::vector<int> processedArgIndicies;
+    enum installMode {
+        INSTALL_DEFAULT,
+        INSTALL_PUSH,
+        INSTALL_STREAM
+    } installMode = INSTALL_DEFAULT;
+    bool use_fastdeploy = false;
+    bool is_reinstall = false;
+    bool use_localagent = false;
+    FastDeploy_AgentUpdateStrategy agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+
+    for (int i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "--streaming")) {
+            processedArgIndicies.push_back(i);
+            installMode = INSTALL_STREAM;
+        } else if (!strcmp(argv[i], "--no-streaming")) {
+            processedArgIndicies.push_back(i);
+            installMode = INSTALL_PUSH;
+        } else if (!strcmp(argv[i], "-r")) {
+            // Note that this argument is not added to processedArgIndicies because it
+            // must be passed through to pm
+            is_reinstall = true;
+        } else if (!strcmp(argv[i], "--fastdeploy")) {
+            processedArgIndicies.push_back(i);
+            use_fastdeploy = true;
+        } else if (!strcmp(argv[i], "--no-fastdeploy")) {
+            processedArgIndicies.push_back(i);
+            use_fastdeploy = false;
+        } else if (!strcmp(argv[i], "--force-agent")) {
+            processedArgIndicies.push_back(i);
+            agent_update_strategy = FastDeploy_AgentUpdateAlways;
+        } else if (!strcmp(argv[i], "--date-check-agent")) {
+            processedArgIndicies.push_back(i);
+            agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
+        } else if (!strcmp(argv[i], "--version-check-agent")) {
+            processedArgIndicies.push_back(i);
+            agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+#ifndef _WIN32
+        } else if (!strcmp(argv[i], "--local-agent")) {
+            processedArgIndicies.push_back(i);
+            use_localagent = true;
+#endif
+        }
+    }
+
+    if (installMode == INSTALL_DEFAULT) {
+        if (use_legacy_install()) {
+            installMode = INSTALL_PUSH;
+        } else {
+            installMode = INSTALL_STREAM;
+        }
+    }
+
+    if (installMode == INSTALL_STREAM && use_legacy_install() == true) {
+        error_exit("Attempting to use streaming install on unsupported device");
+    }
+
+#if defined(ENABLE_FASTDEPLOY)
+    if (use_fastdeploy == true && get_device_api_level() < kFastDeployMinApi) {
+        printf("Fast Deploy is only compatible with devices of API version %d or higher, "
+               "ignoring.\n",
+               kFastDeployMinApi);
+        use_fastdeploy = false;
+    }
+#endif
+
+    std::vector<const char*> passthrough_argv;
+    for (int i = 0; i < argc; i++) {
+        if (std::find(processedArgIndicies.begin(), processedArgIndicies.end(), i) ==
+            processedArgIndicies.end()) {
+            passthrough_argv.push_back(argv[i]);
+        }
+    }
+    if (passthrough_argv.size() < 2) {
+        error_exit("install requires an apk argument");
+    }
+
+    if (use_fastdeploy == true) {
+#if defined(ENABLE_FASTDEPLOY)
+        fastdeploy_set_local_agent(use_localagent);
+        update_agent(agent_update_strategy);
+
+        // The last argument must be the APK file
+        const char* file = passthrough_argv.back();
+        use_fastdeploy = find_package(file);
+#else
+        error_exit("fastdeploy is disabled");
+#endif
+    }
+
+    switch (installMode) {
+        case INSTALL_PUSH:
+            return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
+                                      use_fastdeploy, use_localagent);
+        case INSTALL_STREAM:
+            return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
+                                        use_fastdeploy, use_localagent);
+        case INSTALL_DEFAULT:
+        default:
+            return 1;
+    }
+}
+
+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(argv[i], ".apex")) {
+            error_exit("APEX packages are not compatible with install-multiple");
+        }
+
+        if (android::base::EndsWithIgnoreCase(file, ".apk") ||
+            android::base::EndsWithIgnoreCase(file, ".dm") ||
+            android::base::EndsWithIgnoreCase(file, ".fsv_sig")) {
+            struct stat sb;
+            if (stat(file, &sb) != -1) total_size += sb.st_size;
+            first_apk = i;
+        } else {
+            break;
+        }
+    }
+
+    if (first_apk == -1) error_exit("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;
+    char buf[BUFSIZ];
+    {
+        unique_fd fd(adb_connect(cmd, &error));
+        if (fd < 0) {
+            fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
+            return EXIT_FAILURE;
+        }
+        read_status_line(fd.get(), buf, sizeof(buf));
+    }
+
+    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 %s -",
+                                            install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
+                                            session_id, android::base::Basename(file).c_str());
+
+        unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC));
+        if (local_fd < 0) {
+            fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
+            success = 0;
+            goto finalize_session;
+        }
+
+        std::string error;
+        unique_fd remote_fd(adb_connect(cmd, &error));
+        if (remote_fd < 0) {
+            fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
+            success = 0;
+            goto finalize_session;
+        }
+
+        copy_to_file(local_fd.get(), remote_fd.get());
+        read_status_line(remote_fd.get(), buf, sizeof(buf));
+
+        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);
+    {
+        unique_fd 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.get(), buf, sizeof(buf));
+    }
+
+    if (!strncmp("Success", buf, 7)) {
+        fputs(buf, stdout);
+        return 0;
+    }
+    fprintf(stderr, "adb: failed to finalize session\n");
+    fputs(buf, stderr);
+    return EXIT_FAILURE;
+}
+
+int install_multi_package(int argc, const char** argv) {
+    // Find all APK arguments starting at end.
+    // All other arguments passed through verbatim.
+    bool apex_found = false;
+    int first_package = -1;
+    for (int i = argc - 1; i >= 0; i--) {
+        const char* file = argv[i];
+        if (android::base::EndsWithIgnoreCase(file, ".apk") ||
+            android::base::EndsWithIgnoreCase(file, ".apex")) {
+            first_package = i;
+            if (android::base::EndsWithIgnoreCase(file, ".apex")) {
+                apex_found = true;
+            }
+        } else {
+            break;
+        }
+    }
+
+    if (first_package == -1) error_exit("need APK or APEX files on command line");
+
+    if (use_legacy_install()) {
+        fprintf(stderr, "adb: multi-package install is not supported on this device\n");
+        return EXIT_FAILURE;
+    }
+    std::string install_cmd = "exec:cmd package";
+
+    std::string multi_package_cmd =
+            android::base::StringPrintf("%s install-create --multi-package", install_cmd.c_str());
+    for (int i = 1; i < first_package; i++) {
+        multi_package_cmd += " " + escape_arg(argv[i]);
+    }
+
+    if (apex_found) {
+        multi_package_cmd += " --staged";
+    }
+
+    // Create multi-package install session
+    std::string error;
+    char buf[BUFSIZ];
+    {
+        unique_fd fd(adb_connect(multi_package_cmd, &error));
+        if (fd < 0) {
+            fprintf(stderr, "adb: connect error for create multi-package: %s\n", error.c_str());
+            return EXIT_FAILURE;
+        }
+        read_status_line(fd.get(), buf, sizeof(buf));
+    }
+
+    int parent_session_id = -1;
+    if (!strncmp("Success", buf, 7)) {
+        char* start = strrchr(buf, '[');
+        char* end = strrchr(buf, ']');
+        if (start && end) {
+            *end = '\0';
+            parent_session_id = strtol(start + 1, nullptr, 10);
+        }
+    }
+    if (parent_session_id < 0) {
+        fprintf(stderr, "adb: failed to create multi-package session\n");
+        fputs(buf, stderr);
+        return EXIT_FAILURE;
+    }
+
+    fprintf(stdout, "Created parent session ID %d.\n", parent_session_id);
+
+    std::vector<int> session_ids;
+
+    // Valid session, now create the individual sessions and stream the APKs
+    int success = EXIT_FAILURE;
+    std::string individual_cmd =
+            android::base::StringPrintf("%s install-create", install_cmd.c_str());
+    std::string all_session_ids = "";
+    for (int i = 1; i < first_package; i++) {
+        individual_cmd += " " + escape_arg(argv[i]);
+    }
+    if (apex_found) {
+        individual_cmd += " --staged";
+    }
+    std::string individual_apex_cmd = individual_cmd + " --apex";
+    std::string cmd = "";
+    for (int i = first_package; i < argc; i++) {
+        const char* file = argv[i];
+        char buf[BUFSIZ];
+        {
+            unique_fd fd;
+            // Create individual install session
+            if (android::base::EndsWithIgnoreCase(file, ".apex")) {
+                fd.reset(adb_connect(individual_apex_cmd, &error));
+            } else {
+                fd.reset(adb_connect(individual_cmd, &error));
+            }
+            if (fd < 0) {
+                fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
+                goto finalize_multi_package_session;
+            }
+            read_status_line(fd.get(), buf, sizeof(buf));
+        }
+
+        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 multi-package session\n");
+            fputs(buf, stderr);
+            goto finalize_multi_package_session;
+        }
+
+        fprintf(stdout, "Created child session ID %d.\n", session_id);
+        session_ids.push_back(session_id);
+
+        // Support splitAPKs by allowing the notation split1.apk:split2.apk:split3.apk as argument.
+        std::vector<std::string> splits = android::base::Split(file, ":");
+
+        for (const std::string& split : splits) {
+            struct stat sb;
+            if (stat(split.c_str(), &sb) == -1) {
+                fprintf(stderr, "adb: failed to stat %s: %s\n", split.c_str(), strerror(errno));
+                goto finalize_multi_package_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(split).c_str());
+
+            unique_fd local_fd(adb_open(split.c_str(), O_RDONLY | O_CLOEXEC));
+            if (local_fd < 0) {
+                fprintf(stderr, "adb: failed to open %s: %s\n", split.c_str(), strerror(errno));
+                goto finalize_multi_package_session;
+            }
+
+            std::string error;
+            unique_fd remote_fd(adb_connect(cmd, &error));
+            if (remote_fd < 0) {
+                fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
+                goto finalize_multi_package_session;
+            }
+
+            copy_to_file(local_fd.get(), remote_fd.get());
+            read_status_line(remote_fd.get(), buf, sizeof(buf));
+
+            if (strncmp("Success", buf, 7)) {
+                fprintf(stderr, "adb: failed to write %s\n", split.c_str());
+                fputs(buf, stderr);
+                goto finalize_multi_package_session;
+            }
+        }
+        all_session_ids += android::base::StringPrintf(" %d", session_id);
+    }
+
+    cmd = android::base::StringPrintf("%s install-add-session %d%s", install_cmd.c_str(),
+                                      parent_session_id, all_session_ids.c_str());
+    {
+        unique_fd fd(adb_connect(cmd, &error));
+        if (fd < 0) {
+            fprintf(stderr, "adb: connect error for install-add-session: %s\n", error.c_str());
+            goto finalize_multi_package_session;
+        }
+        read_status_line(fd.get(), buf, sizeof(buf));
+    }
+
+    if (strncmp("Success", buf, 7)) {
+        fprintf(stderr, "adb: failed to link sessions (%s)\n", cmd.c_str());
+        fputs(buf, stderr);
+        goto finalize_multi_package_session;
+    }
+
+    // no failures means we can proceed with the assumption of success
+    success = 0;
+
+finalize_multi_package_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 == 0 ? "commit" : "abandon", parent_session_id);
+    {
+        unique_fd 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.get(), buf, sizeof(buf));
+    }
+
+    if (!strncmp("Success", buf, 7)) {
+        fputs(buf, stdout);
+        if (success == 0) {
+            return 0;
+        }
+    } else {
+        fprintf(stderr, "adb: failed to finalize session\n");
+        fputs(buf, stderr);
+    }
+
+    session_ids.push_back(parent_session_id);
+    // try to abandon all remaining sessions
+    for (std::size_t i = 0; i < session_ids.size(); i++) {
+        service = android::base::StringPrintf("%s install-abandon %d", install_cmd.c_str(),
+                                              session_ids[i]);
+        fprintf(stderr, "Attempting to abandon session ID %d\n", session_ids[i]);
+        unique_fd fd(adb_connect(service, &error));
+        if (fd < 0) {
+            fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
+            continue;
+        }
+        read_status_line(fd.get(), buf, sizeof(buf));
+    }
+    return EXIT_FAILURE;
+}
+
+int delete_device_file(const std::string& filename) {
+    std::string cmd = "rm -f " + escape_arg(filename);
+    return send_shell_command(cmd);
+}
diff --git a/adb/client/adb_install.h b/adb/client/adb_install.h
new file mode 100644
index 0000000..9946604
--- /dev/null
+++ b/adb/client/adb_install.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.
+ */
+
+#pragma once
+
+#include <string>
+
+int install_app(int argc, const char** argv);
+int install_multiple_app(int argc, const char** argv);
+int install_multi_package(int argc, const char** argv);
+int uninstall_app(int argc, const char** argv);
+
+int delete_device_file(const std::string& filename);
+int delete_host_file(const std::string& filename);
+
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
new file mode 100644
index 0000000..ed6a9a8
--- /dev/null
+++ b/adb/client/auth.cpp
@@ -0,0 +1,503 @@
+/*
+ * 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_io.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() {
+    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)
+    if (username.empty() && getlogin()) username = getlogin();
+#endif
+    if (username.empty()) hostname = "unknown";
+
+    return " " + username + "@" + hostname;
+}
+
+static bool calculate_public_key(std::string* out, RSA* private_key) {
+    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;
+    }
+
+    out->resize(expected_length);
+    size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(out->data()), binary_key_data,
+                                           sizeof(binary_key_data));
+    out->resize(actual_length);
+    out->append(get_user_info());
+    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;
+    std::string pubkey;
+
+    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);
+
+    if (!calculate_public_key(&pubkey, rsa)) {
+        LOG(ERROR) << "failed to calculate public key";
+        goto out;
+    }
+
+    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)) {
+        LOG(ERROR) << "Failed to write key";
+        goto out;
+    }
+
+    if (!android::base::WriteStringToFile(pubkey, file + ".pub")) {
+        PLOG(ERROR) << "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 std::shared_ptr<RSA> read_key_file(const std::string& file) {
+    std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file.c_str(), "r"), fclose);
+    if (!fp) {
+        PLOG(ERROR) << "Failed to open '" << file << "'";
+        return nullptr;
+    }
+
+    RSA* key = RSA_new();
+    if (!PEM_read_RSAPrivateKey(fp.get(), &key, nullptr, nullptr)) {
+        LOG(ERROR) << "Failed to read key";
+        RSA_free(key);
+        return nullptr;
+    }
+
+    return std::shared_ptr<RSA>(key, RSA_free);
+}
+
+static bool load_key(const std::string& file) {
+    std::shared_ptr<RSA> key = read_key_file(file);
+    if (!key) {
+        return false;
+    }
+
+    std::lock_guard<std::mutex> lock(g_keys_mutex);
+    std::string fingerprint = hash_key(key.get());
+    if (g_keys.find(fingerprint) != g_keys.end()) {
+        LOG(INFO) << "ignoring already-loaded key: " << file;
+    } else {
+        g_keys[fingerprint] = std::move(key);
+    }
+    return true;
+}
+
+static bool load_keys(const std::string& path, bool allow_dir = true) {
+    LOG(INFO) << "load_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 load_key(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 |= load_key((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 generate_userkey() {
+    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 load_key(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;
+}
+
+static bool pubkey_from_privkey(std::string* out, const std::string& path) {
+    std::shared_ptr<RSA> privkey = read_key_file(path);
+    if (!privkey) {
+        return false;
+    }
+    return calculate_public_key(out, privkey.get());
+}
+
+std::string adb_auth_get_userkey() {
+    std::string path = get_user_key_path();
+    if (path.empty()) {
+        PLOG(ERROR) << "Error getting user key filename";
+        return "";
+    }
+
+    std::string result;
+    if (!pubkey_from_privkey(&result, path)) {
+        return "";
+    }
+    return result;
+}
+
+int adb_auth_keygen(const char* filename) {
+    return (generate_key(filename) == 0);
+}
+
+int adb_auth_pubkey(const char* filename) {
+    std::string pubkey;
+    if (!pubkey_from_privkey(&pubkey, filename)) {
+        return 1;
+    }
+    pubkey.push_back('\n');
+
+    return WriteFdExactly(STDOUT_FILENO, pubkey.data(), pubkey.size()) ? 0 : 1;
+}
+
+#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 << "'";
+                    load_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 (!generate_userkey()) {
+        LOG(ERROR) << "Failed to generate 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) {
+        load_keys(path);
+    }
+}
+
+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);
+        t->SetConnectionEstablished(true);
+        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..8ca44e8
--- /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) error_exit("usage: 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/bugreport.h b/adb/client/bugreport.h
similarity index 100%
rename from adb/bugreport.h
rename to adb/client/bugreport.h
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
new file mode 100644
index 0000000..11a3dfd
--- /dev/null
+++ b/adb/client/commandline.cpp
@@ -0,0 +1,1945 @@
+/*
+ * 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_install.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 "fastdeploy.h"
+#include "services.h"
+#include "shell_protocol.h"
+#include "sysdeps/chrono.h"
+
+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) {
+        error_exit("product directory not specified; set $ANDROID_PRODUCT_OUT");
+    }
+    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|system_ext|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 (see also `adb shell cmd package help`):\n"
+        " install [-lrtsdg] [--instant] PACKAGE\n"
+        "     push a single package to the device and install it\n"
+        " install-multiple [-lrtsdpg] [--instant] PACKAGE...\n"
+        "     push multiple APKs to the device for a single package and install them\n"
+        " install-multi-package [-lrtsdpg] [--instant] PACKAGE...\n"
+        "     push one or more packages to the device and install them atomically\n"
+        "     -r: replace existing application\n"
+        "     -t: allow test packages\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"
+        "     --no-streaming: always push APK to device and invoke Package Manager as separate steps\n"
+        "     --streaming: force streaming APK directly into Package Manager\n"
+        "     --fastdeploy: use fast deploy\n"
+        "     --no-fastdeploy: prevent use of fast deploy\n"
+        "     --force-agent: force update of deployment agent when using fast deploy\n"
+        "     --date-check-agent: update deployment agent when local version is newer and using fast deploy\n"
+        "     --version-check-agent: update deployment agent when local version has different version code and using fast deploy\n"
+#ifndef _WIN32
+        "     --local-agent: locate agent files from local source build (instead of SDK location)\n"
+#endif
+        //TODO--installlog <filename>
+        " uninstall [-k] PACKAGE\n"
+        "     remove this app package from the device\n"
+        "     '-k': keep the data and cache directories\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"
+        "\n"
+        "scripting:\n"
+        " wait-for[-TRANSPORT]-STATE\n"
+        "     wait for device to be in the given state\n"
+        "     STATE: device, recovery, rescue, sideload, bootloader, or disconnect\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 [-R]\n"
+        "      remount partitions read-write. if a reboot is required, -R will\n"
+        "      will automatically reboot the device.\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 adbd listening on USB\n"
+        " tcpip PORT               restart adbd 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"
+        " $ADB_LOCAL_TRANSPORT_MAX_PORT max emulator scan port (default 5585, 16 emus)\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(borrowed_fd 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:
+                    // data() returns a char* which doesn't have defined signedness.
+                    // Cast to uint8_t to prevent 255 from being sign extended to INT_MIN,
+                    // which doesn't get truncated on Windows.
+                    exit_code = static_cast<uint8_t>(protocol->data()[0]);
+                    continue;
+                default:
+                    continue;
+            }
+            length = protocol->data_length();
+        } else {
+            D("read_and_dump(): pre adb_read(fd=%d)", fd.get());
+            length = adb_read(fd, raw_buffer, sizeof(raw_buffer));
+            D("read_and_dump(): post adb_read(fd=%d): length=%d", fd.get(), length);
+            if (length <= 0) {
+                break;
+            }
+            callback->OnStdout(buffer_ptr, length);
+        }
+    }
+
+    return callback->Done(exit_code);
+}
+
+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) {
+            PLOG(FATAL) << "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) {
+            PLOG(FATAL) << "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) {
+            PLOG(FATAL) << "could not restore stdin mode";
+        }
+#endif
+    }
+
+#ifdef _WIN32
+    if (outFd == STDOUT_FILENO) {
+        if (_setmode(STDOUT_FILENO, old_stdout_mode) == -1) {
+            PLOG(FATAL) << "could not restore stdout mode";
+        }
+    }
+#endif
+}
+
+void copy_to_file(int inFd, int outFd) {
+    std::vector<char> buf(32 * 1024);
+    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(), buf.size());
+        } else {
+            len = adb_read(inFd, buf.data(), buf.size());
+        }
+        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,
+                       bool empty_command, const std::string& service_string) {
+    // 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() && empty_command));
+
+    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_message;
+    if (!adb_get_feature_set(&features, &error_message)) {
+        fprintf(stderr, "error: %s\n", error_message.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)) {
+                    error_exit("-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), ' ');
+    }
+
+    std::string service_string = ShellServiceString(use_shell_protocol, shell_type_arg, command);
+    return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command.empty(),
+                       service_string);
+}
+
+static int adb_abb(int argc, const char** argv) {
+    FeatureSet features;
+    std::string error_message;
+    if (!adb_get_feature_set(&features, &error_message)) {
+        fprintf(stderr, "error: %s\n", error_message.c_str());
+        return 1;
+    }
+
+    if (!CanUseFeature(features, kFeatureAbb)) {
+        error_exit("abb is not supported by the device");
+    }
+
+    optind = 1;  // argv[0] is always "abb", so set `optind` appropriately.
+
+    // Defaults.
+    constexpr char escape_char = '~';  // -e
+    constexpr bool use_shell_protocol = true;
+    constexpr auto shell_type_arg = kShellServiceArgRaw;
+    constexpr bool empty_command = false;
+
+    std::vector<const char*> args(argv + optind, argv + argc);
+    std::string service_string = "abb:" + android::base::Join(args, ABB_ARG_DELIMETER);
+
+    D("abb -e 0x%x [%*.s]\n", escape_char, static_cast<int>(service_string.size()),
+      service_string.data());
+
+    return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, empty_command,
+                       service_string);
+}
+
+static int adb_sideload_legacy(const char* filename, int in_fd, int size) {
+    std::string error;
+    unique_fd 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));
+            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());
+            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());
+        return -1;
+    }
+
+    return 0;
+}
+
+#define SIDELOAD_HOST_BLOCK_SIZE (CHUNK_SIZE)
+
+// Connects to the sideload / rescue service on the device (served by minadbd) and sends over the
+// data in an OTA package.
+//
+// It uses a simple protocol as follows.
+//
+// - 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 (e.g. "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" or "FAILFAIL" instead of a block number, we have done all
+//   the data transfer.
+//
+static int adb_sideload_install(const char* filename, bool rescue_mode) {
+    // 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(
+            "%s:%" PRId64 ":%d", rescue_mode ? "rescue-install" : "sideload-host",
+            static_cast<int64_t>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
+    std::string error;
+    unique_fd device_fd(adb_connect(service, &error));
+    if (device_fd < 0) {
+        fprintf(stderr, "adb: sideload connection failed: %s\n", error.c_str());
+
+        if (rescue_mode) {
+            return -1;
+        }
+
+        // If this is a small enough package, maybe this is an older device that doesn't
+        // support sideload-host. Try falling back to the older (<= K) sideload method.
+        if (sb.st_size > INT_MAX) {
+            return -1;
+        }
+        fprintf(stderr, "adb: trying pre-KitKat sideload method...\n");
+        return adb_sideload_legacy(filename, package_fd.get(), 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];
+
+    int64_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(kMinadbdServicesExitSuccess, buf) == 0 ||
+            strcmp(kMinadbdServicesExitFailure, 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), "");
+            if (strcmp(kMinadbdServicesExitFailure, buf) == 0) {
+                return 1;
+            }
+            return 0;
+        }
+
+        int64_t block = strtoll(buf, nullptr, 10);
+        int64_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
+        if (offset >= static_cast<int64_t>(sb.st_size)) {
+            fprintf(stderr,
+                    "adb: failed to read block %" PRId64 " at offset %" PRId64 ", past end %" PRId64
+                    "\n",
+                    block, offset, static_cast<int64_t>(sb.st_size));
+            return -1;
+        }
+
+        size_t to_write = SIDELOAD_HOST_BLOCK_SIZE;
+        if ((offset + SIDELOAD_HOST_BLOCK_SIZE) > static_cast<int64_t>(sb.st_size)) {
+            to_write = sb.st_size - offset;
+        }
+
+        if (adb_lseek(package_fd, offset, SEEK_SET) != 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;
+        }
+    }
+}
+
+static int adb_wipe_devices() {
+    auto wipe_devices_message_size = strlen(kMinadbdServicesExitSuccess);
+    std::string error;
+    unique_fd fd(adb_connect(
+            android::base::StringPrintf("rescue-wipe:userdata:%zu", wipe_devices_message_size),
+            &error));
+    if (fd < 0) {
+        fprintf(stderr, "adb: wipe device connection failed: %s\n", error.c_str());
+        return 1;
+    }
+
+    std::string message(wipe_devices_message_size, '\0');
+    if (!ReadFdExactly(fd, message.data(), wipe_devices_message_size)) {
+        fprintf(stderr, "adb: failed to read wipe result: %s\n", strerror(errno));
+        return 1;
+    }
+
+    if (message == kMinadbdServicesExitSuccess) {
+        return 0;
+    }
+
+    if (message != kMinadbdServicesExitFailure) {
+        fprintf(stderr, "adb: got unexpected message from rescue wipe %s\n", message.c_str());
+    }
+    return 1;
+}
+
+/**
+ * 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)
+    error_exit("adb %s not implemented on Win32", argv[0]);
+    __builtin_unreachable();
+#else
+    if (argc < 2) error_exit("usage: adb %s <adb service name> [ppp opts]", argv[0]);
+
+    const char* adb_service_name = argv[1];
+    std::string error_message;
+    int fd = adb_connect(adb_service_name, &error_message);
+    if (fd < 0) {
+        error_exit("could not open adb service %s: %s", adb_service_name, error_message.c_str());
+    }
+
+    pid_t pid = fork();
+    if (pid == -1) {
+        perror_exit("fork failed");
+    }
+
+    if (pid == 0) {
+        // child side
+        int i;
+
+        // copy args
+        const char** 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;
+
+        dup2(fd, STDIN_FILENO);
+        dup2(fd, STDOUT_FILENO);
+        adb_close(STDERR_FILENO);
+        adb_close(fd);
+
+        execvp("pppd", (char* const*)ppp_args);
+        perror_exit("exec pppd failed");
+    }
+
+    // parent side
+    adb_close(fd);
+    return 0;
+#endif /* !defined(_WIN32) */
+}
+
+static bool wait_for_device(const char* service,
+                            std::optional<std::chrono::milliseconds> timeout = std::nullopt) {
+    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] != "rescue" && components[3] != "sideload" &&
+        components[3] != "disconnect") {
+        fprintf(stderr,
+                "adb: unknown state %s; "
+                "expected 'any', 'bootloader', 'device', 'recovery', 'rescue', 'sideload', or "
+                "'disconnect'\n",
+                components[3].c_str());
+        return false;
+    }
+
+    std::string cmd = format_host_command(android::base::Join(components, "-").c_str());
+    if (timeout) {
+        std::thread([timeout]() {
+            std::this_thread::sleep_for(*timeout);
+            fprintf(stderr, "timeout expired while waiting for device\n");
+            _exit(1);
+        }).detach();
+    }
+    return adb_command(cmd);
+}
+
+static bool adb_root(const char* command) {
+    std::string error;
+
+    TransportId transport_id;
+    unique_fd fd(adb_connect(&transport_id, 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;
+    }
+
+    // Wait for the device to go away.
+    TransportType previous_type;
+    const char* previous_serial;
+    TransportId previous_id;
+    adb_get_transport(&previous_type, &previous_serial, &previous_id);
+
+    adb_set_transport(kTransportAny, nullptr, transport_id);
+    wait_for_device("wait-for-disconnect");
+
+    // Wait for the device to come back.
+    // If we were using a specific transport ID, there's nothing we can wait for.
+    if (previous_id == 0) {
+        adb_set_transport(previous_type, previous_serial, 0);
+        wait_for_device("wait-for-device", 6000ms);
+    }
+
+    return true;
+}
+
+int send_shell_command(const std::string& command, bool disable_shell_protocol,
+                       StandardStreamsCallbackInterface* callback) {
+    unique_fd 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.reset(adb_connect(service_string, &error));
+            if (fd >= 0) {
+                break;
+            }
+        }
+
+        fprintf(stderr, "- waiting for device -\n");
+        if (!wait_for_device("wait-for-device")) {
+            return 1;
+        }
+    }
+
+    return read_and_dump(fd.get(), use_shell_protocol, callback);
+}
+
+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, borrowed_fd fd) {
+    int old_stdin_mode = -1;
+    int old_stdout_mode = -1;
+    std::vector<char> buf(bytes);
+
+    D("write_zeros(%d) -> %d", bytes, fd.get());
+
+    stdinout_raw_prologue(-1, fd.get(), 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.get(), old_stdin_mode, old_stdout_mode);
+
+    D("write_zeros() finished");
+}
+
+static int backup(int argc, const char** argv) {
+    fprintf(stdout, "WARNING: adb backup is deprecated and may be removed in a future release\n");
+
+    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) error_exit("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) error_exit("backup either needs a list of packages or -all/-shared");
+
+    adb_unlink(filename);
+    unique_fd 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;
+    unique_fd fd(adb_connect(cmd, &error));
+    if (fd < 0) {
+        fprintf(stderr, "adb: unable to connect for backup: %s\n", error.c_str());
+        return EXIT_FAILURE;
+    }
+
+    fprintf(stdout, "Now unlock your device and confirm the backup operation...\n");
+    fflush(stdout);
+
+    copy_to_file(fd.get(), outFd.get());
+    return EXIT_SUCCESS;
+}
+
+static int restore(int argc, const char** argv) {
+    fprintf(stdout, "WARNING: adb restore is deprecated and may be removed in a future release\n");
+
+    if (argc != 2) error_exit("restore requires an argument");
+
+    const char* filename = argv[1];
+    unique_fd 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;
+    unique_fd fd(adb_connect("restore:", &error));
+    if (fd < 0) {
+        fprintf(stderr, "adb: unable to connect for restore: %s\n", error.c_str());
+        return -1;
+    }
+
+    fprintf(stdout, "Now unlock your device and confirm the restore operation.\n");
+    fflush(stdout);
+
+    copy_to_file(tarFd.get(), fd.get());
+
+    // 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.get(), STDOUT_FILENO);
+    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 {
+                error_exit("unrecognized option '%s'", *arg);
+            }
+        }
+        ++arg;
+        --narg;
+    }
+
+    if (srcs->size() > 1) {
+        *dst = srcs->back();
+        srcs->pop_back();
+    }
+}
+
+static int adb_connect_command(const std::string& command, TransportId* transport = nullptr) {
+    std::string error;
+    unique_fd fd(adb_connect(transport, command, &error));
+    if (fd < 0) {
+        fprintf(stderr, "error: %s\n", error.c_str());
+        return 1;
+    }
+    read_and_dump(fd);
+    return 0;
+}
+
+static int adb_connect_command_bidirectional(const std::string& command) {
+    std::string error;
+    unique_fd fd(adb_connect(command, &error));
+    if (fd < 0) {
+        fprintf(stderr, "error: %s\n", error.c_str());
+        return 1;
+    }
+
+    static constexpr auto forward = [](int src, int sink, bool exit_on_end) {
+        char buf[4096];
+        while (true) {
+            int rc = adb_read(src, buf, sizeof(buf));
+            if (rc == 0) {
+                if (exit_on_end) {
+                    exit(0);
+                } else {
+                    adb_shutdown(sink, SHUT_WR);
+                }
+                return;
+            } else if (rc < 0) {
+                perror_exit("read failed");
+            }
+            if (!WriteFdExactly(sink, buf, rc)) {
+                perror_exit("write failed");
+            }
+        }
+    };
+
+    std::thread read(forward, fd.get(), STDOUT_FILENO, true);
+    std::thread write(forward, STDIN_FILENO, fd.get(), false);
+    read.join();
+    write.join();
+    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
+}
+
+int adb_commandline(int argc, const char** argv) {
+    bool no_daemon = false;
+    bool is_daemon = false;
+    bool is_server = false;
+    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 = true;
+        } else if (!strcmp(argv[0], "nodaemon")) {
+            no_daemon = true;
+        } else if (!strcmp(argv[0], "fork-server")) {
+            /* this is a special flag used only when the ADB client launches the ADB Server */
+            is_daemon = true;
+        } else if (!strcmp(argv[0], "--reply-fd")) {
+            if (argc < 2) error_exit("--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') error_exit("-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') {
+                error_exit("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) error_exit("-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) error_exit("-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) error_exit("-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) {
+        error_exit("-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)) {
+                error_exit(
+                        "$ANDROID_ADB_SERVER_PORT must be a positive number less than 65535: "
+                        "got \"%s\"",
+                        server_port_str);
+            }
+        }
+
+        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) {
+            LOG(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 {
+            error_exit("adb devices [-l]");
+        }
+
+        std::string query = android::base::StringPrintf("host:%s%s", argv[0], listopt);
+        std::string error;
+        if (!adb_check_server_version(&error)) {
+            error_exit("failed to check server version: %s", error.c_str());
+        }
+        printf("List of devices attached\n");
+        return adb_query_command(query);
+    }
+    else if (!strcmp(argv[0], "connect")) {
+        if (argc != 2) error_exit("usage: 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) error_exit("usage: 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], "abb")) {
+        return adb_abb(argc, argv);
+    } 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) error_exit("usage: 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;
+        unique_fd 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.get());
+        } else {
+            copy_to_file(fd.get(), STDOUT_FILENO);
+        }
+        return 0;
+    } else if (!strcmp(argv[0], "kill-server")) {
+        return adb_kill_server() ? 0 : 1;
+    } else if (!strcmp(argv[0], "sideload")) {
+        if (argc != 2) error_exit("sideload requires an argument");
+        if (adb_sideload_install(argv[1], false /* rescue_mode */)) {
+            return 1;
+        } else {
+            return 0;
+        }
+    } else if (!strcmp(argv[0], "rescue")) {
+        // adb rescue getprop
+        // adb rescue getprop <prop>
+        // adb rescue install <filename>
+        // adb rescue wipe userdata
+        if (argc < 2) error_exit("rescue requires at least one argument");
+        if (!strcmp(argv[1], "getprop")) {
+            if (argc == 2) {
+                return adb_connect_command("rescue-getprop:");
+            }
+            if (argc == 3) {
+                return adb_connect_command(
+                        android::base::StringPrintf("rescue-getprop:%s", argv[2]));
+            }
+            error_exit("invalid rescue getprop arguments");
+        } else if (!strcmp(argv[1], "install")) {
+            if (argc != 3) error_exit("rescue install requires two arguments");
+            if (adb_sideload_install(argv[2], true /* rescue_mode */) != 0) {
+                return 1;
+            }
+        } else if (!strcmp(argv[1], "wipe")) {
+            if (argc != 3 || strcmp(argv[2], "userdata") != 0) {
+                error_exit("invalid rescue wipe arguments");
+            }
+            return adb_wipe_devices();
+        } else {
+            error_exit("invalid rescue argument");
+        }
+        return 0;
+    } else if (!strcmp(argv[0], "tcpip")) {
+        if (argc != 2) error_exit("tcpip requires an argument");
+        int port;
+        if (!android::base::ParseInt(argv[1], &port, 1, 65535)) {
+            error_exit("tcpip: invalid port: %s", argv[1]);
+        }
+        return adb_connect_command(android::base::StringPrintf("tcpip:%d", port));
+    }
+    // clang-format off
+    else if (!strcmp(argv[0], "remount") ||
+             !strcmp(argv[0], "reboot") ||
+             !strcmp(argv[0], "reboot-bootloader") ||
+             !strcmp(argv[0], "reboot-fastboot") ||
+             !strcmp(argv[0], "usb") ||
+             !strcmp(argv[0], "disable-verity") ||
+             !strcmp(argv[0], "enable-verity")) {
+        // clang-format on
+        std::string command;
+        if (!strcmp(argv[0], "reboot-bootloader")) {
+            command = "reboot:bootloader";
+        } else if (!strcmp(argv[0], "reboot-fastboot")) {
+            command = "reboot:fastboot";
+        } 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) error_exit("%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_message;
+        if (strcmp(argv[0], "--list") == 0) {
+            if (argc != 1) error_exit("--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) error_exit("--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) error_exit("--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) error_exit("--no-rebind takes two arguments");
+            if (forward_targets_are_valid(argv[1], argv[2], &error_message)) {
+                cmd = host_prefix + ":forward:norebind:" + argv[1] + ";" + argv[2];
+            }
+        } else {
+            // forward <local> <remote>
+            if (argc != 2) error_exit("forward takes two arguments");
+            if (forward_targets_are_valid(argv[0], argv[1], &error_message)) {
+                cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
+            }
+        }
+
+        if (!error_message.empty()) {
+            error_exit("error: %s", error_message.c_str());
+        }
+
+        unique_fd fd(adb_connect(cmd, &error_message));
+        if (fd < 0 || !adb_status(fd.get(), &error_message)) {
+            error_exit("error: %s", error_message.c_str());
+        }
+
+        // Server or device may optionally return a resolved TCP port number.
+        std::string resolved_port;
+        if (ReadProtocolString(fd, &resolved_port, &error_message) && !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) error_exit("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) error_exit("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()) error_exit("pull requires an argument");
+        return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
+    } else if (!strcmp(argv[0], "install")) {
+        if (argc < 2) error_exit("install requires an argument");
+        return install_app(argc, argv);
+    } else if (!strcmp(argv[0], "install-multiple")) {
+        if (argc < 2) error_exit("install-multiple requires an argument");
+        return install_multiple_app(argc, argv);
+    } else if (!strcmp(argv[0], "install-multi-package")) {
+        if (argc < 2) error_exit("install-multi-package requires an argument");
+        return install_multi_package(argc, argv);
+    } else if (!strcmp(argv[0], "uninstall")) {
+        if (argc < 2) error_exit("uninstall requires an argument");
+        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 {
+            error_exit("usage: adb sync [-l] [PARTITION]");
+        }
+
+        if (src.empty()) src = "all";
+        std::vector<std::string> partitions{"data",   "odm",        "oem",   "product",
+                                            "system", "system_ext", "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;
+            }
+        }
+        if (!found) error_exit("don't know how to sync %s partition", src.c_str());
+        return 0;
+    }
+    /* 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) error_exit("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], "pubkey")) {
+        if (argc != 2) error_exit("pubkey requires an argument");
+        return adb_auth_pubkey(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) {
+            error_exit("usage: adb raw SERVICE");
+        }
+        return adb_connect_command_bidirectional(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 {
+                error_exit("usage: adb reconnect [device|offline]");
+            }
+        }
+    }
+
+    error_exit("unknown command %s", argv[0]);
+    __builtin_unreachable();
+}
diff --git a/adb/client/commandline.h b/adb/client/commandline.h
new file mode 100644
index 0000000..6cfd4f7
--- /dev/null
+++ b/adb/client/commandline.h
@@ -0,0 +1,108 @@
+/*
+ * 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);
+
+void copy_to_file(int inFd, int outFd);
+
+// 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/client/console.cpp b/adb/client/console.cpp
new file mode 100644
index 0000000..d10f4de
--- /dev/null
+++ b/adb/client/console.cpp
@@ -0,0 +1,181 @@
+/*
+ * 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 "sysdeps.h"
+
+#include <stdio.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <cutils/sockets.h>
+
+#include "adb.h"
+#include "adb_client.h"
+#include "adb_io.h"
+#include "adb_utils.h"
+
+// Return the console authentication command for the emulator, if needed
+static std::string adb_construct_auth_command() {
+    static const char auth_token_filename[] = ".emulator_console_auth_token";
+
+    std::string auth_token_path = adb_get_homedir_path();
+    auth_token_path += OS_PATH_SEPARATOR;
+    auth_token_path += auth_token_filename;
+
+    // read the token
+    std::string token;
+    if (!android::base::ReadFileToString(auth_token_path, &token)
+        || token.empty()) {
+        // we either can't read the file, or it doesn't exist, or it's empty -
+        // either way we won't add any authentication command.
+        return {};
+    }
+
+    // now construct and return the actual command: "auth <token>\n"
+    std::string command = "auth ";
+    command += token;
+    command += '\n';
+    return command;
+}
+
+// Return the console port of the currently connected emulator (if any) or -1 if
+// there is no emulator, and -2 if there is more than one.
+static int adb_get_emulator_console_port(const char* serial) {
+    if (serial) {
+        // The user specified a serial number; is it an emulator?
+        int port;
+        return (sscanf(serial, "emulator-%d", &port) == 1) ? port : -1;
+    }
+
+    // No specific device was given, so get the list of connected devices and
+    // search for emulators. If there's one, we'll take it. If there are more
+    // than one, that's an error.
+    std::string devices;
+    std::string error;
+    if (!adb_query("host:devices", &devices, &error)) {
+        fprintf(stderr, "error: no emulator connected: %s\n", error.c_str());
+        return -1;
+    }
+
+    int port = -1;
+    size_t emulator_count = 0;
+    for (const auto& device : android::base::Split(devices, "\n")) {
+        if (sscanf(device.c_str(), "emulator-%d", &port) == 1) {
+            if (++emulator_count > 1) {
+                fprintf(
+                    stderr, "error: more than one emulator detected; use -s\n");
+                return -1;
+            }
+        }
+    }
+
+    if (emulator_count == 0) {
+        fprintf(stderr, "error: no emulator detected\n");
+        return -1;
+    }
+
+    return port;
+}
+
+static int connect_to_console(const char* serial) {
+    int port = adb_get_emulator_console_port(serial);
+    if (port == -1) {
+        return -1;
+    }
+
+    std::string error;
+    int fd = network_loopback_client(port, SOCK_STREAM, &error);
+    if (fd == -1) {
+        fprintf(stderr, "error: could not connect to TCP port %d: %s\n", port,
+                error.c_str());
+        return -1;
+    }
+    return fd;
+}
+
+int adb_send_emulator_command(int argc, const char** argv, const char* serial) {
+    unique_fd fd(connect_to_console(serial));
+    if (fd == -1) {
+        return 1;
+    }
+
+    std::string commands = adb_construct_auth_command();
+
+    for (int i = 1; i < argc; i++) {
+        commands.append(argv[i]);
+        commands.push_back(i == argc - 1 ? '\n' : ' ');
+    }
+
+    commands.append("quit\n");
+
+    if (!WriteFdExactly(fd, commands)) {
+        fprintf(stderr, "error: cannot write to emulator: %s\n",
+                strerror(errno));
+        return 1;
+    }
+
+    // Drain output that the emulator console has sent us to prevent a problem
+    // on Windows where if adb closes the socket without reading all the data,
+    // the emulator's next call to recv() will have an ECONNABORTED error,
+    // preventing the emulator from reading the command that adb has sent.
+    // https://code.google.com/p/android/issues/detail?id=21021
+    int result;
+    std::string emulator_output;
+    do {
+        char buf[BUFSIZ];
+        result = adb_read(fd, buf, sizeof(buf));
+        // Keep reading until zero bytes (orderly/graceful shutdown) or an
+        // error. If 'adb emu kill' is executed, the emulator calls exit() with
+        // the socket open (and shutdown(SD_SEND) was not called), which causes
+        // Windows to send a TCP RST segment which causes adb to get ECONNRESET.
+        // Any other emu command is followed by the quit command that we
+        // appended above, and that causes the emulator to close the socket
+        // which should cause zero bytes (orderly/graceful shutdown) to be
+        // returned.
+        if (result > 0) emulator_output.append(buf, result);
+    } while (result > 0);
+
+    // Note: the following messages are expected to be quite stable from emulator.
+    //
+    // Emulator console will send the following message upon connection:
+    //
+    // Android Console: Authentication required
+    // Android Console: type 'auth <auth_token>' to authenticate
+    // Android Console: you can find your <auth_token> in
+    // '/<path-to-home>/.emulator_console_auth_token'
+    // OK\r\n
+    //
+    // and the following after authentication:
+    // Android Console: type 'help' for a list of commands
+    // OK\r\n
+    //
+    // So try search and skip first two "OK\r\n", print the rest.
+    //
+    const std::string delims = "OK\r\n";
+    size_t found = 0;
+    for (int i = 0; i < 2; ++i) {
+        const size_t result = emulator_output.find(delims, found);
+        if (result == std::string::npos) {
+            break;
+        } else {
+            found = result + delims.size();
+        }
+    }
+
+    printf("%s", emulator_output.c_str() + found);
+    return 0;
+}
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
new file mode 100644
index 0000000..f4e8664
--- /dev/null
+++ b/adb/client/fastdeploy.cpp
@@ -0,0 +1,342 @@
+/*
+ * 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 "fastdeploy.h"
+
+#include <string.h>
+#include <algorithm>
+#include <array>
+#include <memory>
+
+#include "android-base/file.h"
+#include "android-base/strings.h"
+#include "androidfw/ResourceTypes.h"
+#include "androidfw/ZipFileRO.h"
+#include "client/file_sync_client.h"
+#include "commandline.h"
+#include "fastdeploycallbacks.h"
+#include "sysdeps.h"
+
+#include "adb_utils.h"
+
+static constexpr long kRequiredAgentVersion = 0x00000002;
+
+static constexpr const char* kDeviceAgentPath = "/data/local/tmp/";
+
+static bool g_use_localagent = false;
+
+long get_agent_version() {
+    std::vector<char> versionOutputBuffer;
+    std::vector<char> versionErrorBuffer;
+
+    int statusCode = capture_shell_command("/data/local/tmp/deployagent version",
+                                           &versionOutputBuffer, &versionErrorBuffer);
+    long version = -1;
+
+    if (statusCode == 0 && versionOutputBuffer.size() > 0) {
+        version = strtol((char*)versionOutputBuffer.data(), NULL, 16);
+    }
+
+    return version;
+}
+
+int get_device_api_level() {
+    std::vector<char> sdkVersionOutputBuffer;
+    std::vector<char> sdkVersionErrorBuffer;
+    int api_level = -1;
+
+    int statusCode = capture_shell_command("getprop ro.build.version.sdk", &sdkVersionOutputBuffer,
+                                           &sdkVersionErrorBuffer);
+    if (statusCode == 0 && sdkVersionOutputBuffer.size() > 0) {
+        api_level = strtol((char*)sdkVersionOutputBuffer.data(), NULL, 10);
+    }
+
+    return api_level;
+}
+
+void fastdeploy_set_local_agent(bool use_localagent) {
+    g_use_localagent = use_localagent;
+}
+
+// local_path - must start with a '/' and be relative to $ANDROID_PRODUCT_OUT
+static std::string get_agent_component_host_path(const char* local_path, const char* sdk_path) {
+    std::string adb_dir = android::base::GetExecutableDirectory();
+    if (adb_dir.empty()) {
+        error_exit("Could not determine location of adb!");
+    }
+
+    if (g_use_localagent) {
+        const char* product_out = getenv("ANDROID_PRODUCT_OUT");
+        if (product_out == nullptr) {
+            error_exit("Could not locate %s because $ANDROID_PRODUCT_OUT is not defined",
+                       local_path);
+        }
+        return android::base::StringPrintf("%s%s", product_out, local_path);
+    } else {
+        return adb_dir + sdk_path;
+    }
+}
+
+static bool deploy_agent(bool checkTimeStamps) {
+    std::vector<const char*> srcs;
+    std::string jar_path =
+            get_agent_component_host_path("/system/framework/deployagent.jar", "/deployagent.jar");
+    std::string script_path =
+            get_agent_component_host_path("/system/bin/deployagent", "/deployagent");
+    srcs.push_back(jar_path.c_str());
+    srcs.push_back(script_path.c_str());
+
+    if (do_sync_push(srcs, kDeviceAgentPath, checkTimeStamps)) {
+        // on windows the shell script might have lost execute permission
+        // so need to set this explicitly
+        const char* kChmodCommandPattern = "chmod 777 %sdeployagent";
+        std::string chmodCommand =
+                android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentPath);
+        int ret = send_shell_command(chmodCommand);
+        if (ret != 0) {
+            error_exit("Error executing %s returncode: %d", chmodCommand.c_str(), ret);
+        }
+    } else {
+        error_exit("Error pushing agent files to device");
+    }
+
+    return true;
+}
+
+void update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy) {
+    long agent_version = get_agent_version();
+    switch (agentUpdateStrategy) {
+        case FastDeploy_AgentUpdateAlways:
+            deploy_agent(false);
+            break;
+        case FastDeploy_AgentUpdateNewerTimeStamp:
+            deploy_agent(true);
+            break;
+        case FastDeploy_AgentUpdateDifferentVersion:
+            if (agent_version != kRequiredAgentVersion) {
+                if (agent_version < 0) {
+                    printf("Could not detect agent on device, deploying\n");
+                } else {
+                    printf("Device agent version is (%ld), (%ld) is required, re-deploying\n",
+                           agent_version, kRequiredAgentVersion);
+                }
+                deploy_agent(false);
+            }
+            break;
+    }
+
+    agent_version = get_agent_version();
+    if (agent_version != kRequiredAgentVersion) {
+        error_exit("After update agent version remains incorrect! Expected %ld but version is %ld",
+                   kRequiredAgentVersion, agent_version);
+    }
+}
+
+static std::string get_string_from_utf16(const char16_t* input, int input_len) {
+    ssize_t utf8_length = utf16_to_utf8_length(input, input_len);
+    if (utf8_length <= 0) {
+        return {};
+    }
+    std::string utf8;
+    utf8.resize(utf8_length);
+    utf16_to_utf8(input, input_len, &*utf8.begin(), utf8_length + 1);
+    return utf8;
+}
+
+static std::string get_packagename_from_apk(const char* apkPath) {
+#undef open
+    std::unique_ptr<android::ZipFileRO> zipFile(android::ZipFileRO::open(apkPath));
+#define open ___xxx_unix_open
+    if (zipFile == nullptr) {
+        perror_exit("Could not open %s", apkPath);
+    }
+    android::ZipEntryRO entry = zipFile->findEntryByName("AndroidManifest.xml");
+    if (entry == nullptr) {
+        error_exit("Could not find AndroidManifest.xml inside %s", apkPath);
+    }
+    uint32_t manifest_len = 0;
+    if (!zipFile->getEntryInfo(entry, NULL, &manifest_len, NULL, NULL, NULL, NULL)) {
+        error_exit("Could not read AndroidManifest.xml inside %s", apkPath);
+    }
+    std::vector<char> manifest_data(manifest_len);
+    if (!zipFile->uncompressEntry(entry, manifest_data.data(), manifest_len)) {
+        error_exit("Could not uncompress AndroidManifest.xml inside %s", apkPath);
+    }
+    android::ResXMLTree tree;
+    android::status_t setto_status = tree.setTo(manifest_data.data(), manifest_len, true);
+    if (setto_status != android::OK) {
+        error_exit("Could not parse AndroidManifest.xml inside %s", apkPath);
+    }
+    android::ResXMLParser::event_code_t code;
+    while ((code = tree.next()) != android::ResXMLParser::BAD_DOCUMENT &&
+           code != android::ResXMLParser::END_DOCUMENT) {
+        switch (code) {
+            case android::ResXMLParser::START_TAG: {
+                size_t element_name_length;
+                const char16_t* element_name = tree.getElementName(&element_name_length);
+                if (element_name == nullptr) {
+                    continue;
+                }
+                std::u16string element_name_string(element_name, element_name_length);
+                if (element_name_string == u"manifest") {
+                    for (size_t i = 0; i < tree.getAttributeCount(); i++) {
+                        size_t attribute_name_length;
+                        const char16_t* attribute_name_text =
+                                tree.getAttributeName(i, &attribute_name_length);
+                        if (attribute_name_text == nullptr) {
+                            continue;
+                        }
+                        std::u16string attribute_name_string(attribute_name_text,
+                                                             attribute_name_length);
+                        if (attribute_name_string == u"package") {
+                            size_t attribute_value_length;
+                            const char16_t* attribute_value_text =
+                                    tree.getAttributeStringValue(i, &attribute_value_length);
+                            if (attribute_value_text == nullptr) {
+                                continue;
+                            }
+                            return get_string_from_utf16(attribute_value_text,
+                                                         attribute_value_length);
+                        }
+                    }
+                }
+                break;
+            }
+            default:
+                break;
+        }
+    }
+    error_exit("Could not find package name tag in AndroidManifest.xml inside %s", apkPath);
+}
+
+void extract_metadata(const char* apkPath, FILE* outputFp) {
+    std::string packageName = get_packagename_from_apk(apkPath);
+    const char* kAgentExtractCommandPattern = "/data/local/tmp/deployagent extract %s";
+    std::string extractCommand =
+            android::base::StringPrintf(kAgentExtractCommandPattern, packageName.c_str());
+
+    std::vector<char> extractErrorBuffer;
+    DeployAgentFileCallback cb(outputFp, &extractErrorBuffer);
+    int returnCode = send_shell_command(extractCommand, false, &cb);
+    if (returnCode != 0) {
+        fprintf(stderr, "Executing %s returned %d\n", extractCommand.c_str(), returnCode);
+        fprintf(stderr, "%*s\n", int(extractErrorBuffer.size()), extractErrorBuffer.data());
+        error_exit("Aborting");
+    }
+}
+
+static std::string get_patch_generator_command() {
+    if (g_use_localagent) {
+        // This should never happen on a Windows machine
+        const char* host_out = getenv("ANDROID_HOST_OUT");
+        if (host_out == nullptr) {
+            error_exit(
+                    "Could not locate deploypatchgenerator.jar because $ANDROID_HOST_OUT "
+                    "is not defined");
+        }
+        return android::base::StringPrintf("java -jar %s/framework/deploypatchgenerator.jar",
+                                           host_out);
+    }
+
+    std::string adb_dir = android::base::GetExecutableDirectory();
+    if (adb_dir.empty()) {
+        error_exit("Could not locate deploypatchgenerator.jar");
+    }
+    return android::base::StringPrintf(R"(java -jar "%s/deploypatchgenerator.jar")",
+                                       adb_dir.c_str());
+}
+
+void create_patch(const char* apkPath, const char* metadataPath, const char* patchPath) {
+    std::string generatePatchCommand = android::base::StringPrintf(
+            R"(%s "%s" "%s" > "%s")", get_patch_generator_command().c_str(), apkPath, metadataPath,
+            patchPath);
+    int returnCode = system(generatePatchCommand.c_str());
+    if (returnCode != 0) {
+        error_exit("Executing %s returned %d", generatePatchCommand.c_str(), returnCode);
+    }
+}
+
+std::string get_patch_path(const char* apkPath) {
+    std::string packageName = get_packagename_from_apk(apkPath);
+    std::string patchDevicePath =
+            android::base::StringPrintf("%s%s.patch", kDeviceAgentPath, packageName.c_str());
+    return patchDevicePath;
+}
+
+void apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath) {
+    const std::string kAgentApplyCommandPattern = "/data/local/tmp/deployagent apply %s %s -o %s";
+    std::string packageName = get_packagename_from_apk(apkPath);
+    std::string patchDevicePath = get_patch_path(apkPath);
+
+    std::vector<const char*> srcs = {patchPath};
+    bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
+    if (!push_ok) {
+        error_exit("Error pushing %s to %s returned", patchPath, patchDevicePath.c_str());
+    }
+
+    std::string applyPatchCommand =
+            android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
+                                        patchDevicePath.c_str(), outputPath);
+
+    int returnCode = send_shell_command(applyPatchCommand);
+    if (returnCode != 0) {
+        error_exit("Executing %s returned %d", applyPatchCommand.c_str(), returnCode);
+    }
+}
+
+void install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv) {
+    const std::string kAgentApplyCommandPattern = "/data/local/tmp/deployagent apply %s %s -pm %s";
+    std::string packageName = get_packagename_from_apk(apkPath);
+
+    std::string patchDevicePath =
+            android::base::StringPrintf("%s%s.patch", kDeviceAgentPath, packageName.c_str());
+
+    std::vector<const char*> srcs{patchPath};
+    bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
+    if (!push_ok) {
+        error_exit("Error pushing %s to %s returned", patchPath, patchDevicePath.c_str());
+    }
+
+    std::vector<unsigned char> applyOutputBuffer;
+    std::vector<unsigned char> applyErrorBuffer;
+    std::string argsString;
+
+    bool rSwitchPresent = false;
+    for (int i = 0; i < argc; i++) {
+        argsString.append(argv[i]);
+        argsString.append(" ");
+        if (!strcmp(argv[i], "-r")) {
+            rSwitchPresent = true;
+        }
+    }
+    if (!rSwitchPresent) {
+        argsString.append("-r");
+    }
+
+    std::string applyPatchCommand =
+            android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
+                                        patchDevicePath.c_str(), argsString.c_str());
+    int returnCode = send_shell_command(applyPatchCommand);
+    if (returnCode != 0) {
+        error_exit("Executing %s returned %d", applyPatchCommand.c_str(), returnCode);
+    }
+}
+
+bool find_package(const char* apkPath) {
+    const std::string findCommand =
+            "/data/local/tmp/deployagent find " + get_packagename_from_apk(apkPath);
+    return !send_shell_command(findCommand);
+}
diff --git a/adb/client/fastdeploy.h b/adb/client/fastdeploy.h
new file mode 100644
index 0000000..7b7f2ec
--- /dev/null
+++ b/adb/client/fastdeploy.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>
+
+enum FastDeploy_AgentUpdateStrategy {
+    FastDeploy_AgentUpdateAlways,
+    FastDeploy_AgentUpdateNewerTimeStamp,
+    FastDeploy_AgentUpdateDifferentVersion
+};
+
+void fastdeploy_set_local_agent(bool use_localagent);
+int get_device_api_level();
+void update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy);
+void extract_metadata(const char* apkPath, FILE* outputFp);
+void create_patch(const char* apkPath, const char* metadataPath, const char* patchPath);
+void apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath);
+void install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv);
+std::string get_patch_path(const char* apkPath);
+bool find_package(const char* apkPath);
diff --git a/adb/client/fastdeploycallbacks.cpp b/adb/client/fastdeploycallbacks.cpp
new file mode 100644
index 0000000..23a0aca
--- /dev/null
+++ b/adb/client/fastdeploycallbacks.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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 ADB
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include "client/file_sync_client.h"
+#include "commandline.h"
+#include "sysdeps.h"
+
+#include "fastdeploycallbacks.h"
+
+static void appendBuffer(std::vector<char>* buffer, const char* input, int length) {
+    if (buffer != NULL) {
+        buffer->insert(buffer->end(), input, input + length);
+    }
+}
+
+class DeployAgentBufferCallback : public StandardStreamsCallbackInterface {
+  public:
+    DeployAgentBufferCallback(std::vector<char>* outBuffer, std::vector<char>* errBuffer);
+
+    virtual void OnStdout(const char* buffer, int length);
+    virtual void OnStderr(const char* buffer, int length);
+    virtual int Done(int status);
+
+  private:
+    std::vector<char>* mpOutBuffer;
+    std::vector<char>* mpErrBuffer;
+};
+
+int capture_shell_command(const char* command, std::vector<char>* outBuffer,
+                          std::vector<char>* errBuffer) {
+    DeployAgentBufferCallback cb(outBuffer, errBuffer);
+    return send_shell_command(command, false, &cb);
+}
+
+DeployAgentFileCallback::DeployAgentFileCallback(FILE* outputFile, std::vector<char>* errBuffer) {
+    mpOutFile = outputFile;
+    mpErrBuffer = errBuffer;
+    mBytesWritten = 0;
+}
+
+void DeployAgentFileCallback::OnStdout(const char* buffer, int length) {
+    if (mpOutFile != NULL) {
+        int bytes_written = fwrite(buffer, 1, length, mpOutFile);
+        if (bytes_written != length) {
+            printf("Write error %d\n", bytes_written);
+        }
+        mBytesWritten += bytes_written;
+    }
+}
+
+void DeployAgentFileCallback::OnStderr(const char* buffer, int length) {
+    appendBuffer(mpErrBuffer, buffer, length);
+}
+
+int DeployAgentFileCallback::Done(int status) {
+    return status;
+}
+
+int DeployAgentFileCallback::getBytesWritten() {
+    return mBytesWritten;
+}
+
+DeployAgentBufferCallback::DeployAgentBufferCallback(std::vector<char>* outBuffer,
+                                                     std::vector<char>* errBuffer) {
+    mpOutBuffer = outBuffer;
+    mpErrBuffer = errBuffer;
+}
+
+void DeployAgentBufferCallback::OnStdout(const char* buffer, int length) {
+    appendBuffer(mpOutBuffer, buffer, length);
+}
+
+void DeployAgentBufferCallback::OnStderr(const char* buffer, int length) {
+    appendBuffer(mpErrBuffer, buffer, length);
+}
+
+int DeployAgentBufferCallback::Done(int status) {
+    return status;
+}
diff --git a/adb/client/fastdeploycallbacks.h b/adb/client/fastdeploycallbacks.h
new file mode 100644
index 0000000..7e049c5
--- /dev/null
+++ b/adb/client/fastdeploycallbacks.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 <vector>
+#include "commandline.h"
+
+class DeployAgentFileCallback : public StandardStreamsCallbackInterface {
+  public:
+    DeployAgentFileCallback(FILE* outputFile, std::vector<char>* errBuffer);
+
+    virtual void OnStdout(const char* buffer, int length);
+    virtual void OnStderr(const char* buffer, int length);
+    virtual int Done(int status);
+
+    int getBytesWritten();
+
+  private:
+    FILE* mpOutFile;
+    std::vector<char>* mpErrBuffer;
+    int mBytesWritten;
+};
+
+int capture_shell_command(const char* command, std::vector<char>* outBuffer,
+                          std::vector<char>* errBuffer);
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
new file mode 100644
index 0000000..5d10238
--- /dev/null
+++ b/adb/client/file_sync_client.cpp
@@ -0,0 +1,1287 @@
+/*
+ * 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)) {
+            Error("failed to get feature set: %s", error.c_str());
+        } else {
+            have_stat_v2_ = CanUseFeature(features_, kFeatureStat2);
+            fd.reset(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);
+        }
+
+        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.get(), .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.get(), &msg.stat_v2, sizeof(msg.stat_v2))) {
+                PLOG(FATAL) << "protocol fault: failed to read stat response";
+            }
+
+            if (msg.stat_v2.id != ID_LSTAT_V2 && msg.stat_v2.id != ID_STAT_V2) {
+                PLOG(FATAL) << "protocol fault: stat response has wrong message id: "
+                            << 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.get(), &msg.stat_v1, sizeof(msg.stat_v1))) {
+                PLOG(FATAL) << "protocol fault: failed to read stat response";
+            }
+
+            if (msg.stat_v1.id != ID_LSTAT_V1) {
+                PLOG(FATAL) << "protocol fault: stat response has wrong message id: "
+                            << 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;
+
+        unique_fd 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));
+                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);
+        }
+
+        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...
+    unique_fd 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)) {
+            if (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);
+    unique_fd 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_unlink(lpath);
+            return false;
+        }
+
+        if (msg.data.id == ID_DONE) break;
+
+        if (msg.data.id != ID_DATA) {
+            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_unlink(lpath);
+            return false;
+        }
+
+        char buffer[SYNC_DATA_MAX];
+        if (!ReadFdExactly(sc.fd, buffer, msg.data.size)) {
+            adb_unlink(lpath);
+            return false;
+        }
+
+        if (!WriteFdExactly(lfd, buffer, msg.data.size)) {
+            sc.Error("cannot write '%s': %s", lpath, strerror(errno));
+            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);
+    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(), kFeatureFixedPushMkdir) &&
+        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) && 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, 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, lpath)) {
+        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, 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..50c03e8
--- /dev/null
+++ b/adb/client/line_printer.cpp
@@ -0,0 +1,138 @@
+// 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;
+// This does not account for multiple UTF-8 bytes corresponding to a single Unicode code point, or
+// multiple code points corresponding to a single grapheme cluster (user-perceived character).
+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_) {
+    if (type == LineType::INFO) {
+        info_line_ = to_print + "\n";
+    } else {
+        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);
+
+    to_print = ElideMiddle(to_print, static_cast<size_t>(csbi.dwSize.X));
+    std::wstring to_print_wide;
+    // ElideMiddle may create invalid UTF-8, so ignore conversion errors.
+    (void)android::base::UTF8ToWide(to_print, &to_print_wide);
+    // 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) {
+        char_data[i].Char.UnicodeChar = i < to_print_wide.size() ? to_print_wide[i] : L' ';
+        char_data[i].Attributes = csbi.wAttributes;
+    }
+    WriteConsoleOutputW(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 (smart_terminal_) {
+      if (!have_blank_line_) Out("\n");
+      have_blank_line_ = true;
+  } else {
+      Out(info_line_);
+      info_line_.clear();
+  }
+}
diff --git a/adb/client/line_printer.h b/adb/client/line_printer.h
new file mode 100644
index 0000000..4c4c7c6
--- /dev/null
+++ b/adb/client/line_printer.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef NINJA_LINE_PRINTER_H_
+#define NINJA_LINE_PRINTER_H_
+
+#include <stddef.h>
+#include <string>
+
+/// Prints lines of text, possibly overprinting previously printed lines
+/// if the terminal supports it.
+struct LinePrinter {
+  LinePrinter();
+
+  bool is_smart_terminal() const { return smart_terminal_; }
+  void set_smart_terminal(bool smart) { smart_terminal_ = smart; }
+
+  enum LineType { INFO, WARNING, ERROR };
+
+  /// Outputs the given line. INFO output will be overwritten.
+  /// WARNING and ERROR appear on a line to themselves.
+  void Print(std::string to_print, LineType type);
+
+  /// If there's an INFO line, keep it. If not, do nothing.
+  void KeepInfoLine();
+
+ private:
+  /// Whether we can do fancy terminal control codes.
+  bool smart_terminal_;
+
+  /// Whether the caret is at the beginning of a blank line.
+  bool have_blank_line_;
+
+  /// The last printed info line when printing to a dumb terminal.
+  std::string info_line_;
+
+#ifdef _WIN32
+  void* console_;
+#endif
+};
+
+#endif  // NINJA_LINE_PRINTER_H_
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 31cb853..0c5c28f 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -32,23 +32,27 @@
 
 #include "adb.h"
 #include "adb_auth.h"
+#include "adb_client.h"
 #include "adb_listeners.h"
 #include "adb_utils.h"
 #include "commandline.h"
 #include "sysdeps/chrono.h"
 #include "transport.h"
 
+const char** __adb_argv;
+const char** __adb_envp;
+
 static void setup_daemon_logging() {
     const std::string log_file_path(GetLogFilePath());
-    int fd = unix_open(log_file_path.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0640);
+    int fd = unix_open(log_file_path, O_WRONLY | O_CREAT | O_APPEND, 0640);
     if (fd == -1) {
-        fatal("cannot open '%s': %s", log_file_path.c_str(), strerror(errno));
+        PLOG(FATAL) << "cannot open " << log_file_path;
     }
     if (dup2(fd, STDOUT_FILENO) == -1) {
-        fatal("cannot redirect stdout: %s", strerror(errno));
+        PLOG(FATAL) << "cannot redirect stdout";
     }
     if (dup2(fd, STDERR_FILENO) == -1) {
-        fatal("cannot redirect stderr: %s", strerror(errno));
+        PLOG(FATAL) << "cannot redirect stderr";
     }
     unix_close(fd);
 
@@ -56,15 +60,6 @@
     LOG(INFO) << adb_version();
 }
 
-#if defined(_WIN32)
-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().
-    exit(STATUS_CONTROL_C_EXIT);
-    return TRUE;
-}
-#endif
-
 void adb_server_cleanup() {
     // Upon exit, we want to clean up in the following order:
     //   1. close_smartsockets, so that we don't get any new clients
@@ -89,20 +84,24 @@
     // 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) {
-            fatal("cannot make stdout unbuffered: %s", strerror(errno));
+        if (setvbuf(stdout, nullptr, _IONBF, 0) == -1) {
+            PLOG(FATAL) << "cannot make stdout unbuffered";
         }
-        if (setvbuf(stderr, NULL, _IONBF, 0) == -1) {
-            fatal("cannot make stderr unbuffered: %s", strerror(errno));
+        if (setvbuf(stderr, nullptr, _IONBF, 0) == -1) {
+            PLOG(FATAL) << "cannot make stderr unbuffered";
         }
     }
 
-    SetConsoleCtrlHandler(ctrlc_handler, TRUE);
-#else
+    // TODO: On Ctrl-C, consider trying to kill a starting up adb server (if we're in
+    // launch_server) by calling GenerateConsoleCtrlEvent().
+
+    // On Windows, SIGBREAK is when Ctrl-Break is pressed or the console window is closed. It should
+    // act like Ctrl-C.
+    signal(SIGBREAK, [](int) { raise(SIGINT); });
+#endif
     signal(SIGINT, [](int) {
         fdevent_run_on_main_thread([]() { exit(0); });
     });
-#endif
 
     char* leak = getenv("ADB_LEAK");
     if (leak && strcmp(leak, "1") == 0) {
@@ -117,10 +116,21 @@
     atexit(adb_server_cleanup);
 
     init_transport_registration();
-    init_mdns_transport_discovery();
+    init_reconnect_handler();
 
-    usb_init();
-    local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
+    if (!getenv("ADB_MDNS") || strcmp(getenv("ADB_MDNS"), "0") != 0) {
+        init_mdns_transport_discovery();
+    }
+
+    if (!getenv("ADB_USB") || strcmp(getenv("ADB_USB"), "0") != 0) {
+        usb_init();
+    } else {
+        adb_notify_device_scan_complete();
+    }
+
+    if (!getenv("ADB_EMU") || strcmp(getenv("ADB_EMU"), "0") != 0) {
+        local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
+    }
 
     std::string error;
 
@@ -131,7 +141,7 @@
     while (install_listener(socket_spec, "*smartsocket*", nullptr, 0, nullptr, &error) !=
            INSTALL_STATUS_OK) {
         if (std::chrono::steady_clock::now() - start > 0.5s) {
-            fatal("could not install *smartsocket* listener: %s", error.c_str());
+            LOG(FATAL) << "could not install *smartsocket* listener: " << error;
         }
 
         std::this_thread::sleep_for(100ms);
@@ -147,7 +157,7 @@
         // setsid will fail with EPERM if it's already been a lead process of new session.
         // Ignore such error.
         if (setsid() == -1 && errno != EPERM) {
-            fatal("setsid() failed: %s", strerror(errno));
+            PLOG(FATAL) << "setsid() failed";
         }
 #endif
 
@@ -165,19 +175,19 @@
             const DWORD bytes_to_write = arraysize(ack) - 1;
             DWORD written = 0;
             if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
-                fatal("adb: cannot write ACK to handle 0x%p: %s", ack_reply_handle,
-                      android::base::SystemErrorCodeToString(GetLastError()).c_str());
+                LOG(FATAL) << "cannot write ACK to handle " << ack_reply_handle
+                           << android::base::SystemErrorCodeToString(GetLastError());
             }
             if (written != bytes_to_write) {
-                fatal("adb: cannot write %lu bytes of ACK: only wrote %lu bytes", bytes_to_write,
-                      written);
+                LOG(FATAL) << "cannot write " << bytes_to_write << " bytes of ACK: only wrote "
+                           << written << " bytes";
             }
             CloseHandle(ack_reply_handle);
 #else
             // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
             // "OKAY".
             if (!android::base::WriteStringToFd("OK\n", ack_reply_fd)) {
-                fatal_errno("error writing ACK to fd %d", ack_reply_fd);
+                PLOG(FATAL) << "error writing ACK to fd " << ack_reply_fd;
             }
             unix_close(ack_reply_fd);
 #endif
@@ -185,13 +195,29 @@
         notify_thread.detach();
     }
 
+#if defined(__linux__)
+    // Write our location to .android/adb.$PORT, so that older clients can exec us.
+    std::string path;
+    if (!android::base::Readlink("/proc/self/exe", &path)) {
+        PLOG(ERROR) << "failed to readlink /proc/self/exe";
+    }
+
+    std::optional<std::string> server_executable_path = adb_get_server_executable_path();
+    if (server_executable_path) {
+      if (!android::base::WriteStringToFile(path, *server_executable_path)) {
+          PLOG(ERROR) << "failed to write server path to " << path;
+      }
+    }
+#endif
+
     D("Event loop starting");
     fdevent_loop();
-
     return 0;
 }
 
-int main(int argc, char** argv) {
+int main(int argc, char* argv[], char* envp[]) {
+    __adb_argv = const_cast<const char**>(argv);
+    __adb_envp = const_cast<const char**>(envp);
     adb_trace_init(argv);
     return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
 }
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_dispatch.cpp b/adb/client/usb_dispatch.cpp
index ce57731..f55ae90 100644
--- a/adb/client/usb_dispatch.cpp
+++ b/adb/client/usb_dispatch.cpp
@@ -52,6 +52,11 @@
                                : native::usb_close(reinterpret_cast<native::usb_handle*>(h));
 }
 
+void usb_reset(usb_handle* h) {
+    should_use_libusb() ? libusb::usb_reset(reinterpret_cast<libusb::usb_handle*>(h))
+                        : native::usb_reset(reinterpret_cast<native::usb_handle*>(h));
+}
+
 void usb_kick(usb_handle* h) {
     should_use_libusb() ? libusb::usb_kick(reinterpret_cast<libusb::usb_handle*>(h))
                         : native::usb_kick(reinterpret_cast<native::usb_handle*>(h));
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 46c3f58..53f01a0 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -332,13 +332,6 @@
             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) {
@@ -589,7 +582,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) {
@@ -629,6 +622,11 @@
     return 0;
 }
 
+void usb_reset(usb_handle* h) {
+    libusb_reset_device(h->device_handle);
+    usb_kick(h);
+}
+
 void usb_kick(usb_handle* h) {
     h->Close();
 }
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index 1f376a4..81b8306 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -30,6 +30,7 @@
 #include <string.h>
 #include <sys/ioctl.h>
 #include <sys/time.h>
+#include <sys/sysmacros.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -38,6 +39,7 @@
 #include <list>
 #include <mutex>
 #include <string>
+#include <string_view>
 #include <thread>
 
 #include <android-base/file.h>
@@ -89,7 +91,7 @@
 static auto& g_usb_handles_mutex = *new std::mutex();
 static auto& g_usb_handles = *new std::list<usb_handle*>();
 
-static int is_known_device(const char* dev_name) {
+static int is_known_device(std::string_view dev_name) {
     std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
     for (usb_handle* usb : g_usb_handles) {
         if (usb->path == dev_name) {
@@ -128,7 +130,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;
@@ -151,11 +153,11 @@
             if (contains_non_digit(de->d_name)) continue;
 
             std::string dev_name = bus_name + "/" + de->d_name;
-            if (is_known_device(dev_name.c_str())) {
+            if (is_known_device(dev_name)) {
                 continue;
             }
 
-            int fd = unix_open(dev_name.c_str(), O_RDONLY | O_CLOEXEC);
+            int fd = unix_open(dev_name, O_RDONLY | O_CLOEXEC);
             if (fd == -1) {
                 continue;
             }
@@ -418,11 +420,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)
@@ -456,6 +458,11 @@
     return orig_len - len;
 }
 
+void usb_reset(usb_handle* h) {
+    ioctl(h->fd, USBDEVFS_RESET);
+    usb_kick(h);
+}
+
 void usb_kick(usb_handle* h) {
     std::lock_guard<std::mutex> lock(h->mutex);
     D("[ kicking %p (fd = %d) ]", h, h->fd);
@@ -534,10 +541,10 @@
     // Initialize mark so we don't get garbage collected after the device scan.
     usb->mark = true;
 
-    usb->fd = unix_open(usb->path.c_str(), O_RDWR | O_CLOEXEC);
+    usb->fd = unix_open(usb->path, O_RDWR | O_CLOEXEC);
     if (usb->fd == -1) {
         // Opening RW failed, so see if we have RO access.
-        usb->fd = unix_open(usb->path.c_str(), O_RDONLY | O_CLOEXEC);
+        usb->fd = unix_open(usb->path, O_RDONLY | O_CLOEXEC);
         if (usb->fd == -1) {
             D("[ usb open %s failed: %s]", usb->path.c_str(), strerror(errno));
             return;
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index 8a95a19..5c0da47 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -136,8 +136,8 @@
     io_service_t             usbDevice;
     io_service_t             usbInterface;
     IOCFPlugInInterface      **plugInInterface = NULL;
-    IOUSBInterfaceInterface220  **iface = NULL;
-    IOUSBDeviceInterface197  **dev = NULL;
+    IOUSBInterfaceInterface500  **iface = NULL;
+    IOUSBDeviceInterface500  **dev = NULL;
     HRESULT                  result;
     SInt32                   score;
     uint32_t                 locationId;
@@ -163,7 +163,7 @@
         //* This gets us the interface object
         result = (*plugInInterface)->QueryInterface(
             plugInInterface,
-            CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID*)&iface);
+            CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID500), (LPVOID*)&iface);
         //* We only needed the plugin to get the interface, so discard it
         (*plugInInterface)->Release(plugInInterface);
         if (result || !iface) {
@@ -209,7 +209,7 @@
         }
 
         result = (*plugInInterface)->QueryInterface(plugInInterface,
-            CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*)&dev);
+            CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID500), (LPVOID*)&dev);
         //* only needed this to query the plugin
         (*plugInInterface)->Release(plugInInterface);
         if (result || !dev) {
@@ -497,8 +497,8 @@
         }
     }
 
-    if (0 == result)
-        return 0;
+    if (!result)
+        return len;
 
     LOG(ERROR) << "usb_write failed with status: " << std::hex << result;
     return -1;
@@ -556,6 +556,11 @@
     return 0;
 }
 
+void usb_reset(usb_handle* handle) {
+    // Unimplemented on OS X.
+    usb_kick(handle);
+}
+
 static void usb_kick_locked(usb_handle *handle)
 {
     LOG(INFO) << "Kicking handle";
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index 9751ebf..f23c3a5 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -75,7 +75,7 @@
 static const GUID usb_class_id = ANDROID_USB_CLASS_ID;
 
 /// List of opened usb handles
-static std::vector<usb_handle*> 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();
@@ -126,11 +126,11 @@
 int usb_close(usb_handle* handle);
 
 int known_device_locked(const wchar_t* dev_name) {
-    if (NULL != dev_name) {
+    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 ((NULL != usb->interface_name) && (0 == wcsicmp(usb->interface_name, dev_name))) {
+            if ((nullptr != usb->interface_name) && (0 == wcsicmp(usb->interface_name, dev_name))) {
                 return 1;
             }
         }
@@ -142,7 +142,7 @@
 int known_device(const wchar_t* dev_name) {
     int ret = 0;
 
-    if (NULL != dev_name) {
+    if (nullptr != dev_name) {
         std::lock_guard<std::mutex> lock(usb_lock);
         ret = known_device_locked(dev_name);
     }
@@ -151,7 +151,7 @@
 }
 
 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);
 
@@ -209,11 +209,11 @@
 
     // Get the HINSTANCE corresponding to the module that _power_window_proc
     // is in (the main module).
-    const HINSTANCE instance = GetModuleHandleW(NULL);
+    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());
+        LOG(FATAL) << "GetModuleHandleW failed: "
+                   << android::base::SystemErrorCodeToString(GetLastError());
     }
 
     WNDCLASSEXW wndclass;
@@ -223,19 +223,19 @@
     wndclass.hInstance = instance;
     wndclass.lpszClassName = kPowerNotificationWindowClassName;
     if (!RegisterClassExW(&wndclass)) {
-        fatal("RegisterClassExW failed: %s",
-              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        LOG(FATAL) << "RegisterClassExW failed: "
+                   << android::base::SystemErrorCodeToString(GetLastError());
     }
 
     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());
+                         L"ADB Power Notification Window", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr,
+                         instance, nullptr)) {
+        LOG(FATAL) << "CreateWindowExW failed: "
+                   << android::base::SystemErrorCodeToString(GetLastError());
     }
 
     MSG msg;
-    while (GetMessageW(&msg, NULL, 0, 0)) {
+    while (GetMessageW(&msg, nullptr, 0, 0)) {
         TranslateMessage(&msg);
         DispatchMessageW(&msg);
     }
@@ -259,14 +259,14 @@
 
     // Allocate our handle
     usb_handle* ret = (usb_handle*)calloc(1, sizeof(usb_handle));
-    if (NULL == ret) {
+    if (nullptr == ret) {
         D("Could not allocate %u bytes for usb_handle: %s", sizeof(usb_handle), strerror(errno));
         goto fail;
     }
 
     // Create interface.
     ret->adb_interface = AdbCreateInterfaceByName(interface_name);
-    if (NULL == ret->adb_interface) {
+    if (nullptr == ret->adb_interface) {
         D("AdbCreateInterfaceByName failed: %s",
           android::base::SystemErrorCodeToString(GetLastError()).c_str());
         goto fail;
@@ -275,7 +275,7 @@
     // Open read pipe (endpoint)
     ret->adb_read_pipe = AdbOpenDefaultBulkReadEndpoint(
         ret->adb_interface, AdbOpenAccessTypeReadWrite, AdbOpenSharingModeReadWrite);
-    if (NULL == ret->adb_read_pipe) {
+    if (nullptr == ret->adb_read_pipe) {
         D("AdbOpenDefaultBulkReadEndpoint failed: %s",
           android::base::SystemErrorCodeToString(GetLastError()).c_str());
         goto fail;
@@ -284,7 +284,7 @@
     // Open write pipe (endpoint)
     ret->adb_write_pipe = AdbOpenDefaultBulkWriteEndpoint(
         ret->adb_interface, AdbOpenAccessTypeReadWrite, AdbOpenSharingModeReadWrite);
-    if (NULL == ret->adb_write_pipe) {
+    if (nullptr == ret->adb_write_pipe) {
         D("AdbOpenDefaultBulkWriteEndpoint failed: %s",
           android::base::SystemErrorCodeToString(GetLastError()).c_str());
         goto fail;
@@ -292,7 +292,7 @@
 
     // Save interface name
     // First get expected name length
-    AdbGetInterfaceName(ret->adb_interface, NULL, &name_len, false);
+    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());
@@ -300,7 +300,7 @@
     }
 
     ret->interface_name = (wchar_t*)malloc(name_len * sizeof(ret->interface_name[0]));
-    if (NULL == ret->interface_name) {
+    if (nullptr == ret->interface_name) {
         D("Could not allocate %lu characters for interface_name: %s", name_len, strerror(errno));
         goto fail;
     }
@@ -316,12 +316,12 @@
     return ret;
 
 fail:
-    if (NULL != 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) {
@@ -330,7 +330,7 @@
     int err = 0;
 
     D("usb_write %d", len);
-    if (NULL == handle) {
+    if (nullptr == handle) {
         D("usb_write was passed NULL handle");
         err = EINVAL;
         goto fail;
@@ -357,7 +357,8 @@
 
     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)) {
+        unsigned long dummy;
+        if (!AdbWriteEndpointSync(handle->adb_write_pipe, (void*)data, 0, &dummy, time_out)) {
             D("AdbWriteEndpointSync of zero length packet failed: %s",
               android::base::SystemErrorCodeToString(GetLastError()).c_str());
             err = EIO;
@@ -365,12 +366,12 @@
         }
     }
 
-    return 0;
+    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) {
+    if (nullptr != handle) {
         D("Kicking device due to error in usb_write");
         usb_kick(handle);
     }
@@ -387,7 +388,7 @@
     int orig_len = len;
 
     D("usb_read %d", len);
-    if (NULL == handle) {
+    if (nullptr == handle) {
         D("usb_read was passed NULL handle");
         err = EINVAL;
         goto fail;
@@ -411,7 +412,7 @@
 fail:
     // Any failure should cause us to kick the device instead of leaving it a
     // zombie state with potential to hang.
-    if (NULL != handle) {
+    if (nullptr != handle) {
         D("Kicking device due to error in usb_read");
         usb_kick(handle);
     }
@@ -431,22 +432,27 @@
 
 void usb_cleanup_handle(usb_handle* handle) {
     D("usb_cleanup_handle");
-    if (NULL != handle) {
-        if (NULL != handle->interface_name) free(handle->interface_name);
+    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 (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);
+        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;
     }
 }
 
+void usb_reset(usb_handle* handle) {
+    // Unimplemented on Windows.
+    usb_kick(handle);
+}
+
 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.
@@ -455,7 +461,7 @@
 
 void usb_kick(usb_handle* handle) {
     D("usb_kick");
-    if (NULL != handle) {
+    if (nullptr != handle) {
         std::lock_guard<std::mutex> lock(usb_lock);
         usb_kick_locked(handle);
     } else {
@@ -466,7 +472,7 @@
 int usb_close(usb_handle* handle) {
     D("usb_close");
 
-    if (NULL != handle) {
+    if (nullptr != handle) {
         // Remove handle from the list
         {
             std::lock_guard<std::mutex> lock(usb_lock);
@@ -487,7 +493,7 @@
 }
 
 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;
@@ -532,7 +538,7 @@
 }
 
 void find_devices() {
-    usb_handle* handle = NULL;
+    usb_handle* handle = nullptr;
     char entry_buffer[2048];
     AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
     unsigned long entry_buffer_size = sizeof(entry_buffer);
@@ -540,7 +546,7 @@
     // Enumerate all present and active interfaces.
     ADBAPIHANDLE enum_handle = AdbEnumInterfaces(usb_class_id, true, true, true);
 
-    if (NULL == enum_handle) {
+    if (nullptr == enum_handle) {
         D("AdbEnumInterfaces failed: %s",
           android::base::SystemErrorCodeToString(GetLastError()).c_str());
         return;
@@ -551,7 +557,7 @@
         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) {
+            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);
@@ -569,7 +575,7 @@
                                            true)) {
                         // Lets make sure that we don't duplicate this device
                         if (register_new_device(handle)) {
-                            register_usb_transport(handle, serial_number, NULL, 1);
+                            register_usb_transport(handle, serial_number, nullptr, 1);
                         } else {
                             D("register_new_device failed for %ls", next_interface->device_name);
                             usb_cleanup_handle(handle);
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
deleted file mode 100644
index 6981ff2..0000000
--- a/adb/commandline.cpp
+++ /dev/null
@@ -1,2113 +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"
-#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) {
-    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 = 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, 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) {
-    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++);
-    }
-
-    // No need for shell protocol with logcat, always disable for simplicity.
-    return send_shell_command(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;
-    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");
-        ++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(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");
-    }
-
-
-    /* "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, false);
-}
-
-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") ||
-            android::base::EndsWithIgnoreCase(file, ".dm")) {
-            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 %s -", install_cmd.c_str(),
-            static_cast<uint64_t>(sb.st_size), session_id, 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, false);
-}
-
-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, false);
-}
-
-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/commandline.h b/adb/commandline.h
deleted file mode 100644
index 36cd798..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(
-    const std::string& command, bool disable_shell_protocol,
-    StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
-
-#endif  // COMMANDLINE_H
diff --git a/adb/console.cpp b/adb/console.cpp
deleted file mode 100644
index 9563eac..0000000
--- a/adb/console.cpp
+++ /dev/null
@@ -1,154 +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 "sysdeps.h"
-
-#include <stdio.h>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-#include <cutils/sockets.h>
-
-#include "adb.h"
-#include "adb_client.h"
-#include "adb_io.h"
-#include "adb_utils.h"
-
-// Return the console authentication command for the emulator, if needed
-static std::string adb_construct_auth_command() {
-    static const char auth_token_filename[] = ".emulator_console_auth_token";
-
-    std::string auth_token_path = adb_get_homedir_path();
-    auth_token_path += OS_PATH_SEPARATOR;
-    auth_token_path += auth_token_filename;
-
-    // read the token
-    std::string token;
-    if (!android::base::ReadFileToString(auth_token_path, &token)
-        || token.empty()) {
-        // we either can't read the file, or it doesn't exist, or it's empty -
-        // either way we won't add any authentication command.
-        return {};
-    }
-
-    // now construct and return the actual command: "auth <token>\n"
-    std::string command = "auth ";
-    command += token;
-    command += '\n';
-    return command;
-}
-
-// Return the console port of the currently connected emulator (if any) or -1 if
-// there is no emulator, and -2 if there is more than one.
-static int adb_get_emulator_console_port(const char* serial) {
-    if (serial) {
-        // The user specified a serial number; is it an emulator?
-        int port;
-        return (sscanf(serial, "emulator-%d", &port) == 1) ? port : -1;
-    }
-
-    // No specific device was given, so get the list of connected devices and
-    // search for emulators. If there's one, we'll take it. If there are more
-    // than one, that's an error.
-    std::string devices;
-    std::string error;
-    if (!adb_query("host:devices", &devices, &error)) {
-        fprintf(stderr, "error: no emulator connected: %s\n", error.c_str());
-        return -1;
-    }
-
-    int port;
-    size_t emulator_count = 0;
-    for (const auto& device : android::base::Split(devices, "\n")) {
-        if (sscanf(device.c_str(), "emulator-%d", &port) == 1) {
-            if (++emulator_count > 1) {
-                fprintf(
-                    stderr, "error: more than one emulator detected; use -s\n");
-                return -1;
-            }
-        }
-    }
-
-    if (emulator_count == 0) {
-        fprintf(stderr, "error: no emulator detected\n");
-        return -1;
-    }
-
-    return port;
-}
-
-static int connect_to_console(const char* serial) {
-    int port = adb_get_emulator_console_port(serial);
-    if (port == -1) {
-        return -1;
-    }
-
-    std::string error;
-    int fd = network_loopback_client(port, SOCK_STREAM, &error);
-    if (fd == -1) {
-        fprintf(stderr, "error: could not connect to TCP port %d: %s\n", port,
-                error.c_str());
-        return -1;
-    }
-    return fd;
-}
-
-int adb_send_emulator_command(int argc, const char** argv, const char* serial) {
-    int fd = connect_to_console(serial);
-    if (fd == -1) {
-        return 1;
-    }
-
-    std::string commands = adb_construct_auth_command();
-
-    for (int i = 1; i < argc; i++) {
-        commands.append(argv[i]);
-        commands.push_back(i == argc - 1 ? '\n' : ' ');
-    }
-
-    commands.append("quit\n");
-
-    if (!WriteFdExactly(fd, commands)) {
-        fprintf(stderr, "error: cannot write to emulator: %s\n",
-                strerror(errno));
-        adb_close(fd);
-        return 1;
-    }
-
-    // Drain output that the emulator console has sent us to prevent a problem
-    // on Windows where if adb closes the socket without reading all the data,
-    // the emulator's next call to recv() will have an ECONNABORTED error,
-    // preventing the emulator from reading the command that adb has sent.
-    // https://code.google.com/p/android/issues/detail?id=21021
-    int result;
-    do {
-        char buf[BUFSIZ];
-        result = adb_read(fd, buf, sizeof(buf));
-        // Keep reading until zero bytes (orderly/graceful shutdown) or an
-        // error. If 'adb emu kill' is executed, the emulator calls exit() with
-        // the socket open (and shutdown(SD_SEND) was not called), which causes
-        // Windows to send a TCP RST segment which causes adb to get ECONNRESET.
-        // Any other emu command is followed by the quit command that we
-        // appended above, and that causes the emulator to close the socket
-        // which should cause zero bytes (orderly/graceful shutdown) to be
-        // returned.
-    } while (result > 0);
-
-    adb_close(fd);
-
-    return 0;
-}
diff --git a/adb/daemon/abb.cpp b/adb/daemon/abb.cpp
new file mode 100644
index 0000000..425438e
--- /dev/null
+++ b/adb/daemon/abb.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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 <sys/wait.h>
+
+#include <android-base/cmsg.h>
+#include <android-base/strings.h>
+#include <cmd.h>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_utils.h"
+#include "shell_service.h"
+
+namespace {
+
+class AdbFdTextOutput : public android::TextOutput {
+  public:
+    explicit AdbFdTextOutput(borrowed_fd fd) : fd_(fd) {}
+
+  private:
+    android::status_t print(const char* txt, size_t len) override {
+        return WriteFdExactly(fd_, txt, len) ? android::OK : -errno;
+    }
+    void moveIndent(int delta) override { /*not implemented*/
+    }
+
+    void pushBundle() override { /*not implemented*/
+    }
+    void popBundle() override { /*not implemented*/
+    }
+
+  private:
+    borrowed_fd fd_;
+};
+
+std::vector<std::string_view> parseCmdArgs(std::string_view args) {
+    std::vector<std::string_view> argv;
+
+    char delim = ABB_ARG_DELIMETER;
+    size_t size = args.size();
+    size_t base = 0;
+    while (base < size) {
+        size_t found;
+        for (found = base; found < size && args[found] && args[found] != delim; ++found)
+            ;
+        if (found > base) {
+            argv.emplace_back(args.substr(base, found - base));
+        }
+        base = found + 1;
+    }
+
+    return argv;
+}
+
+}  // namespace
+
+static int execCmd(std::string_view args, borrowed_fd in, borrowed_fd out, borrowed_fd err) {
+    AdbFdTextOutput oin(out);
+    AdbFdTextOutput oerr(err);
+    return cmdMain(parseCmdArgs(args), oin, oerr, in.get(), out.get(), err.get(),
+                   RunMode::kLibrary);
+}
+
+int main(int argc, char* const argv[]) {
+    signal(SIGPIPE, SIG_IGN);
+
+    int fd = STDIN_FILENO;
+    std::string data;
+    while (true) {
+        std::string error;
+        if (!ReadProtocolString(fd, &data, &error)) {
+            PLOG(ERROR) << "Failed to read message: " << error;
+            break;
+        }
+
+        std::string_view name = data;
+        auto protocol = SubprocessProtocol::kShell;
+        if (android::base::ConsumePrefix(&name, "abb:")) {
+            protocol = SubprocessProtocol::kShell;
+        } else if (android::base::ConsumePrefix(&name, "abb_exec:")) {
+            protocol = SubprocessProtocol::kNone;
+        } else {
+            LOG(FATAL) << "Unknown command prefix for abb: " << data;
+        }
+
+        unique_fd result = StartCommandInProcess(std::string(name), &execCmd, protocol);
+        if (android::base::SendFileDescriptors(fd, "", 1, result.get()) != 1) {
+            PLOG(ERROR) << "Failed to send an inprocess fd for command: " << data;
+            break;
+        }
+    }
+}
diff --git a/adb/daemon/abb_service.cpp b/adb/daemon/abb_service.cpp
new file mode 100644
index 0000000..a435279
--- /dev/null
+++ b/adb/daemon/abb_service.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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.h"
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "shell_service.h"
+
+#include <android-base/cmsg.h>
+
+namespace {
+
+struct AbbProcess;
+static auto& abbp = *new std::unique_ptr<AbbProcess>(std::make_unique<AbbProcess>());
+
+struct AbbProcess {
+    unique_fd sendCommand(std::string_view command);
+
+  private:
+    static unique_fd startAbbProcess(unique_fd* error_fd);
+
+    static constexpr auto kRetries = 2;
+    static constexpr auto kErrorProtocol = SubprocessProtocol::kShell;
+
+    std::mutex locker_;
+    unique_fd socket_fd_;
+};
+
+unique_fd AbbProcess::sendCommand(std::string_view command) {
+    std::unique_lock lock{locker_};
+
+    for (int i = 0; i < kRetries; ++i) {
+        unique_fd error_fd;
+        if (socket_fd_ == -1) {
+            socket_fd_ = startAbbProcess(&error_fd);
+        }
+        if (socket_fd_ == -1) {
+            LOG(ERROR) << "failed to start abb process";
+            return error_fd;
+        }
+
+        if (!SendProtocolString(socket_fd_, std::string(command))) {
+            PLOG(ERROR) << "failed to send command to abb";
+            socket_fd_.reset();
+            continue;
+        }
+
+        unique_fd fd;
+        std::string error;
+        char buf;
+        if (android::base::ReceiveFileDescriptors(socket_fd_, &buf, 1, &fd) != 1) {
+            PLOG(ERROR) << "failed to receive FD from abb";
+            socket_fd_.reset();
+            continue;
+        }
+
+        return fd;
+    }
+
+    LOG(ERROR) << "abb is unavailable";
+    socket_fd_.reset();
+    return ReportError(kErrorProtocol, "abb is unavailable");
+}
+
+unique_fd AbbProcess::startAbbProcess(unique_fd* error_fd) {
+    constexpr auto abb_process_type = SubprocessType::kRaw;
+    constexpr auto abb_protocol = SubprocessProtocol::kNone;
+    constexpr auto make_pty_raw = false;
+    return StartSubprocess("abb", "dumb", abb_process_type, abb_protocol, make_pty_raw,
+                           kErrorProtocol, error_fd);
+}
+
+}  // namespace
+
+unique_fd execute_abb_command(std::string_view command) {
+    return abbp->sendCommand(command);
+}
diff --git a/adb/daemon/auth.cpp b/adb/daemon/auth.cpp
new file mode 100644
index 0000000..1800f84
--- /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..0e70d47
--- /dev/null
+++ b/adb/daemon/file_sync_service.cpp
@@ -0,0 +1,575 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <utime.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#if defined(__ANDROID__)
+#include <selinux/android.h>
+#include <sys/xattr.h>
+#endif
+
+#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::Dirname;
+using android::base::StringPrintf;
+
+static bool should_use_fs_config(const std::string& path) {
+#if defined(__ANDROID__)
+    // TODO: use fs_config to configure permissions on /data too.
+    return !android::base::StartsWith(path, "/data/");
+#else
+    UNUSED(path);
+    return false;
+#endif
+}
+
+static bool update_capabilities(const char* path, uint64_t capabilities) {
+#if defined(__ANDROID__)
+    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;
+#else
+    UNUSED(path, capabilities);
+    return true;
+#endif
+}
+
+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;
+
+#if defined(__ANDROID__)
+            // Not all filesystems support setting SELinux labels. http://b/23530370.
+            selinux_android_restorecon(partial_path.c_str(), 0);
+#endif
+
+            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, uint32_t* timestamp, uid_t uid, gid_t gid,
+                             uint64_t capabilities, mode_t mode, std::vector<char>& buffer,
+                             bool do_unlink) {
+    int rc;
+    syncmsg msg;
+
+    __android_log_security_bswrite(SEC_TAG_ADB_SEND_FILE, path);
+
+    unique_fd fd(adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode));
+
+    if (fd < 0 && errno == ENOENT) {
+        if (!secure_mkdirs(Dirname(path))) {
+            SendSyncFailErrno(s, "secure_mkdirs failed");
+            goto fail;
+        }
+        fd.reset(adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode));
+    }
+    if (fd < 0 && errno == EEXIST) {
+        fd.reset(adb_open_mode(path, O_WRONLY | O_CLOEXEC, mode));
+    }
+    if (fd < 0) {
+        SendSyncFailErrno(s, "couldn't create file");
+        goto fail;
+    } else {
+        if (fchown(fd.get(), uid, gid) == -1) {
+            SendSyncFailErrno(s, "fchown failed");
+            goto fail;
+        }
+
+#if defined(__ANDROID__)
+        // Not all filesystems support setting SELinux labels. http://b/23530370.
+        selinux_android_restorecon(path, 0);
+#endif
+
+        // 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.get(), mode);
+    }
+
+    rc = posix_fadvise(fd.get(), 0, 0,
+                       POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED);
+    if (rc != 0) {
+        D("[ Failed to fadvise: %s ]", strerror(rc));
+    }
+
+    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.get(), &buffer[0], msg.data.size)) {
+            SendSyncFailErrno(s, "write failed");
+            goto fail;
+        }
+    }
+
+    if (!update_capabilities(path, capabilities)) {
+        SendSyncFailErrno(s, "update_capabilities failed");
+        goto fail;
+    }
+
+    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 (do_unlink) adb_unlink(path);
+    return false;
+}
+
+#if defined(_WIN32)
+extern bool handle_send_link(int s, const std::string& path,
+                             uint32_t* timestamp, std::vector<char>& buffer)
+        __attribute__((error("no symlinks on Windows")));
+#else
+static bool handle_send_link(int s, const std::string& path, uint32_t* timestamp,
+                             std::vector<char>& buffer) {
+    syncmsg msg;
+
+    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;
+    }
+
+    unsigned int 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;
+
+    std::string buf_link;
+    if (!android::base::Readlink(path, &buf_link) || (buf_link != &buffer[0])) {
+        adb_unlink(path.c_str());
+        auto ret = symlink(&buffer[0], path.c_str());
+        if (ret && errno == ENOENT) {
+            if (!secure_mkdirs(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) {
+        *timestamp = msg.data.size;
+        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) && !S_ISLNK(mode));
+    if (do_unlink) {
+        adb_unlink(path.c_str());
+    }
+
+    bool result;
+    uint32_t timestamp;
+    if (S_ISLNK(mode)) {
+        result = handle_send_link(s, path, &timestamp, buffer);
+    } else {
+        // 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;
+        }
+
+        result = handle_send_file(s, path.c_str(), &timestamp, uid, gid, capabilities, mode, buffer,
+                                  do_unlink);
+    }
+
+    if (!result) {
+      return false;
+    }
+
+    struct timeval tv[2];
+    tv[0].tv_sec = timestamp;
+    tv[0].tv_usec = 0;
+    tv[1].tv_sec = timestamp;
+    tv[1].tv_usec = 0;
+    lutimes(path.c_str(), tv);
+    return true;
+}
+
+static bool do_recv(int s, const char* path, std::vector<char>& buffer) {
+    __android_log_security_bswrite(SEC_TAG_ADB_RECV_FILE, path);
+
+    unique_fd fd(adb_open(path, O_RDONLY | O_CLOEXEC));
+    if (fd < 0) {
+        SendSyncFailErrno(s, "open failed");
+        return false;
+    }
+
+    int rc = posix_fadvise(fd.get(), 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
+    if (rc != 0) {
+        D("[ Failed to fadvise: %s ]", strerror(rc));
+    }
+
+    syncmsg msg;
+    msg.data.id = ID_DATA;
+    while (true) {
+        int r = adb_read(fd.get(), &buffer[0], buffer.size() - sizeof(msg.data));
+        if (r <= 0) {
+            if (r == 0) break;
+            SendSyncFailErrno(s, "read failed");
+            return false;
+        }
+        msg.data.size = r;
+        if (!WriteFdExactly(s, &msg.data, sizeof(msg.data)) || !WriteFdExactly(s, &buffer[0], r)) {
+            return false;
+        }
+    }
+
+    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..2a6418a
--- /dev/null
+++ b/adb/daemon/framebuffer_service.cpp
@@ -0,0 +1,188 @@
+/*
+ * 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 "adb_utils.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);
+        perror_exit("exec screencap failed");
+    }
+
+    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..bab44be
--- /dev/null
+++ b/adb/daemon/framebuffer_service.h
@@ -0,0 +1,23 @@
+/*
+ * 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"
+
+#if defined(__ANDROID__)
+void framebuffer_service(unique_fd fd);
+#endif
diff --git a/adb/daemon/include/adbd/usb.h b/adb/daemon/include/adbd/usb.h
new file mode 100644
index 0000000..2204246
--- /dev/null
+++ b/adb/daemon/include/adbd/usb.h
@@ -0,0 +1,68 @@
+#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 <linux/usb/functionfs.h>
+
+#include <atomic>
+#include <condition_variable>
+#include <mutex>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#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, bool allow_partial);
+    void (*kick)(usb_handle* h);
+    void (*close)(usb_handle* h);
+
+    // FunctionFS
+    android::base::unique_fd control;
+    android::base::unique_fd bulk_out;  // "out" from the host's perspective => source for adbd
+    android::base::unique_fd bulk_in;   // "in" from the host's perspective => sink for adbd
+
+    // Access to these blocks is very not thread safe. Have one block for each of 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);
+bool open_functionfs(android::base::unique_fd* control, android::base::unique_fd* bulk_out,
+                     android::base::unique_fd* bulk_in);
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
new file mode 100644
index 0000000..b92a7de
--- /dev/null
+++ b/adb/daemon/jdwp_service.cpp
@@ -0,0 +1,438 @@
+/*
+ * 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 <thread>
+#include <vector>
+
+#include <adbconnection/server.h>
+#include <android-base/cmsg.h>
+#include <android-base/unique_fd.h>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+
+/* 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 {
+    JdwpProcess(unique_fd socket, pid_t pid) {
+        CHECK(pid != 0);
+
+        this->socket = socket;
+        this->pid = pid;
+        this->fde = fdevent_create(socket.release(), jdwp_process_event, this);
+
+        if (!this->fde) {
+            LOG(FATAL) << "could not create fdevent for new JDWP process";
+        }
+    }
+
+    ~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() {
+        auto pred = [this](const auto& proc) { return proc.get() == this; };
+        _jdwp_list.remove_if(pred);
+    }
+
+    borrowed_fd socket = -1;
+    int32_t pid = -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) {
+        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) {
+        LOG(FATAL) << "invalid JDWP process list buffer size: " << 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);
+    CHECK_EQ(socket, proc->socket.get());
+
+    if (events & FDE_READ) {
+        // We already have the PID, if we can read from the socket, we've probably hit EOF.
+        D("terminating JDWP connection %d", proc->pid);
+        goto CloseProcess;
+    }
+
+    if (events & FDE_WRITE) {
+        D("trying to send fd to JDWP process (count = %zu)", proc->out_fds.size());
+        CHECK(!proc->out_fds.empty());
+
+        int fd = proc->out_fds.back().get();
+        if (android::base::SendFileDescriptors(socket, "", 1, fd) != 1) {
+            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 (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{};
+}
+
+/** "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) {
+        LOG(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) {
+        LOG(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) {
+    std::thread([]() {
+        adb_thread_setname("jdwp control");
+        adbconnection_listen([](int fd, pid_t pid) {
+            LOG(INFO) << "jdwp connection from " << pid;
+            fdevent_run_on_main_thread([fd, pid] {
+                unique_fd ufd(fd);
+                auto proc = std::make_unique<JdwpProcess>(std::move(ufd), pid);
+                if (!proc) {
+                    LOG(FATAL) << "failed to allocate JdwpProcess";
+                }
+                _jdwp_list.emplace_back(std::move(proc));
+                jdwp_process_list_updated();
+            });
+        });
+    }).detach();
+    return 0;
+}
+
+#endif /* !ADB_HOST */
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 5adeb44..e5a4917 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -18,8 +18,13 @@
 
 #include "sysdeps.h"
 
+#if defined(__BIONIC__)
+#include <android/fdsan.h>
+#endif
+
 #include <errno.h>
 #include <getopt.h>
+#include <malloc.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -32,13 +37,15 @@
 #include <android-base/macros.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
+
+#if defined(__ANDROID__)
 #include <libminijail.h>
 #include <log/log_properties.h>
 #include <scoped_minijail.h>
 
 #include <private/android_filesystem_config.h>
-#include "debuggerd/handler.h"
 #include "selinux/android.h"
+#endif
 
 #include "adb.h"
 #include "adb_auth.h"
@@ -48,19 +55,26 @@
 
 #include "mdns.h"
 
+#if defined(__ANDROID__)
 static const char* root_seclabel = nullptr;
 
+static inline bool is_device_unlocked() {
+    return "orange" == android::base::GetProperty("ro.boot.verifiedbootstate", "");
+}
+
 static bool should_drop_capabilities_bounding_set() {
-#if defined(ALLOW_ADBD_ROOT)
-    if (__android_log_is_debuggable()) {
-        return false;
+    if (ALLOW_ADBD_ROOT || is_device_unlocked()) {
+        if (__android_log_is_debuggable()) {
+            return false;
+        }
     }
-#endif
     return true;
 }
 
 static bool should_drop_privileges() {
-#if defined(ALLOW_ADBD_ROOT)
+    // "adb root" not allowed, always drop privileges.
+    if (!ALLOW_ADBD_ROOT && !is_device_unlocked()) return true;
+
     // The properties that affect `adb root` and `adb unroot` are ro.secure and
     // ro.debuggable. In this context the names don't make the expected behavior
     // particularly obvious.
@@ -90,9 +104,6 @@
     }
 
     return drop;
-#else
-    return true; // "adb root" not allowed, always drop privileges.
-#endif // ALLOW_ADBD_ROOT
 }
 
 static void drop_privileges(int server_port) {
@@ -166,10 +177,14 @@
         }
     }
 }
+#endif
 
 static void setup_port(int port) {
+    LOG(INFO) << "adbd listening on port " << port;
     local_init(port);
+#if defined(__ANDROID__)
     setup_mdns(port);
+#endif
 }
 
 int adbd_main(int server_port) {
@@ -177,15 +192,27 @@
 
     signal(SIGPIPE, SIG_IGN);
 
+#if defined(__BIONIC__)
+    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);
+    }
+#endif
+
     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);
+#elif defined(__ANDROID__)
+    if (is_device_unlocked()) {  // allows no authentication when the device is unlocked.
+        auth_required = android::base::GetBoolProperty("ro.adb.secure", false);
     }
+#endif
 
     adbd_auth_init();
 
@@ -199,14 +226,19 @@
           " unchanged.\n");
     }
 
+#if defined(__ANDROID__)
     drop_privileges(server_port);
+#endif
 
     bool is_usb = false;
+
+#if defined(__ANDROID__)
     if (access(USB_FFS_ADB_EP0, F_OK) == 0) {
         // Listen on USB.
         usb_init();
         is_usb = true;
     }
+#endif
 
     // If one of these properties is set, also listen on that port.
     // If one of the properties isn't set and we couldn't listen on usb, listen
@@ -237,6 +269,11 @@
 }
 
 int main(int argc, char** argv) {
+#if defined(__BIONIC__)
+    // Set M_DECAY_TIME so that our allocations aren't immediately purged on free.
+    mallopt(M_DECAY_TIME, 1);
+#endif
+
     while (true) {
         static struct option opts[] = {
             {"root_seclabel", required_argument, nullptr, 's'},
@@ -251,25 +288,26 @@
         }
 
         switch (c) {
-        case 's':
-            root_seclabel = optarg;
-            break;
-        case 'b':
-            adb_device_banner = optarg;
-            break;
-        case 'v':
-            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.
-            return 1;
+#if defined(__ANDROID__)
+            case 's':
+                root_seclabel = optarg;
+                break;
+#endif
+            case 'b':
+                adb_device_banner = optarg;
+                break;
+            case 'v':
+                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.
+                return 1;
         }
     }
 
     close_stdin();
 
-    debuggerd_init(nullptr);
     adb_trace_init(argv);
 
     D("Handling main()");
diff --git a/adb/daemon/mdns.cpp b/adb/daemon/mdns.cpp
index 849378f..3530f48 100644
--- a/adb/daemon/mdns.cpp
+++ b/adb/daemon/mdns.cpp
@@ -74,7 +74,7 @@
 
     if (error != kDNSServiceErr_NoError) {
         LOG(ERROR) << "Could not register mDNS service (" << error << ").";
-        mdns_registered = false;
+        return;
     }
 
     mdns_registered = true;
diff --git a/adb/daemon/reboot_service.cpp b/adb/daemon/reboot_service.cpp
new file mode 100644
index 0000000..13398af
--- /dev/null
+++ b/adb/daemon/reboot_service.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.
+ */
+
+#define TRACE_TAG SERVICES
+
+#include "sysdeps.h"
+
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <bootloader_message/bootloader_message.h>
+#include <cutils/android_reboot.h>
+
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+
+void reboot_service(unique_fd fd, const std::string& arg) {
+    std::string reboot_arg = arg;
+    sync();
+
+    if (reboot_arg.empty()) reboot_arg = "adb";
+    std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
+
+    if (reboot_arg == "fastboot" &&
+        android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) &&
+        access("/dev/socket/recovery", F_OK) == 0) {
+        LOG(INFO) << "Recovery specific reboot fastboot";
+        /*
+         * The socket is created to allow switching between recovery and
+         * fastboot.
+         */
+        android::base::unique_fd sock(socket(AF_UNIX, SOCK_STREAM, 0));
+        if (sock < 0) {
+            WriteFdFmt(fd, "reboot (%s) create\n", strerror(errno));
+            PLOG(ERROR) << "Creating recovery socket failed";
+            return;
+        }
+
+        sockaddr_un addr = {.sun_family = AF_UNIX};
+        strncpy(addr.sun_path, "/dev/socket/recovery", sizeof(addr.sun_path) - 1);
+        if (connect(sock.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
+            WriteFdFmt(fd, "reboot (%s) connect\n", strerror(errno));
+            PLOG(ERROR) << "Couldn't connect to recovery socket";
+            return;
+        }
+        const char msg_switch_to_fastboot = 'f';
+        auto ret = adb_write(sock, &msg_switch_to_fastboot, sizeof(msg_switch_to_fastboot));
+        if (ret != sizeof(msg_switch_to_fastboot)) {
+            WriteFdFmt(fd, "reboot (%s) write\n", strerror(errno));
+            PLOG(ERROR) << "Couldn't write message to recovery socket to switch to fastboot";
+            return;
+        }
+    } else {
+        if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
+            WriteFdFmt(fd.get(), "reboot (%s) failed\n", reboot_string.c_str());
+            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();
+    }
+}
diff --git a/adb/daemon/reboot_service.h b/adb/daemon/reboot_service.h
new file mode 100644
index 0000000..f68913e
--- /dev/null
+++ b/adb/daemon/reboot_service.h
@@ -0,0 +1,25 @@
+/*
+ * 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 "adb_unique_fd.h"
+
+#if defined(__ANDROID__)
+void reboot_service(unique_fd fd, const std::string& arg);
+#endif
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
new file mode 100644
index 0000000..ce494ee
--- /dev/null
+++ b/adb/daemon/remount_service.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+
+static constexpr char kRemountCmd[] = "/system/bin/remount";
+
+static bool do_remount(int fd, const std::string& cmd) {
+    if (getuid() != 0) {
+        WriteFdExactly(fd, "Not running as root. Try \"adb root\" first.\n");
+        return false;
+    }
+
+    auto pid = fork();
+    if (pid < 0) {
+        WriteFdFmt(fd, "Failed to fork to %s: %s\n", kRemountCmd, strerror(errno));
+        return false;
+    }
+
+    if (pid == 0) {
+        // child side of the fork
+        dup2(fd, STDIN_FILENO);
+        dup2(fd, STDOUT_FILENO);
+        dup2(fd, STDERR_FILENO);
+
+        execl(kRemountCmd, kRemountCmd, cmd.empty() ? nullptr : cmd.c_str(), nullptr);
+        _exit(errno);
+    }
+
+    int wstatus = 0;
+    auto ret = waitpid(pid, &wstatus, 0);
+
+    if (ret == -1) {
+        WriteFdFmt(fd, "Failed to wait for %s: %s\n", kRemountCmd, strerror(errno));
+        return false;
+    } else if (ret != pid) {
+        WriteFdFmt(fd, "pid %d and waitpid return %d do not match for %s\n",
+                   static_cast<int>(pid), static_cast<int>(ret), kRemountCmd);
+        return false;
+    }
+
+    if (WIFSIGNALED(wstatus)) {
+        WriteFdFmt(fd, "%s terminated with signal %s\n", kRemountCmd,
+                   strsignal(WTERMSIG(wstatus)));
+        return false;
+    }
+
+    if (!WIFEXITED(wstatus)) {
+        WriteFdFmt(fd, "%s stopped with status 0x%x\n", kRemountCmd, wstatus);
+        return false;
+    }
+
+    if (WEXITSTATUS(wstatus)) {
+        WriteFdFmt(fd, "%s exited with status %d\n", kRemountCmd, WEXITSTATUS(wstatus));
+        return false;
+    }
+
+    return true;
+}
+
+void remount_service(unique_fd fd, const std::string& cmd) {
+    const char* success = do_remount(fd.get(), cmd) ? "succeeded" : "failed";
+    WriteFdFmt(fd.get(), "remount %s\n", success);
+}
diff --git a/adb/daemon/remount_service.h b/adb/daemon/remount_service.h
new file mode 100644
index 0000000..522a5da
--- /dev/null
+++ b/adb/daemon/remount_service.h
@@ -0,0 +1,25 @@
+/*
+ * 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"
+
+#if defined(__ANDROID__)
+void remount_service(unique_fd, const std::string&);
+#endif
diff --git a/adb/daemon/restart_service.cpp b/adb/daemon/restart_service.cpp
new file mode 100644
index 0000000..16d2627
--- /dev/null
+++ b/adb/daemon/restart_service.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <log/log_properties.h>
+
+#include "adb_io.h"
+#include "adb_unique_fd.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;
+    }
+
+    LOG(INFO) << "adbd restarting as root";
+    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;
+    }
+
+    LOG(INFO) << "adbd restarting as nonroot";
+    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;
+    }
+
+    LOG(INFO) << "adbd restarting in TCP mode (port = " << port << ")";
+    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) {
+    LOG(INFO) << "adbd restarting in USB mode";
+    android::base::SetProperty("service.adb.tcp.port", "0");
+    WriteFdExactly(fd.get(), "restarting in USB mode\n");
+}
diff --git a/adb/daemon/restart_service.h b/adb/daemon/restart_service.h
new file mode 100644
index 0000000..19840bd
--- /dev/null
+++ b/adb/daemon/restart_service.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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"
+
+#if defined(__ANDROID__)
+void restart_root_service(unique_fd fd);
+void restart_unroot_service(unique_fd fd);
+void restart_tcp_service(unique_fd fd, int port);
+void restart_usb_service(unique_fd fd);
+#endif
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
new file mode 100644
index 0000000..e6f4499
--- /dev/null
+++ b/adb/daemon/services.cpp
@@ -0,0 +1,317 @@
+/*
+ * 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 <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/parseint.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 <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/reboot_service.h"
+#include "daemon/remount_service.h"
+#include "daemon/restart_service.h"
+#include "daemon/set_verity_enable_state_service.h"
+#include "daemon/shell_service.h"
+
+
+void reconnect_service(unique_fd fd, atransport* t) {
+    WriteFdExactly(fd.get(), "done");
+    kick_transport(t);
+}
+
+unique_fd reverse_service(std::string_view command, atransport* transport) {
+    // TODO: Switch handle_forward_request to std::string_view.
+    std::string str(command);
+
+    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(str.c_str(), 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(std::string_view 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{};
+    }
+
+    // TODO: android::base::Split(const std::string_view&, ...)
+    std::string service_args(args.substr(0, delimiter_index));
+    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 (arg.starts_with("TERM=")) {
+            terminal_type = arg.substr(strlen("TERM="));
+        } 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, terminal_type.c_str(), type, protocol);
+}
+
+static void spin_service(unique_fd fd) {
+    if (!__android_log_is_debuggable()) {
+        WriteFdExactly(fd.get(), "refusing to spin on non-debuggable build\n");
+        return;
+    }
+
+    // A service that creates an fdevent that's always pending, and then ignores it.
+    unique_fd pipe_read, pipe_write;
+    if (!Pipe(&pipe_read, &pipe_write)) {
+        WriteFdExactly(fd.get(), "failed to create pipe\n");
+        return;
+    }
+
+    fdevent_run_on_main_thread([fd = pipe_read.release()]() {
+        fdevent* fde = fdevent_create(
+                fd, [](int, unsigned, void*) {}, nullptr);
+        fdevent_add(fde, FDE_READ);
+    });
+
+    WriteFdExactly(fd.get(), "spinning\n");
+}
+
+struct ServiceSocket : public asocket {
+    ServiceSocket() {
+        install_local_socket(this);
+        this->enqueue = [](asocket* self, apacket::payload_type data) {
+            return static_cast<ServiceSocket*>(self)->Enqueue(std::move(data));
+        };
+        this->ready = [](asocket* self) { return static_cast<ServiceSocket*>(self)->Ready(); };
+        this->close = [](asocket* self) { return static_cast<ServiceSocket*>(self)->Close(); };
+    }
+    virtual ~ServiceSocket() = default;
+
+    virtual int Enqueue(apacket::payload_type data) { return -1; }
+    virtual void Ready() {}
+    virtual void Close() {
+        if (peer) {
+            peer->peer = nullptr;
+            if (peer->shutdown) {
+                peer->shutdown(peer);
+            }
+            peer->close(peer);
+        }
+
+        remove_socket(this);
+        delete this;
+    }
+};
+
+struct SinkSocket : public ServiceSocket {
+    explicit SinkSocket(size_t byte_count) {
+        LOG(INFO) << "Creating new SinkSocket with capacity " << byte_count;
+        bytes_left_ = byte_count;
+    }
+
+    virtual ~SinkSocket() { LOG(INFO) << "SinkSocket destroyed"; }
+
+    virtual int Enqueue(apacket::payload_type data) override final {
+        if (bytes_left_ <= data.size()) {
+            // Done reading.
+            Close();
+            return -1;
+        }
+
+        bytes_left_ -= data.size();
+        return 0;
+    }
+
+    size_t bytes_left_;
+};
+
+struct SourceSocket : public ServiceSocket {
+    explicit SourceSocket(size_t byte_count) {
+        LOG(INFO) << "Creating new SourceSocket with capacity " << byte_count;
+        bytes_left_ = byte_count;
+    }
+
+    virtual ~SourceSocket() { LOG(INFO) << "SourceSocket destroyed"; }
+
+    void Ready() {
+        size_t len = std::min(bytes_left_, get_max_payload());
+        if (len == 0) {
+            Close();
+            return;
+        }
+
+        Block block(len);
+        memset(block.data(), 0, block.size());
+        peer->enqueue(peer, std::move(block));
+        bytes_left_ -= len;
+    }
+
+    int Enqueue(apacket::payload_type data) { return -1; }
+
+    size_t bytes_left_;
+};
+
+asocket* daemon_service_to_socket(std::string_view name) {
+    if (name == "jdwp") {
+        return create_jdwp_service_socket();
+    } else if (name == "track-jdwp") {
+        return create_jdwp_tracker_service_socket();
+    } else if (android::base::ConsumePrefix(&name, "sink:")) {
+        uint64_t byte_count = 0;
+        if (!ParseUint(&byte_count, name)) {
+            return nullptr;
+        }
+        return new SinkSocket(byte_count);
+    } else if (android::base::ConsumePrefix(&name, "source:")) {
+        uint64_t byte_count = 0;
+        if (!ParseUint(&byte_count, name)) {
+            return nullptr;
+        }
+        return new SourceSocket(byte_count);
+    }
+
+    return nullptr;
+}
+
+unique_fd daemon_service_to_fd(std::string_view name, atransport* transport) {
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
+    if (name.starts_with("abb:") || name.starts_with("abb_exec:")) {
+        return execute_abb_command(name);
+    }
+#endif
+
+#if defined(__ANDROID__)
+    if (name.starts_with("framebuffer:")) {
+        return create_service_thread("fb", framebuffer_service);
+    } else if (android::base::ConsumePrefix(&name, "remount:")) {
+        std::string arg(name);
+        return create_service_thread("remount",
+                                     std::bind(remount_service, std::placeholders::_1, arg));
+    } else if (android::base::ConsumePrefix(&name, "reboot:")) {
+        std::string arg(name);
+        return create_service_thread("reboot",
+                                     std::bind(reboot_service, std::placeholders::_1, arg));
+    } else if (name.starts_with("root:")) {
+        return create_service_thread("root", restart_root_service);
+    } else if (name.starts_with("unroot:")) {
+        return create_service_thread("unroot", restart_unroot_service);
+    } else if (android::base::ConsumePrefix(&name, "backup:")) {
+        std::string cmd = "/system/bin/bu backup ";
+        cmd += name;
+        return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
+    } else if (name.starts_with("restore:")) {
+        return StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
+                               SubprocessProtocol::kNone);
+    } else if (name.starts_with("disable-verity:")) {
+        return create_service_thread("verity-on", std::bind(set_verity_enabled_state_service,
+                                                            std::placeholders::_1, false));
+    } else if (name.starts_with("enable-verity:")) {
+        return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
+                                                             std::placeholders::_1, true));
+    } else if (android::base::ConsumePrefix(&name, "tcpip:")) {
+        std::string str(name);
+
+        int port;
+        if (sscanf(str.c_str(), "%d", &port) != 1) {
+            return unique_fd{};
+        }
+        return create_service_thread("tcp",
+                                     std::bind(restart_tcp_service, std::placeholders::_1, port));
+    } else if (name.starts_with("usb:")) {
+        return create_service_thread("usb", restart_usb_service);
+    }
+#endif
+
+    if (android::base::ConsumePrefix(&name, "dev:")) {
+        return unique_fd{unix_open(name, O_RDWR | O_CLOEXEC)};
+    } else if (android::base::ConsumePrefix(&name, "jdwp:")) {
+        pid_t pid;
+        if (!ParseUint(&pid, name)) {
+            return unique_fd{};
+        }
+        return create_jdwp_connection_fd(pid);
+    } else if (android::base::ConsumePrefix(&name, "shell")) {
+        return ShellService(name, transport);
+    } else if (android::base::ConsumePrefix(&name, "exec:")) {
+        return StartSubprocess(std::string(name), nullptr, SubprocessType::kRaw,
+                               SubprocessProtocol::kNone);
+    } else if (name.starts_with("sync:")) {
+        return create_service_thread("sync", file_sync_service);
+    } else if (android::base::ConsumePrefix(&name, "reverse:")) {
+        return reverse_service(name, transport);
+    } else if (name == "reconnect") {
+        return create_service_thread(
+                "reconnect", std::bind(reconnect_service, std::placeholders::_1, transport));
+    } else if (name == "spin") {
+        return create_service_thread("spin", spin_service);
+    }
+
+    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..4fbccdb
--- /dev/null
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -0,0 +1,250 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libavb_user/libavb_user.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <fs_mgr.h>
+#include <fs_mgr_overlayfs.h>
+#include <fstab/fstab.h>
+#include <log/log_properties.h>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+
+#include "fec/io.h"
+
+#ifdef ALLOW_ADBD_DISABLE_VERITY
+static const bool kAllowDisableVerity = true;
+#else
+static const bool kAllowDisableVerity = false;
+#endif
+
+void suggest_run_adb_root(int fd) {
+    if (getuid() != 0) WriteFdExactly(fd, "Maybe run adb root?\n");
+}
+
+static bool make_block_device_writable(const std::string& dev) {
+    unique_fd fd(unix_open(dev, O_RDONLY | O_CLOEXEC));
+    if (fd == -1) {
+        return false;
+    }
+
+    int OFF = 0;
+    bool result = (ioctl(fd.get(), BLKROSET, &OFF) != -1);
+    return result;
+}
+
+/* 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));
+        suggest_run_adb_root(fd);
+        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;
+    }
+
+    auto change = false;
+    errno = 0;
+    if (enable ? fs_mgr_overlayfs_teardown(mount_point, &change)
+               : fs_mgr_overlayfs_setup(nullptr, mount_point, &change)) {
+        if (change) {
+            WriteFdFmt(fd, "%s overlayfs for %s\n", enable ? "disabling" : "using", mount_point);
+        }
+    } else if (errno) {
+        int expected_errno = enable ? EBUSY : ENOENT;
+        if (errno != expected_errno) {
+            WriteFdFmt(fd, "Overlayfs %s for %s failed with error %s\n",
+                       enable ? "teardown" : "setup", mount_point, strerror(errno));
+        }
+    }
+    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";
+}
+
+static bool overlayfs_setup(int fd, bool enable) {
+    auto change = false;
+    errno = 0;
+    if (enable ? fs_mgr_overlayfs_teardown(nullptr, &change)
+               : fs_mgr_overlayfs_setup(nullptr, nullptr, &change)) {
+        if (change) {
+            WriteFdFmt(fd, "%s overlayfs\n", enable ? "disabling" : "using");
+        }
+    } else if (errno) {
+        WriteFdFmt(fd, "Overlayfs %s failed with error %s\n", enable ? "teardown" : "setup",
+                   strerror(errno));
+        suggest_run_adb_root(fd);
+    }
+    return change;
+}
+
+/* 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;
+    }
+
+    overlayfs_setup(fd, enable_verity);
+    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)) {
+            overlayfs_setup(fd.get(), enable);
+            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
+        android::fs_mgr::Fstab fstab;
+        if (!android::fs_mgr::ReadDefaultFstab(&fstab)) {
+            WriteFdExactly(fd.get(), "Failed to read fstab\n");
+            suggest_run_adb_root(fd.get());
+            return;
+        }
+
+        // Loop through entries looking for ones that verity manages.
+        for (const auto& entry : fstab) {
+            if (entry.fs_mgr_flags.verify) {
+                if (set_verity_enabled_state(fd.get(), entry.blk_device.c_str(),
+                                             entry.mount_point.c_str(), enable)) {
+                    any_changed = true;
+                }
+            }
+        }
+    }
+    if (!any_changed) any_changed = overlayfs_setup(fd.get(), enable);
+
+    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..c0ed98e
--- /dev/null
+++ b/adb/daemon/set_verity_enable_state_service.h
@@ -0,0 +1,23 @@
+/*
+ * 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"
+
+#if defined(__ANDROID__)
+void set_verity_enabled_state_service(unique_fd fd, bool enable);
+#endif
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
new file mode 100644
index 0000000..de97068
--- /dev/null
+++ b/adb/daemon/shell_service.cpp
@@ -0,0 +1,884 @@
+/*
+ * 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>
+
+#if defined(__ANDROID__)
+#include <selinux/android.h>
+#endif
+
+#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(borrowed_fd 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(std::string command, const char* terminal_type, SubprocessType type,
+               SubprocessProtocol protocol, bool make_pty_raw);
+    ~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);
+
+    // Sets up FDs, starts a thread executing command and the manager thread,
+    // Returns false and sets error on failure.
+    bool ExecInProcess(Command command, 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);
+
+    bool ConnectProtocolEndpoints(std::string* _Nonnull error);
+
+    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_;
+    SubprocessType type_;
+    SubprocessProtocol protocol_;
+    bool make_pty_raw_;
+    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(std::string command, const char* terminal_type, SubprocessType type,
+                       SubprocessProtocol protocol, bool make_pty_raw)
+    : command_(std::move(command)),
+      terminal_type_(terminal_type ? terminal_type : ""),
+      type_(type),
+      protocol_(protocol),
+      make_pty_raw_(make_pty_raw) {}
+
+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 (const 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.get(), STDIN_FILENO);
+        dup2(child_stdinout_sfd.get(), STDOUT_FILENO);
+        dup2(child_stderr_sfd != -1 ? child_stderr_sfd.get() : child_stdinout_sfd.get(),
+             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 (!ConnectProtocolEndpoints(error)) {
+        kill(pid_, SIGKILL);
+        return false;
+    }
+
+    D("subprocess parent: completed");
+    return true;
+}
+
+bool Subprocess::ExecInProcess(Command command, std::string* _Nonnull error) {
+    unique_fd child_stdinout_sfd, child_stderr_sfd;
+
+    CHECK(type_ == SubprocessType::kRaw);
+
+    __android_log_security_bswrite(SEC_TAG_ADB_SHELL_CMD, command_.c_str());
+
+    if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
+        *error = android::base::StringPrintf("failed to create socketpair for stdin/out: %s",
+                                             strerror(errno));
+        return false;
+    }
+    if (protocol_ == SubprocessProtocol::kShell) {
+        // Shell protocol allows for splitting stderr.
+        if (!CreateSocketpair(&stderr_sfd_, &child_stderr_sfd)) {
+            *error = android::base::StringPrintf("failed to create socketpair for stderr: %s",
+                                                 strerror(errno));
+            return false;
+        }
+    } else {
+        // Raw protocol doesn't support multiple output streams, so combine stdout and stderr.
+        child_stderr_sfd.reset(dup(child_stdinout_sfd.get()));
+    }
+
+    D("execinprocess: stdin/stdout FD = %d, stderr FD = %d", stdinout_sfd_.get(),
+      stderr_sfd_.get());
+
+    if (!ConnectProtocolEndpoints(error)) {
+        return false;
+    }
+
+    std::thread([inout_sfd = std::move(child_stdinout_sfd), err_sfd = std::move(child_stderr_sfd),
+                 command = std::move(command),
+                 args = command_]() { command(args, inout_sfd, inout_sfd, err_sfd); })
+            .detach();
+
+    D("execinprocess: completed");
+    return true;
+}
+
+bool Subprocess::ConnectProtocolEndpoints(std::string* _Nonnull error) {
+    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 {
+        // Required for 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));
+            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";
+            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);
+                    return false;
+                }
+            }
+        }
+    }
+
+    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->get(), &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->get(), &master_read_set);
+            FD_CLR(dead_sfd->get(), &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_);
+                if (pid_ != -1) {
+                    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.get(), 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_.get(), stdinout_sfd_.get()), stderr_sfd_.get()) + 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_.get(), master_read_set_ptr);
+                FD_SET(stdinout_sfd_.get(), 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_.get(), master_read_set_ptr);
+                FD_CLR(stdinout_sfd_.get(), 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_.get();
+            }
+            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_.get(), 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_.get();
+                        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_.get();
+            }
+            // 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->get();
+        }
+        return sfd;
+    }
+
+    if (bytes > 0 && !output_->Write(id, bytes)) {
+        if (errno != 0) {
+            PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_.get();
+        }
+        return &protocol_sfd_;
+    }
+
+    return nullptr;
+}
+
+void Subprocess::WaitForExit() {
+    int exit_code = 1;
+
+    D("waiting for pid %d", pid_);
+    while (pid_ != -1) {
+        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.
+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(std::string name, const char* terminal_type, SubprocessType type,
+                          SubprocessProtocol 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.
+    bool make_pty_raw = false;
+    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;
+    }
+
+    unique_fd error_fd;
+    unique_fd fd = StartSubprocess(std::move(name), terminal_type, type, protocol, make_pty_raw,
+                                   protocol, &error_fd);
+    if (fd == -1) {
+        return error_fd;
+    }
+    return fd;
+}
+
+unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
+                          SubprocessProtocol protocol, bool make_pty_raw,
+                          SubprocessProtocol error_protocol, unique_fd* error_fd) {
+    D("starting %s subprocess (protocol=%s, TERM=%s): '%s'",
+      type == SubprocessType::kRaw ? "raw" : "PTY",
+      protocol == SubprocessProtocol::kNone ? "none" : "shell", terminal_type, name.c_str());
+
+    auto subprocess = std::make_unique<Subprocess>(std::move(name), terminal_type, type, protocol,
+                                                   make_pty_raw);
+    if (!subprocess) {
+        LOG(ERROR) << "failed to allocate new subprocess";
+        *error_fd = ReportError(error_protocol, "failed to allocate new subprocess");
+        return {};
+    }
+
+    std::string error;
+    if (!subprocess->ForkAndExec(&error)) {
+        LOG(ERROR) << "failed to start subprocess: " << error;
+        *error_fd = ReportError(error_protocol, error);
+        return {};
+    }
+
+    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;
+        *error_fd = ReportError(error_protocol, error);
+        return {};
+    }
+
+    return local_socket;
+}
+
+unique_fd StartCommandInProcess(std::string name, Command command, SubprocessProtocol protocol) {
+    LOG(INFO) << "StartCommandInProcess(" << dump_hex(name.data(), name.size()) << ")";
+
+    constexpr auto terminal_type = "";
+    constexpr auto type = SubprocessType::kRaw;
+    constexpr auto make_pty_raw = false;
+
+    auto subprocess = std::make_unique<Subprocess>(std::move(name), terminal_type, type, protocol,
+                                                   make_pty_raw);
+    if (!subprocess) {
+        LOG(ERROR) << "failed to allocate new subprocess";
+        return ReportError(protocol, "failed to allocate new subprocess");
+    }
+
+    std::string error;
+    if (!subprocess->ExecInProcess(std::move(command), &error)) {
+        LOG(ERROR) << "failed to start subprocess: " << error;
+        return ReportError(protocol, error);
+    }
+
+    unique_fd local_socket(subprocess->ReleaseLocalSocket());
+    D("inprocess 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 inprocess 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..030228c
--- /dev/null
+++ b/adb/daemon/shell_service.h
@@ -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.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "adb_unique_fd.h"
+
+#include <string_view>
+
+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(std::string name, const char* terminal_type, SubprocessType type,
+                          SubprocessProtocol protocol);
+
+// The same as above but with more fined grained control and custom error handling.
+unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
+                          SubprocessProtocol protocol, bool make_pty_raw,
+                          SubprocessProtocol error_protocol, unique_fd* error_fd);
+
+// Executes |command| in a separate thread.
+// Sets up in/out and error streams to emulate shell-like behavior.
+//
+// Returns an open FD connected to the thread or -1 on failure.
+using Command = int(std::string_view args, borrowed_fd in, borrowed_fd out, borrowed_fd err);
+unique_fd StartCommandInProcess(std::string name, Command command, SubprocessProtocol protocol);
+
+// Create a pipe containing the error.
+unique_fd ReportError(SubprocessProtocol protocol, const std::string& message);
diff --git a/adb/daemon/shell_service_test.cpp b/adb/daemon/shell_service_test.cpp
new file mode 100644
index 0000000..cdd8dbe
--- /dev/null
+++ b/adb/daemon/shell_service_test.cpp
@@ -0,0 +1,320 @@
+/*
+ * 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();
+
+    void StartTestCommandInProcess(std::string name, Command command, SubprocessProtocol protocol);
+
+    virtual void TearDown() override { CleanupTestSubprocess(); }
+
+    static sighandler_t saved_sigpipe_handler_;
+
+    unique_fd command_fd_;
+};
+
+sighandler_t ShellServiceTest::saved_sigpipe_handler_ = nullptr;
+
+void ShellServiceTest::StartTestSubprocess(
+        const char* command, SubprocessType type, SubprocessProtocol protocol) {
+    command_fd_ = StartSubprocess(command, nullptr, type, protocol);
+    ASSERT_TRUE(command_fd_ >= 0);
+}
+
+void ShellServiceTest::CleanupTestSubprocess() {
+}
+
+void ShellServiceTest::StartTestCommandInProcess(std::string name, Command command,
+                                                 SubprocessProtocol protocol) {
+    command_fd_ = StartCommandInProcess(std::move(name), std::move(command), protocol);
+    ASSERT_TRUE(command_fd_ >= 0);
+}
+
+namespace {
+
+// Reads raw data from |fd| until it closes or errors.
+std::string ReadRaw(borrowed_fd 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(borrowed_fd fd, std::string* stdout, std::string* stderr) {
+    int exit_code = -1;
+    stdout->clear();
+    stderr->clear();
+
+    auto protocol = std::make_unique<ShellProtocol>(fd.get());
+    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();
+        }
+    }
+
+    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(command_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(command_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(command_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(command_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(command_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(command_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(command_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(command_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(command_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(command_fd_, &stdout, &stderr));
+    ExpectLinesEqual(stdout, {"foo"});
+    ExpectLinesEqual(stderr, {});
+}
+
+// Tests an inprocess command with no protocol.
+TEST_F(ShellServiceTest, RawNoProtocolInprocess) {
+    ASSERT_NO_FATAL_FAILURE(
+            StartTestCommandInProcess("123",
+                                      [](auto args, auto in, auto out, auto err) -> int {
+                                          EXPECT_EQ("123", args);
+                                          char input[10];
+                                          EXPECT_TRUE(ReadFdExactly(in, input, 2));
+                                          input[2] = 0;
+                                          EXPECT_STREQ("in", input);
+                                          WriteFdExactly(out, "out\n");
+                                          WriteFdExactly(err, "err\n");
+                                          return 0;
+                                      },
+                                      SubprocessProtocol::kNone));
+
+    WriteFdExactly(command_fd_, "in");
+    ExpectLinesEqual(ReadRaw(command_fd_), {"out", "err"});
+}
+
+// Tests an inprocess command with the shell protocol.
+TEST_F(ShellServiceTest, RawShellProtocolInprocess) {
+    ASSERT_NO_FATAL_FAILURE(
+            StartTestCommandInProcess("321",
+                                      [](auto args, auto in, auto out, auto err) -> int {
+                                          EXPECT_EQ("321", args);
+                                          char input[10];
+                                          EXPECT_TRUE(ReadFdExactly(in, input, 2));
+                                          input[2] = 0;
+                                          EXPECT_STREQ("in", input);
+                                          WriteFdExactly(out, "out\n");
+                                          WriteFdExactly(err, "err\n");
+                                          return 0;
+                                      },
+                                      SubprocessProtocol::kShell));
+
+    {
+        auto write_protocol = std::make_unique<ShellProtocol>(command_fd_);
+        memcpy(write_protocol->data(), "in", 2);
+        write_protocol->Write(ShellProtocol::kIdStdin, 2);
+    }
+
+    std::string stdout, stderr;
+    // For in-process commands the exit code is always the default (1).
+    EXPECT_EQ(1, ReadShellProtocol(command_fd_, &stdout, &stderr));
+    ExpectLinesEqual(stdout, {"out"});
+    ExpectLinesEqual(stderr, {"err"});
+}
diff --git a/adb/daemon/transport_qemu.cpp b/adb/daemon/transport_qemu.cpp
new file mode 100644
index 0000000..aa760bc
--- /dev/null
+++ b/adb/daemon/transport_qemu.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 before sysdeps, since it has inlined references to open, read, write.
+#include <qemu_pipe.h>
+
+#define TRACE_TAG TRANSPORT
+#include "sysdeps.h"
+#include "transport.h"
+
+#include <android-base/properties.h>
+
+#include "adb_io.h"
+#include "adb_trace.h"
+#include "adb_unique_fd.h"
+
+/* A worker thread that monitors host connections, and registers a transport for
+ * every new host connection. This thread replaces server_socket_thread on
+ * condition that adbd daemon runs inside the emulator, and emulator uses QEMUD
+ * pipe to communicate with adbd daemon inside the guest. This is done in order
+ * to provide more robust communication channel between ADB host and guest. The
+ * main issue with server_socket_thread approach is that it runs on top of TCP,
+ * and thus is sensitive to network disruptions. For instance, the
+ * ConnectionManager may decide to reset all network connections, in which case
+ * the connection between ADB host and guest will be lost. To make ADB traffic
+ * independent from the network, we use here 'adb' QEMUD service to transfer data
+ * between the host, and the guest. See external/qemu/android/adb-*.* that
+ * implements the emulator's side of the protocol. Another advantage of using
+ * QEMUD approach is that ADB will be up much sooner, since it doesn't depend
+ * anymore on network being set up.
+ * The guest side of the protocol contains the following phases:
+ * - Connect with adb QEMUD service. In this phase a handle to 'adb' QEMUD service
+ *   is opened, and it becomes clear whether or not emulator supports that
+ *   protocol.
+ * - Wait for the ADB host to create connection with the guest. This is done by
+ *   sending an 'accept' request to the adb QEMUD service, and waiting on
+ *   response.
+ * - When new ADB host connection is accepted, the connection with adb QEMUD
+ *   service is registered as the transport, and a 'start' request is sent to the
+ *   adb QEMUD service, indicating that the guest is ready to receive messages.
+ *   Note that the guest will ignore messages sent down from the emulator before
+ *   the transport registration is completed. That's why we need to send the
+ *   'start' request after the transport is registered.
+ */
+void qemu_socket_thread(int port) {
+    /* 'accept' request to the adb QEMUD service. */
+    static const char _accept_req[] = "accept";
+    /* 'start' request to the adb QEMUD service. */
+    static const char _start_req[] = "start";
+    /* 'ok' reply from the adb QEMUD service. */
+    static const char _ok_resp[] = "ok";
+
+    char tmp[256];
+    char con_name[32];
+
+    adb_thread_setname("qemu socket");
+    D("transport: qemu_socket_thread() starting");
+
+    /* adb QEMUD service connection request. */
+    snprintf(con_name, sizeof(con_name), "pipe:qemud:adb:%d", port);
+
+    /* Connect to the adb QEMUD service. */
+    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. */
+        D("adb service is not available. Falling back to TCP socket.");
+        std::thread(server_socket_thread, tcp_listen_inaddr_any, port).detach();
+        return;
+    }
+
+    while (true) {
+        /*
+         * Wait till the host creates a new connection.
+         */
+
+        /* Send the 'accept' request. */
+        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.get(), tmp, 2) || memcmp(tmp, _ok_resp, 2)) {
+                D("Accepting ADB host connection has failed.");
+            } else {
+                /* Host is connected. Register the transport, and start the
+                 * exchange. */
+                std::string serial = android::base::StringPrintf("host-%d", fd.get());
+                WriteFdExactly(fd.get(), _start_req, strlen(_start_req));
+                register_socket_transport(std::move(fd), std::move(serial), port, 1,
+                                          [](atransport*) { return ReconnectResult::Abort; });
+            }
+
+            /* Prepare for accepting of the next ADB host connection. */
+            fd.reset(qemu_pipe_open(con_name));
+            if (fd < 0) {
+                D("adb service become unavailable.");
+                return;
+            }
+        } else {
+            D("Unable to send the '%s' request to ADB service.", _accept_req);
+            return;
+        }
+    }
+    D("transport: qemu_socket_thread() exiting");
+    return;
+}
+
+// If adbd is running inside the emulator, it will normally use QEMUD pipe (aka
+// goldfish) as the transport. This can either be explicitly set by the
+// service.adb.transport property, or be inferred from ro.kernel.qemu that is
+// set to "1" for ranchu/goldfish.
+bool use_qemu_goldfish() {
+    // Legacy way to detect if adbd should use the goldfish pipe is to check for
+    // ro.kernel.qemu, keep that behaviour for backward compatibility.
+    if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
+        return true;
+    }
+    // If service.adb.transport is present and is set to "goldfish", use the
+    // QEMUD pipe.
+    if (android::base::GetProperty("service.adb.transport", "") == "goldfish") {
+        return true;
+    }
+    return false;
+}
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 7869324..f4aa9fb 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,538 +18,746 @@
 
 #include "sysdeps.h"
 
-#include <dirent.h>
 #include <errno.h>
-#include <linux/usb/ch9.h>
-#include <linux/usb/functionfs.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/ioctl.h>
-#include <sys/mman.h>
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <linux/usb/functionfs.h>
+#include <sys/eventfd.h>
+
 #include <algorithm>
-#include <atomic>
-#include <chrono>
-#include <condition_variable>
+#include <array>
+#include <future>
+#include <memory>
 #include <mutex>
-#include <thread>
+#include <optional>
+#include <vector>
+
+#include <asyncio/AsyncIO.h>
 
 #include <android-base/logging.h>
+#include <android-base/macros.h>
 #include <android-base/properties.h>
+#include <android-base/thread_annotations.h>
 
-#include "adb.h"
-#include "daemon/usb.h"
+#include <adbd/usb.h>
+
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "sysdeps/chrono.h"
 #include "transport.h"
+#include "types.h"
 
-using namespace std::chrono_literals;
+using android::base::StringPrintf;
 
-#define MAX_PACKET_SIZE_FS 64
-#define MAX_PACKET_SIZE_HS 512
-#define MAX_PACKET_SIZE_SS 1024
+// We can't find out whether we have support for AIO on ffs endpoints until we submit a read.
+static std::optional<bool> gFfsAioSupported;
 
-#define USB_FFS_BULK_SIZE 16384
+// Not all USB controllers support operations larger than 16k, so don't go above that.
+// Also, each submitted operation does an allocation in the kernel of that size, so we want to
+// minimize our queue depth while still maintaining a deep enough queue to keep the USB stack fed.
+static constexpr size_t kUsbReadQueueDepth = 8;
+static constexpr size_t kUsbReadSize = 4 * PAGE_SIZE;
 
-// Number of buffers needed to fit MAX_PAYLOAD, with an extra for ZLPs.
-#define USB_FFS_NUM_BUFS ((MAX_PAYLOAD / USB_FFS_BULK_SIZE) + 1)
+static constexpr size_t kUsbWriteQueueDepth = 8;
+static constexpr size_t kUsbWriteSize = 4 * PAGE_SIZE;
 
-#define cpu_to_le16(x) htole16(x)
-#define cpu_to_le32(x) htole32(x)
-
-static int dummy_fd = -1;
-
-struct func_desc {
-    struct usb_interface_descriptor intf;
-    struct usb_endpoint_descriptor_no_audio source;
-    struct usb_endpoint_descriptor_no_audio sink;
-} __attribute__((packed));
-
-struct ss_func_desc {
-    struct usb_interface_descriptor intf;
-    struct usb_endpoint_descriptor_no_audio source;
-    struct usb_ss_ep_comp_descriptor source_comp;
-    struct usb_endpoint_descriptor_no_audio sink;
-    struct usb_ss_ep_comp_descriptor sink_comp;
-} __attribute__((packed));
-
-struct desc_v1 {
-    struct usb_functionfs_descs_head_v1 {
-        __le32 magic;
-        __le32 length;
-        __le32 fs_count;
-        __le32 hs_count;
-    } __attribute__((packed)) header;
-    struct func_desc fs_descs, hs_descs;
-} __attribute__((packed));
-
-struct desc_v2 {
-    struct usb_functionfs_descs_head_v2 header;
-    // The rest of the structure depends on the flags in the header.
-    __le32 fs_count;
-    __le32 hs_count;
-    __le32 ss_count;
-    __le32 os_count;
-    struct func_desc fs_descs, hs_descs;
-    struct ss_func_desc ss_descs;
-    struct usb_os_desc_header os_header;
-    struct usb_ext_compat_desc os_desc;
-} __attribute__((packed));
-
-static struct func_desc fs_descriptors = {
-    .intf = {
-        .bLength = sizeof(fs_descriptors.intf),
-        .bDescriptorType = USB_DT_INTERFACE,
-        .bInterfaceNumber = 0,
-        .bNumEndpoints = 2,
-        .bInterfaceClass = ADB_CLASS,
-        .bInterfaceSubClass = ADB_SUBCLASS,
-        .bInterfaceProtocol = ADB_PROTOCOL,
-        .iInterface = 1, /* first string from the provided table */
-    },
-    .source = {
-        .bLength = sizeof(fs_descriptors.source),
-        .bDescriptorType = USB_DT_ENDPOINT,
-        .bEndpointAddress = 1 | USB_DIR_OUT,
-        .bmAttributes = USB_ENDPOINT_XFER_BULK,
-        .wMaxPacketSize = MAX_PACKET_SIZE_FS,
-    },
-    .sink = {
-        .bLength = sizeof(fs_descriptors.sink),
-        .bDescriptorType = USB_DT_ENDPOINT,
-        .bEndpointAddress = 2 | USB_DIR_IN,
-        .bmAttributes = USB_ENDPOINT_XFER_BULK,
-        .wMaxPacketSize = MAX_PACKET_SIZE_FS,
-    },
-};
-
-static struct func_desc hs_descriptors = {
-    .intf = {
-        .bLength = sizeof(hs_descriptors.intf),
-        .bDescriptorType = USB_DT_INTERFACE,
-        .bInterfaceNumber = 0,
-        .bNumEndpoints = 2,
-        .bInterfaceClass = ADB_CLASS,
-        .bInterfaceSubClass = ADB_SUBCLASS,
-        .bInterfaceProtocol = ADB_PROTOCOL,
-        .iInterface = 1, /* first string from the provided table */
-    },
-    .source = {
-        .bLength = sizeof(hs_descriptors.source),
-        .bDescriptorType = USB_DT_ENDPOINT,
-        .bEndpointAddress = 1 | USB_DIR_OUT,
-        .bmAttributes = USB_ENDPOINT_XFER_BULK,
-        .wMaxPacketSize = MAX_PACKET_SIZE_HS,
-    },
-    .sink = {
-        .bLength = sizeof(hs_descriptors.sink),
-        .bDescriptorType = USB_DT_ENDPOINT,
-        .bEndpointAddress = 2 | USB_DIR_IN,
-        .bmAttributes = USB_ENDPOINT_XFER_BULK,
-        .wMaxPacketSize = MAX_PACKET_SIZE_HS,
-    },
-};
-
-static struct ss_func_desc ss_descriptors = {
-    .intf = {
-        .bLength = sizeof(ss_descriptors.intf),
-        .bDescriptorType = USB_DT_INTERFACE,
-        .bInterfaceNumber = 0,
-        .bNumEndpoints = 2,
-        .bInterfaceClass = ADB_CLASS,
-        .bInterfaceSubClass = ADB_SUBCLASS,
-        .bInterfaceProtocol = ADB_PROTOCOL,
-        .iInterface = 1, /* first string from the provided table */
-    },
-    .source = {
-        .bLength = sizeof(ss_descriptors.source),
-        .bDescriptorType = USB_DT_ENDPOINT,
-        .bEndpointAddress = 1 | USB_DIR_OUT,
-        .bmAttributes = USB_ENDPOINT_XFER_BULK,
-        .wMaxPacketSize = MAX_PACKET_SIZE_SS,
-    },
-    .source_comp = {
-        .bLength = sizeof(ss_descriptors.source_comp),
-        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
-        .bMaxBurst = 4,
-    },
-    .sink = {
-        .bLength = sizeof(ss_descriptors.sink),
-        .bDescriptorType = USB_DT_ENDPOINT,
-        .bEndpointAddress = 2 | USB_DIR_IN,
-        .bmAttributes = USB_ENDPOINT_XFER_BULK,
-        .wMaxPacketSize = MAX_PACKET_SIZE_SS,
-    },
-    .sink_comp = {
-        .bLength = sizeof(ss_descriptors.sink_comp),
-        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
-        .bMaxBurst = 4,
-    },
-};
-
-struct usb_ext_compat_desc os_desc_compat = {
-    .bFirstInterfaceNumber = 0,
-    .Reserved1 = cpu_to_le32(1),
-    .CompatibleID = {0},
-    .SubCompatibleID = {0},
-    .Reserved2 = {0},
-};
-
-static struct usb_os_desc_header os_desc_header = {
-    .interface = cpu_to_le32(1),
-    .dwLength = cpu_to_le32(sizeof(os_desc_header) + sizeof(os_desc_compat)),
-    .bcdVersion = cpu_to_le32(1),
-    .wIndex = cpu_to_le32(4),
-    .bCount = cpu_to_le32(1),
-    .Reserved = cpu_to_le32(0),
-};
-
-#define STR_INTERFACE_ "ADB Interface"
-
-static const struct {
-    struct usb_functionfs_strings_head header;
-    struct {
-        __le16 code;
-        const char str1[sizeof(STR_INTERFACE_)];
-    } __attribute__((packed)) lang0;
-} __attribute__((packed)) strings = {
-    .header = {
-        .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
-        .length = cpu_to_le32(sizeof(strings)),
-        .str_count = cpu_to_le32(1),
-        .lang_count = cpu_to_le32(1),
-    },
-    .lang0 = {
-        cpu_to_le16(0x0409), /* en-us */
-        STR_INTERFACE_,
-    },
-};
-
-static void aio_block_init(aio_block* aiob) {
-    aiob->iocb.resize(USB_FFS_NUM_BUFS);
-    aiob->iocbs.resize(USB_FFS_NUM_BUFS);
-    aiob->events.resize(USB_FFS_NUM_BUFS);
-    aiob->num_submitted = 0;
-    for (unsigned i = 0; i < USB_FFS_NUM_BUFS; i++) {
-        aiob->iocbs[i] = &aiob->iocb[i];
+static const char* to_string(enum usb_functionfs_event_type type) {
+    switch (type) {
+        case FUNCTIONFS_BIND:
+            return "FUNCTIONFS_BIND";
+        case FUNCTIONFS_UNBIND:
+            return "FUNCTIONFS_UNBIND";
+        case FUNCTIONFS_ENABLE:
+            return "FUNCTIONFS_ENABLE";
+        case FUNCTIONFS_DISABLE:
+            return "FUNCTIONFS_DISABLE";
+        case FUNCTIONFS_SETUP:
+            return "FUNCTIONFS_SETUP";
+        case FUNCTIONFS_SUSPEND:
+            return "FUNCTIONFS_SUSPEND";
+        case FUNCTIONFS_RESUME:
+            return "FUNCTIONFS_RESUME";
     }
 }
 
-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;
+enum class TransferDirection : uint64_t {
+    READ = 0,
+    WRITE = 1,
+};
+
+struct TransferId {
+    TransferDirection direction : 1;
+    uint64_t id : 63;
+
+    TransferId() : TransferId(TransferDirection::READ, 0) {}
+
+  private:
+    TransferId(TransferDirection direction, uint64_t id) : direction(direction), id(id) {}
+
+  public:
+    explicit operator uint64_t() const {
+        uint64_t result;
+        static_assert(sizeof(*this) == sizeof(result));
+        memcpy(&result, this, sizeof(*this));
+        return result;
     }
-}
 
-bool init_functionfs(struct usb_handle* h) {
-    LOG(INFO) << "initializing functionfs";
+    static TransferId read(uint64_t id) { return TransferId(TransferDirection::READ, id); }
+    static TransferId write(uint64_t id) { return TransferId(TransferDirection::WRITE, id); }
 
-    ssize_t ret;
-    struct desc_v1 v1_descriptor;
-    struct desc_v2 v2_descriptor;
+    static TransferId from_value(uint64_t value) {
+        TransferId result;
+        memcpy(&result, &value, sizeof(value));
+        return result;
+    }
+};
 
-    v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
-    v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
-    v2_descriptor.header.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
-                                 FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC;
-    v2_descriptor.fs_count = 3;
-    v2_descriptor.hs_count = 3;
-    v2_descriptor.ss_count = 5;
-    v2_descriptor.os_count = 1;
-    v2_descriptor.fs_descs = fs_descriptors;
-    v2_descriptor.hs_descs = hs_descriptors;
-    v2_descriptor.ss_descs = ss_descriptors;
-    v2_descriptor.os_header = os_desc_header;
-    v2_descriptor.os_desc = os_desc_compat;
+struct IoBlock {
+    bool pending = false;
+    struct iocb control = {};
+    std::shared_ptr<Block> payload;
 
-    if (h->control < 0) { // might have already done this before
-        LOG(INFO) << "opening control endpoint " << USB_FFS_ADB_EP0;
-        h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR);
-        if (h->control < 0) {
-            PLOG(ERROR) << "cannot open control endpoint " << USB_FFS_ADB_EP0;
-            goto err;
+    TransferId id() const { return TransferId::from_value(control.aio_data); }
+};
+
+struct ScopedAioContext {
+    ScopedAioContext() = default;
+    ~ScopedAioContext() { reset(); }
+
+    ScopedAioContext(ScopedAioContext&& move) { reset(move.release()); }
+    ScopedAioContext(const ScopedAioContext& copy) = delete;
+
+    ScopedAioContext& operator=(ScopedAioContext&& move) {
+        reset(move.release());
+        return *this;
+    }
+    ScopedAioContext& operator=(const ScopedAioContext& copy) = delete;
+
+    static ScopedAioContext Create(size_t max_events) {
+        aio_context_t ctx = 0;
+        if (io_setup(max_events, &ctx) != 0) {
+            PLOG(FATAL) << "failed to create aio_context_t";
+        }
+        ScopedAioContext result;
+        result.reset(ctx);
+        return result;
+    }
+
+    aio_context_t release() {
+        aio_context_t result = context_;
+        context_ = 0;
+        return result;
+    }
+
+    void reset(aio_context_t new_context = 0) {
+        if (context_ != 0) {
+            io_destroy(context_);
         }
 
-        ret = adb_write(h->control, &v2_descriptor, sizeof(v2_descriptor));
-        if (ret < 0) {
-            v1_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC);
-            v1_descriptor.header.length = cpu_to_le32(sizeof(v1_descriptor));
-            v1_descriptor.header.fs_count = 3;
-            v1_descriptor.header.hs_count = 3;
-            v1_descriptor.fs_descs = fs_descriptors;
-            v1_descriptor.hs_descs = hs_descriptors;
-            D("[ %s: Switching to V1_descriptor format errno=%d ]", USB_FFS_ADB_EP0, errno);
-            ret = adb_write(h->control, &v1_descriptor, sizeof(v1_descriptor));
-            if (ret < 0) {
-                D("[ %s: write descriptors failed: errno=%d ]", USB_FFS_ADB_EP0, errno);
-                goto err;
+        context_ = new_context;
+    }
+
+    aio_context_t get() { return context_; }
+
+  private:
+    aio_context_t context_ = 0;
+};
+
+struct UsbFfsConnection : public Connection {
+    UsbFfsConnection(unique_fd control, unique_fd read, unique_fd write,
+                     std::promise<void> destruction_notifier)
+        : worker_started_(false),
+          stopped_(false),
+          destruction_notifier_(std::move(destruction_notifier)),
+          control_fd_(std::move(control)),
+          read_fd_(std::move(read)),
+          write_fd_(std::move(write)) {
+        LOG(INFO) << "UsbFfsConnection constructed";
+        worker_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+        if (worker_event_fd_ == -1) {
+            PLOG(FATAL) << "failed to create eventfd";
+        }
+
+        monitor_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+        if (monitor_event_fd_ == -1) {
+            PLOG(FATAL) << "failed to create eventfd";
+        }
+
+        aio_context_ = ScopedAioContext::Create(kUsbReadQueueDepth + kUsbWriteQueueDepth);
+    }
+
+    ~UsbFfsConnection() {
+        LOG(INFO) << "UsbFfsConnection being destroyed";
+        Stop();
+        monitor_thread_.join();
+
+        // We need to explicitly close our file descriptors before we notify our destruction,
+        // because the thread listening on the future will immediately try to reopen the endpoint.
+        aio_context_.reset();
+        control_fd_.reset();
+        read_fd_.reset();
+        write_fd_.reset();
+
+        destruction_notifier_.set_value();
+    }
+
+    virtual bool Write(std::unique_ptr<apacket> packet) override final {
+        LOG(DEBUG) << "USB write: " << dump_header(&packet->msg);
+        Block header(sizeof(packet->msg));
+        memcpy(header.data(), &packet->msg, sizeof(packet->msg));
+
+        std::lock_guard<std::mutex> lock(write_mutex_);
+        write_requests_.push_back(CreateWriteBlock(std::move(header), next_write_id_++));
+        if (!packet->payload.empty()) {
+            // The kernel attempts to allocate a contiguous block of memory for each write,
+            // which can fail if the write is large and the kernel heap is fragmented.
+            // Split large writes into smaller chunks to avoid this.
+            std::shared_ptr<Block> payload = std::make_shared<Block>(std::move(packet->payload));
+            size_t offset = 0;
+            size_t len = payload->size();
+
+            while (len > 0) {
+                size_t write_size = std::min(kUsbWriteSize, len);
+                write_requests_.push_back(
+                        CreateWriteBlock(payload, offset, write_size, next_write_id_++));
+                len -= write_size;
+                offset += write_size;
+            }
+        }
+        SubmitWrites();
+        return true;
+    }
+
+    virtual void Start() override final { StartMonitor(); }
+
+    virtual void Stop() override final {
+        if (stopped_.exchange(true)) {
+            return;
+        }
+        stopped_ = true;
+        uint64_t notify = 1;
+        ssize_t rc = adb_write(worker_event_fd_.get(), &notify, sizeof(notify));
+        if (rc < 0) {
+            PLOG(FATAL) << "failed to notify worker eventfd to stop UsbFfsConnection";
+        }
+        CHECK_EQ(static_cast<size_t>(rc), sizeof(notify));
+
+        rc = adb_write(monitor_event_fd_.get(), &notify, sizeof(notify));
+        if (rc < 0) {
+            PLOG(FATAL) << "failed to notify monitor eventfd to stop UsbFfsConnection";
+        }
+
+        CHECK_EQ(static_cast<size_t>(rc), sizeof(notify));
+    }
+
+  private:
+    void StartMonitor() {
+        // This is a bit of a mess.
+        // It's possible for io_submit to end up blocking, if we call it as the endpoint
+        // becomes disabled. Work around this by having a monitor thread to listen for functionfs
+        // lifecycle events. If we notice an error condition (either we've become disabled, or we
+        // were never enabled in the first place), we send interruption signals to the worker thread
+        // until it dies, and then report failure to the transport via HandleError, which will
+        // eventually result in the transport being destroyed, which will result in UsbFfsConnection
+        // being destroyed, which unblocks the open thread and restarts this entire process.
+        static std::once_flag handler_once;
+        std::call_once(handler_once, []() { signal(kInterruptionSignal, [](int) {}); });
+
+        monitor_thread_ = std::thread([this]() {
+            adb_thread_setname("UsbFfs-monitor");
+
+            bool bound = false;
+            bool enabled = false;
+            bool running = true;
+            while (running) {
+                adb_pollfd pfd[2] = {
+                  { .fd = control_fd_.get(), .events = POLLIN, .revents = 0 },
+                  { .fd = monitor_event_fd_.get(), .events = POLLIN, .revents = 0 },
+                };
+
+                // If we don't see our first bind within a second, try again.
+                int timeout_ms = bound ? -1 : 1000;
+
+                int rc = TEMP_FAILURE_RETRY(adb_poll(pfd, 2, timeout_ms));
+                if (rc == -1) {
+                    PLOG(FATAL) << "poll on USB control fd failed";
+                } else if (rc == 0) {
+                    LOG(WARNING) << "timed out while waiting for FUNCTIONFS_BIND, trying again";
+                    break;
+                }
+
+                if (pfd[1].revents) {
+                    // We were told to die.
+                    break;
+                }
+
+                struct usb_functionfs_event event;
+                rc = TEMP_FAILURE_RETRY(adb_read(control_fd_.get(), &event, sizeof(event)));
+                if (rc == -1) {
+                    PLOG(FATAL) << "failed to read functionfs event";
+                } else if (rc == 0) {
+                    LOG(WARNING) << "hit EOF on functionfs control fd";
+                    break;
+                } else if (rc != sizeof(event)) {
+                    LOG(FATAL) << "read functionfs event of unexpected size, expected "
+                               << sizeof(event) << ", got " << rc;
+                }
+
+                LOG(INFO) << "USB event: "
+                          << to_string(static_cast<usb_functionfs_event_type>(event.type));
+
+                switch (event.type) {
+                    case FUNCTIONFS_BIND:
+                        if (bound) {
+                            LOG(WARNING) << "received FUNCTIONFS_BIND while already bound?";
+                            running = false;
+                            break;
+                        }
+
+                        if (enabled) {
+                            LOG(WARNING) << "received FUNCTIONFS_BIND while already enabled?";
+                            running = false;
+                            break;
+                        }
+
+                        bound = true;
+                        break;
+
+                    case FUNCTIONFS_ENABLE:
+                        if (!bound) {
+                            LOG(WARNING) << "received FUNCTIONFS_ENABLE while not bound?";
+                            running = false;
+                            break;
+                        }
+
+                        if (enabled) {
+                            LOG(WARNING) << "received FUNCTIONFS_ENABLE while already enabled?";
+                            running = false;
+                            break;
+                        }
+
+                        enabled = true;
+                        StartWorker();
+                        break;
+
+                    case FUNCTIONFS_DISABLE:
+                        if (!bound) {
+                            LOG(WARNING) << "received FUNCTIONFS_DISABLE while not bound?";
+                        }
+
+                        if (!enabled) {
+                            LOG(WARNING) << "received FUNCTIONFS_DISABLE while not enabled?";
+                        }
+
+                        enabled = false;
+                        running = false;
+                        break;
+
+                    case FUNCTIONFS_UNBIND:
+                        if (enabled) {
+                            LOG(WARNING) << "received FUNCTIONFS_UNBIND while still enabled?";
+                        }
+
+                        if (!bound) {
+                            LOG(WARNING) << "received FUNCTIONFS_UNBIND when not bound?";
+                        }
+
+                        bound = false;
+                        running = false;
+                        break;
+
+                    case FUNCTIONFS_SETUP: {
+                        LOG(INFO) << "received FUNCTIONFS_SETUP control transfer: bRequestType = "
+                                  << static_cast<int>(event.u.setup.bRequestType)
+                                  << ", bRequest = " << static_cast<int>(event.u.setup.bRequest)
+                                  << ", wValue = " << static_cast<int>(event.u.setup.wValue)
+                                  << ", wIndex = " << static_cast<int>(event.u.setup.wIndex)
+                                  << ", wLength = " << static_cast<int>(event.u.setup.wLength);
+
+                        if ((event.u.setup.bRequestType & USB_DIR_IN)) {
+                            LOG(INFO) << "acking device-to-host control transfer";
+                            ssize_t rc = adb_write(control_fd_.get(), "", 0);
+                            if (rc != 0) {
+                                PLOG(ERROR) << "failed to write empty packet to host";
+                                break;
+                            }
+                        } else {
+                            std::string buf;
+                            buf.resize(event.u.setup.wLength + 1);
+
+                            ssize_t rc = adb_read(control_fd_.get(), buf.data(), buf.size());
+                            if (rc != event.u.setup.wLength) {
+                                LOG(ERROR)
+                                        << "read " << rc
+                                        << " bytes when trying to read control request, expected "
+                                        << event.u.setup.wLength;
+                            }
+
+                            LOG(INFO) << "control request contents: " << buf;
+                            break;
+                        }
+                    }
+                }
+            }
+
+            StopWorker();
+            HandleError("monitor thread finished");
+        });
+    }
+
+    void StartWorker() {
+        CHECK(!worker_started_);
+        worker_started_ = true;
+        worker_thread_ = std::thread([this]() {
+            adb_thread_setname("UsbFfs-worker");
+            for (size_t i = 0; i < kUsbReadQueueDepth; ++i) {
+                read_requests_[i] = CreateReadBlock(next_read_id_++);
+                if (!SubmitRead(&read_requests_[i])) {
+                    return;
+                }
+            }
+
+            while (!stopped_) {
+                uint64_t dummy;
+                ssize_t rc = adb_read(worker_event_fd_.get(), &dummy, sizeof(dummy));
+                if (rc == -1) {
+                    PLOG(FATAL) << "failed to read from eventfd";
+                } else if (rc == 0) {
+                    LOG(FATAL) << "hit EOF on eventfd";
+                }
+
+                ReadEvents();
+            }
+        });
+    }
+
+    void StopWorker() {
+        if (!worker_started_) {
+            return;
+        }
+
+        pthread_t worker_thread_handle = worker_thread_.native_handle();
+        while (true) {
+            int rc = pthread_kill(worker_thread_handle, kInterruptionSignal);
+            if (rc != 0) {
+                LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
+                break;
+            }
+
+            std::this_thread::sleep_for(100ms);
+
+            rc = pthread_kill(worker_thread_handle, 0);
+            if (rc == 0) {
+                continue;
+            } else if (rc == ESRCH) {
+                break;
+            } else {
+                LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
             }
         }
 
-        ret = adb_write(h->control, &strings, sizeof(strings));
-        if (ret < 0) {
-            D("[ %s: writing strings failed: errno=%d]", USB_FFS_ADB_EP0, errno);
-            goto err;
+        worker_thread_.join();
+    }
+
+    void PrepareReadBlock(IoBlock* block, uint64_t id) {
+        block->pending = false;
+        block->payload = std::make_shared<Block>(kUsbReadSize);
+        block->control.aio_data = static_cast<uint64_t>(TransferId::read(id));
+        block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload->data());
+        block->control.aio_nbytes = block->payload->size();
+    }
+
+    IoBlock CreateReadBlock(uint64_t id) {
+        IoBlock block;
+        PrepareReadBlock(&block, id);
+        block.control.aio_rw_flags = 0;
+        block.control.aio_lio_opcode = IOCB_CMD_PREAD;
+        block.control.aio_reqprio = 0;
+        block.control.aio_fildes = read_fd_.get();
+        block.control.aio_offset = 0;
+        block.control.aio_flags = IOCB_FLAG_RESFD;
+        block.control.aio_resfd = worker_event_fd_.get();
+        return block;
+    }
+
+    void ReadEvents() {
+        static constexpr size_t kMaxEvents = kUsbReadQueueDepth + kUsbWriteQueueDepth;
+        struct io_event events[kMaxEvents];
+        struct timespec timeout = {.tv_sec = 0, .tv_nsec = 0};
+        int rc = io_getevents(aio_context_.get(), 0, kMaxEvents, events, &timeout);
+        if (rc == -1) {
+            HandleError(StringPrintf("io_getevents failed while reading: %s", strerror(errno)));
+            return;
         }
-        //Signal only when writing the descriptors to ffs
-        android::base::SetProperty("sys.usb.ffs.ready", "1");
+
+        for (int event_idx = 0; event_idx < rc; ++event_idx) {
+            auto& event = events[event_idx];
+            TransferId id = TransferId::from_value(event.data);
+
+            if (event.res < 0) {
+                std::string error =
+                        StringPrintf("%s %" PRIu64 " failed with error %s",
+                                     id.direction == TransferDirection::READ ? "read" : "write",
+                                     id.id, strerror(-event.res));
+                HandleError(error);
+                return;
+            }
+
+            if (id.direction == TransferDirection::READ) {
+                if (!HandleRead(id, event.res)) {
+                    return;
+                }
+            } else {
+                HandleWrite(id);
+            }
+        }
     }
 
-    h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDWR);
-    if (h->bulk_out < 0) {
-        PLOG(ERROR) << "cannot open bulk-out endpoint " << USB_FFS_ADB_OUT;
-        goto err;
+    bool HandleRead(TransferId id, int64_t size) {
+        uint64_t read_idx = id.id % kUsbReadQueueDepth;
+        IoBlock* block = &read_requests_[read_idx];
+        block->pending = false;
+        block->payload->resize(size);
+
+        // Notification for completed reads can be received out of order.
+        if (block->id().id != needed_read_id_) {
+            LOG(VERBOSE) << "read " << block->id().id << " completed while waiting for "
+                         << needed_read_id_;
+            return true;
+        }
+
+        for (uint64_t id = needed_read_id_;; ++id) {
+            size_t read_idx = id % kUsbReadQueueDepth;
+            IoBlock* current_block = &read_requests_[read_idx];
+            if (current_block->pending) {
+                break;
+            }
+            if (!ProcessRead(current_block)) {
+                return false;
+            }
+            ++needed_read_id_;
+        }
+
+        return true;
     }
 
-    h->bulk_in = adb_open(USB_FFS_ADB_IN, O_RDWR);
-    if (h->bulk_in < 0) {
-        PLOG(ERROR) << "cannot open bulk-in endpoint " << USB_FFS_ADB_IN;
-        goto err;
+    bool ProcessRead(IoBlock* block) {
+        if (!block->payload->empty()) {
+            if (!incoming_header_.has_value()) {
+                if (block->payload->size() != sizeof(amessage)) {
+                    HandleError("received packet of unexpected length while reading header");
+                    return false;
+                }
+                amessage msg;
+                memcpy(&msg, block->payload->data(), sizeof(amessage));
+                LOG(DEBUG) << "USB read:" << dump_header(&msg);
+                incoming_header_ = msg;
+            } else {
+                size_t bytes_left = incoming_header_->data_length - incoming_payload_.size();
+                Block payload = std::move(*block->payload);
+                if (block->payload->size() > bytes_left) {
+                    HandleError("received too many bytes while waiting for payload");
+                    return false;
+                }
+                incoming_payload_.append(std::make_unique<Block>(std::move(payload)));
+            }
+
+            if (incoming_header_->data_length == incoming_payload_.size()) {
+                auto packet = std::make_unique<apacket>();
+                packet->msg = *incoming_header_;
+
+                // TODO: Make apacket contain an IOVector so we don't have to coalesce.
+                packet->payload = incoming_payload_.coalesce();
+                read_callback_(this, std::move(packet));
+
+                incoming_header_.reset();
+                incoming_payload_.clear();
+            }
+        }
+
+        PrepareReadBlock(block, block->id().id + kUsbReadQueueDepth);
+        SubmitRead(block);
+        return true;
     }
 
-    if (io_setup(USB_FFS_NUM_BUFS, &h->read_aiob.ctx) ||
-        io_setup(USB_FFS_NUM_BUFS, &h->write_aiob.ctx)) {
-        D("[ aio: got error on io_setup (%d) ]", errno);
+    bool SubmitRead(IoBlock* block) {
+        block->pending = true;
+        struct iocb* iocb = &block->control;
+        if (io_submit(aio_context_.get(), 1, &iocb) != 1) {
+            if (errno == EINVAL && !gFfsAioSupported.has_value()) {
+                HandleError("failed to submit first read, AIO on FFS not supported");
+                gFfsAioSupported = false;
+                return false;
+            }
+
+            HandleError(StringPrintf("failed to submit read: %s", strerror(errno)));
+            return false;
+        }
+
+        gFfsAioSupported = true;
+        return true;
     }
 
-    h->read_aiob.fd = h->bulk_out;
-    h->write_aiob.fd = h->bulk_in;
-    return true;
+    void HandleWrite(TransferId id) {
+        std::lock_guard<std::mutex> lock(write_mutex_);
+        auto it =
+                std::find_if(write_requests_.begin(), write_requests_.end(), [id](const auto& req) {
+                    return static_cast<uint64_t>(req->id()) == static_cast<uint64_t>(id);
+                });
+        CHECK(it != write_requests_.end());
 
-err:
-    if (h->bulk_in > 0) {
-        adb_close(h->bulk_in);
-        h->bulk_in = -1;
-    }
-    if (h->bulk_out > 0) {
-        adb_close(h->bulk_out);
-        h->bulk_out = -1;
-    }
-    if (h->control > 0) {
-        adb_close(h->control);
-        h->control = -1;
-    }
-    return false;
-}
+        write_requests_.erase(it);
+        size_t outstanding_writes = --writes_submitted_;
+        LOG(DEBUG) << "USB write: reaped, down to " << outstanding_writes;
 
-static void usb_ffs_open_thread(void* x) {
-    struct usb_handle* usb = (struct usb_handle*)x;
+        SubmitWrites();
+    }
 
+    std::unique_ptr<IoBlock> CreateWriteBlock(std::shared_ptr<Block> payload, size_t offset,
+                                              size_t len, uint64_t id) {
+        auto block = std::make_unique<IoBlock>();
+        block->payload = std::move(payload);
+        block->control.aio_data = static_cast<uint64_t>(TransferId::write(id));
+        block->control.aio_rw_flags = 0;
+        block->control.aio_lio_opcode = IOCB_CMD_PWRITE;
+        block->control.aio_reqprio = 0;
+        block->control.aio_fildes = write_fd_.get();
+        block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload->data() + offset);
+        block->control.aio_nbytes = len;
+        block->control.aio_offset = 0;
+        block->control.aio_flags = IOCB_FLAG_RESFD;
+        block->control.aio_resfd = worker_event_fd_.get();
+        return block;
+    }
+
+    std::unique_ptr<IoBlock> CreateWriteBlock(Block payload, uint64_t id) {
+        std::shared_ptr<Block> block = std::make_shared<Block>(std::move(payload));
+        size_t len = block->size();
+        return CreateWriteBlock(std::move(block), 0, len, id);
+    }
+
+    void SubmitWrites() REQUIRES(write_mutex_) {
+        if (writes_submitted_ == kUsbWriteQueueDepth) {
+            return;
+        }
+
+        ssize_t writes_to_submit = std::min(kUsbWriteQueueDepth - writes_submitted_,
+                                            write_requests_.size() - writes_submitted_);
+        CHECK_GE(writes_to_submit, 0);
+        if (writes_to_submit == 0) {
+            return;
+        }
+
+        struct iocb* iocbs[kUsbWriteQueueDepth];
+        for (int i = 0; i < writes_to_submit; ++i) {
+            CHECK(!write_requests_[writes_submitted_ + i]->pending);
+            write_requests_[writes_submitted_ + i]->pending = true;
+            iocbs[i] = &write_requests_[writes_submitted_ + i]->control;
+            LOG(VERBOSE) << "submitting write_request " << static_cast<void*>(iocbs[i]);
+        }
+
+        writes_submitted_ += writes_to_submit;
+
+        int rc = io_submit(aio_context_.get(), writes_to_submit, iocbs);
+        if (rc == -1) {
+            HandleError(StringPrintf("failed to submit write requests: %s", strerror(errno)));
+            return;
+        } else if (rc != writes_to_submit) {
+            LOG(FATAL) << "failed to submit all writes: wanted to submit " << writes_to_submit
+                       << ", actually submitted " << rc;
+        }
+    }
+
+    void HandleError(const std::string& error) {
+        std::call_once(error_flag_, [&]() {
+            error_callback_(this, error);
+            if (!stopped_) {
+                Stop();
+            }
+        });
+    }
+
+    std::thread monitor_thread_;
+
+    bool worker_started_;
+    std::thread worker_thread_;
+
+    std::atomic<bool> stopped_;
+    std::promise<void> destruction_notifier_;
+    std::once_flag error_flag_;
+
+    unique_fd worker_event_fd_;
+    unique_fd monitor_event_fd_;
+
+    ScopedAioContext aio_context_;
+    unique_fd control_fd_;
+    unique_fd read_fd_;
+    unique_fd write_fd_;
+
+    std::optional<amessage> incoming_header_;
+    IOVector incoming_payload_;
+
+    std::array<IoBlock, kUsbReadQueueDepth> read_requests_;
+    IOVector read_data_;
+
+    // ID of the next request that we're going to send out.
+    size_t next_read_id_ = 0;
+
+    // ID of the next packet we're waiting for.
+    size_t needed_read_id_ = 0;
+
+    std::mutex write_mutex_;
+    std::deque<std::unique_ptr<IoBlock>> write_requests_ GUARDED_BY(write_mutex_);
+    size_t next_write_id_ GUARDED_BY(write_mutex_) = 0;
+    size_t writes_submitted_ GUARDED_BY(write_mutex_) = 0;
+
+    static constexpr int kInterruptionSignal = SIGUSR1;
+};
+
+void usb_init_legacy();
+
+static void usb_ffs_open_thread() {
     adb_thread_setname("usb ffs open");
 
     while (true) {
-        // wait until the USB device needs opening
-        std::unique_lock<std::mutex> lock(usb->lock);
-        while (!usb->open_new_connection) {
-            usb->notify.wait(lock);
+        if (gFfsAioSupported.has_value() && !gFfsAioSupported.value()) {
+            LOG(INFO) << "failed to use nonblocking ffs, falling back to legacy";
+            return usb_init_legacy();
         }
-        usb->open_new_connection = false;
-        lock.unlock();
 
-        while (true) {
-            if (init_functionfs(usb)) {
-                LOG(INFO) << "functionfs successfully initialized";
-                break;
-            }
+        unique_fd control;
+        unique_fd bulk_out;
+        unique_fd bulk_in;
+        if (!open_functionfs(&control, &bulk_out, &bulk_in)) {
             std::this_thread::sleep_for(1s);
-        }
-
-        LOG(INFO) << "registering usb transport";
-        register_usb_transport(usb, 0, 0, 1);
-    }
-
-    // never gets here
-    abort();
-}
-
-static int usb_ffs_write(usb_handle* h, const void* data, int len) {
-    D("about to write (fd=%d, len=%d)", h->bulk_in, len);
-
-    const char* buf = static_cast<const char*>(data);
-    while (len > 0) {
-        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));
-            return -1;
-        }
-        buf += n;
-        len -= n;
-    }
-
-    D("[ done fd=%d ]", h->bulk_in);
-    return 0;
-}
-
-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);
-    while (len > 0) {
-        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));
-            return -1;
-        }
-        buf += n;
-        len -= n;
-    }
-
-    D("[ done fd=%d ]", h->bulk_out);
-    return 0;
-}
-
-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 / USB_FFS_BULK_SIZE + (len % USB_FFS_BULK_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, USB_FFS_BULK_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 = true;
-        }
-    }
-    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;
         }
-        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;
-            }
-        }
-        return 0;
+
+        atransport* transport = new atransport();
+        transport->serial = "UsbFfs";
+        std::promise<void> destruction_notifier;
+        std::future<void> future = destruction_notifier.get_future();
+        transport->SetConnection(std::make_unique<UsbFfsConnection>(
+                std::move(control), std::move(bulk_out), std::move(bulk_in),
+                std::move(destruction_notifier)));
+        register_transport(transport);
+        future.wait();
     }
 }
 
-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) {
-    int err;
-
-    err = ioctl(h->bulk_in, FUNCTIONFS_CLEAR_HALT);
-    if (err < 0) {
-        D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in, errno);
-    }
-
-    err = ioctl(h->bulk_out, FUNCTIONFS_CLEAR_HALT);
-    if (err < 0) {
-        D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out, errno);
-    }
-
-    // don't close ep0 here, since we may not need to reinitialize it with
-    // the same descriptors again. if however ep1/ep2 fail to re-open in
-    // init_functionfs, only then would we close and open ep0 again.
-    // Ditto the comment in usb_adb_kick.
-    h->kicked = true;
-    TEMP_FAILURE_RETRY(dup2(dummy_fd, h->bulk_out));
-    TEMP_FAILURE_RETRY(dup2(dummy_fd, h->bulk_in));
-}
-
-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);
-    io_destroy(h->read_aiob.ctx);
-    io_destroy(h->write_aiob.ctx);
-
-    // Notify usb_adb_open_thread to open a new connection.
-    h->lock.lock();
-    h->open_new_connection = true;
-    h->lock.unlock();
-    h->notify.notify_one();
-}
-
-static void usb_ffs_init() {
-    D("[ usb_init - using FunctionFS ]");
-
-    usb_handle* h = new usb_handle();
-
-    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);
-        aio_block_init(&h->write_aiob);
-    }
-    h->kick = usb_ffs_kick;
-    h->close = usb_ffs_close;
-
-    D("[ usb_init - starting thread ]");
-    std::thread(usb_ffs_open_thread, h).detach();
-}
-
 void usb_init() {
-    dummy_fd = adb_open("/dev/null", O_WRONLY);
-    CHECK_NE(dummy_fd, -1);
-    usb_ffs_init();
-}
+    bool use_nonblocking = android::base::GetBoolProperty(
+            "persist.adb.nonblocking_ffs",
+            android::base::GetBoolProperty("ro.adb.nonblocking_ffs", true));
 
-int usb_write(usb_handle* h, const void* data, int len) {
-    return h->write(h, data, len);
-}
-
-int usb_read(usb_handle* h, void* data, int len) {
-    return h->read(h, data, len);
-}
-
-int usb_close(usb_handle* h) {
-    h->close(h);
-    return 0;
-}
-
-void usb_kick(usb_handle* h) {
-    h->kick(h);
+    if (use_nonblocking) {
+        std::thread(usb_ffs_open_thread).detach();
+    } else {
+        usb_init_legacy();
+    }
 }
diff --git a/adb/daemon/usb.h b/adb/daemon/usb.h
deleted file mode 100644
index 15a7f65..0000000
--- a/adb/daemon/usb.h
+++ /dev/null
@@ -1,58 +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>
-
-#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;
-};
-
diff --git a/adb/daemon/usb_dummy.cpp b/adb/daemon/usb_dummy.cpp
new file mode 100644
index 0000000..c9bf797
--- /dev/null
+++ b/adb/daemon/usb_dummy.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <adbd/usb.h>
+
+#include <android-base/logging.h>
+
+int usb_write(usb_handle*, const void*, int) {
+    LOG(FATAL) << "unimplemented";
+    return -1;
+}
+
+int usb_read(usb_handle*, void*, int) {
+    LOG(FATAL) << "unimplemented";
+    return -1;
+}
+
+int usb_close(usb_handle*) {
+    LOG(FATAL) << "unimplemented";
+    return -1;
+}
+
+void usb_reset(usb_handle*) {
+    LOG(FATAL) << "unimplemented";
+}
+
+void usb_kick(usb_handle*) {
+    LOG(FATAL) << "unimplemented";
+}
diff --git a/adb/daemon/usb_ffs.cpp b/adb/daemon/usb_ffs.cpp
new file mode 100644
index 0000000..a64ce40
--- /dev/null
+++ b/adb/daemon/usb_ffs.cpp
@@ -0,0 +1,320 @@
+/*
+ * 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 USB
+
+#include "sysdeps.h"
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/functionfs.h>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+
+#include "adb.h"
+#include "adbd/usb.h"
+
+#define MAX_PACKET_SIZE_FS 64
+#define MAX_PACKET_SIZE_HS 512
+#define MAX_PACKET_SIZE_SS 1024
+
+#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 USB_EXT_PROP_UNICODE 1
+
+#define cpu_to_le16(x) htole16(x)
+#define cpu_to_le32(x) htole32(x)
+
+// clang-format off
+struct func_desc {
+    struct usb_interface_descriptor intf;
+    struct usb_endpoint_descriptor_no_audio source;
+    struct usb_endpoint_descriptor_no_audio sink;
+} __attribute__((packed));
+
+struct ss_func_desc {
+    struct usb_interface_descriptor intf;
+    struct usb_endpoint_descriptor_no_audio source;
+    struct usb_ss_ep_comp_descriptor source_comp;
+    struct usb_endpoint_descriptor_no_audio sink;
+    struct usb_ss_ep_comp_descriptor sink_comp;
+} __attribute__((packed));
+
+struct desc_v1 {
+    struct usb_functionfs_descs_head_v1 {
+        __le32 magic;
+        __le32 length;
+        __le32 fs_count;
+        __le32 hs_count;
+    } __attribute__((packed)) header;
+    struct func_desc fs_descs, hs_descs;
+} __attribute__((packed));
+
+template <size_t PropertyNameLength, size_t PropertyDataLength>
+struct usb_os_desc_ext_prop {
+    uint32_t dwSize = sizeof(*this);
+    uint32_t dwPropertyDataType = cpu_to_le32(USB_EXT_PROP_UNICODE);
+
+    // Property name and value are transmitted as UTF-16, but the kernel only
+    // accepts ASCII values and performs the conversion for us.
+    uint16_t wPropertyNameLength = cpu_to_le16(PropertyNameLength);
+    char bPropertyName[PropertyNameLength];
+
+    uint32_t dwPropertyDataLength = cpu_to_le32(PropertyDataLength);
+    char bProperty[PropertyDataLength];
+} __attribute__((packed));
+
+using usb_os_desc_guid_t = usb_os_desc_ext_prop<20, 39>;
+usb_os_desc_guid_t os_desc_guid = {
+    .bPropertyName = "DeviceInterfaceGUID",
+    .bProperty = "{64379D6C-D531-4BED-BBEC-5A16FC07D6BC}",
+};
+
+struct usb_ext_prop_values {
+    usb_os_desc_guid_t guid;
+} __attribute__((packed));
+
+usb_ext_prop_values os_prop_values = {
+    .guid = os_desc_guid,
+};
+
+struct desc_v2 {
+    struct usb_functionfs_descs_head_v2 header;
+    // The rest of the structure depends on the flags in the header.
+    __le32 fs_count;
+    __le32 hs_count;
+    __le32 ss_count;
+    __le32 os_count;
+    struct func_desc fs_descs, hs_descs;
+    struct ss_func_desc ss_descs;
+    struct usb_os_desc_header os_header;
+    struct usb_ext_compat_desc os_desc;
+    struct usb_os_desc_header os_prop_header;
+    struct usb_ext_prop_values os_prop_values;
+} __attribute__((packed));
+
+static struct func_desc fs_descriptors = {
+    .intf = {
+        .bLength = sizeof(fs_descriptors.intf),
+        .bDescriptorType = USB_DT_INTERFACE,
+        .bInterfaceNumber = 0,
+        .bNumEndpoints = 2,
+        .bInterfaceClass = ADB_CLASS,
+        .bInterfaceSubClass = ADB_SUBCLASS,
+        .bInterfaceProtocol = ADB_PROTOCOL,
+        .iInterface = 1, /* first string from the provided table */
+    },
+    .source = {
+        .bLength = sizeof(fs_descriptors.source),
+        .bDescriptorType = USB_DT_ENDPOINT,
+        .bEndpointAddress = 1 | USB_DIR_OUT,
+        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+        .wMaxPacketSize = MAX_PACKET_SIZE_FS,
+    },
+    .sink = {
+        .bLength = sizeof(fs_descriptors.sink),
+        .bDescriptorType = USB_DT_ENDPOINT,
+        .bEndpointAddress = 2 | USB_DIR_IN,
+        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+        .wMaxPacketSize = MAX_PACKET_SIZE_FS,
+    },
+};
+
+static struct func_desc hs_descriptors = {
+    .intf = {
+        .bLength = sizeof(hs_descriptors.intf),
+        .bDescriptorType = USB_DT_INTERFACE,
+        .bInterfaceNumber = 0,
+        .bNumEndpoints = 2,
+        .bInterfaceClass = ADB_CLASS,
+        .bInterfaceSubClass = ADB_SUBCLASS,
+        .bInterfaceProtocol = ADB_PROTOCOL,
+        .iInterface = 1, /* first string from the provided table */
+    },
+    .source = {
+        .bLength = sizeof(hs_descriptors.source),
+        .bDescriptorType = USB_DT_ENDPOINT,
+        .bEndpointAddress = 1 | USB_DIR_OUT,
+        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+        .wMaxPacketSize = MAX_PACKET_SIZE_HS,
+    },
+    .sink = {
+        .bLength = sizeof(hs_descriptors.sink),
+        .bDescriptorType = USB_DT_ENDPOINT,
+        .bEndpointAddress = 2 | USB_DIR_IN,
+        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+        .wMaxPacketSize = MAX_PACKET_SIZE_HS,
+    },
+};
+
+static struct ss_func_desc ss_descriptors = {
+    .intf = {
+        .bLength = sizeof(ss_descriptors.intf),
+        .bDescriptorType = USB_DT_INTERFACE,
+        .bInterfaceNumber = 0,
+        .bNumEndpoints = 2,
+        .bInterfaceClass = ADB_CLASS,
+        .bInterfaceSubClass = ADB_SUBCLASS,
+        .bInterfaceProtocol = ADB_PROTOCOL,
+        .iInterface = 1, /* first string from the provided table */
+    },
+    .source = {
+        .bLength = sizeof(ss_descriptors.source),
+        .bDescriptorType = USB_DT_ENDPOINT,
+        .bEndpointAddress = 1 | USB_DIR_OUT,
+        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+        .wMaxPacketSize = MAX_PACKET_SIZE_SS,
+    },
+    .source_comp = {
+        .bLength = sizeof(ss_descriptors.source_comp),
+        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+        .bMaxBurst = 4,
+    },
+    .sink = {
+        .bLength = sizeof(ss_descriptors.sink),
+        .bDescriptorType = USB_DT_ENDPOINT,
+        .bEndpointAddress = 2 | USB_DIR_IN,
+        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+        .wMaxPacketSize = MAX_PACKET_SIZE_SS,
+    },
+    .sink_comp = {
+        .bLength = sizeof(ss_descriptors.sink_comp),
+        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+        .bMaxBurst = 4,
+    },
+};
+
+struct usb_ext_compat_desc os_desc_compat = {
+    .bFirstInterfaceNumber = 0,
+    .Reserved1 = cpu_to_le32(1),
+    .CompatibleID = { 'W', 'I', 'N', 'U', 'S', 'B', '\0', '\0'},
+    .SubCompatibleID = {0},
+    .Reserved2 = {0},
+};
+
+static struct usb_os_desc_header os_desc_header = {
+    .interface = cpu_to_le32(0),
+    .dwLength = cpu_to_le32(sizeof(os_desc_header) + sizeof(os_desc_compat)),
+    .bcdVersion = cpu_to_le32(1),
+    .wIndex = cpu_to_le32(4),
+    .bCount = cpu_to_le32(1),
+    .Reserved = cpu_to_le32(0),
+};
+
+static struct usb_os_desc_header os_prop_header = {
+    .interface = cpu_to_le32(0),
+    .dwLength = cpu_to_le32(sizeof(os_desc_header) + sizeof(os_prop_values)),
+    .bcdVersion = cpu_to_le32(1),
+    .wIndex = cpu_to_le32(5),
+    .wCount = cpu_to_le16(1),
+};
+
+#define STR_INTERFACE_ "ADB Interface"
+
+static const struct {
+    struct usb_functionfs_strings_head header;
+    struct {
+        __le16 code;
+        const char str1[sizeof(STR_INTERFACE_)];
+    } __attribute__((packed)) lang0;
+} __attribute__((packed)) strings = {
+    .header = {
+        .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
+        .length = cpu_to_le32(sizeof(strings)),
+        .str_count = cpu_to_le32(1),
+        .lang_count = cpu_to_le32(1),
+    },
+    .lang0 = {
+        cpu_to_le16(0x0409), /* en-us */
+        STR_INTERFACE_,
+    },
+};
+// clang-format on
+
+bool open_functionfs(android::base::unique_fd* out_control, android::base::unique_fd* out_bulk_out,
+                     android::base::unique_fd* out_bulk_in) {
+    unique_fd control, bulk_out, bulk_in;
+    struct desc_v1 v1_descriptor = {};
+    struct desc_v2 v2_descriptor = {};
+
+    v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
+    v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
+    v2_descriptor.header.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
+                                 FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC;
+    v2_descriptor.fs_count = 3;
+    v2_descriptor.hs_count = 3;
+    v2_descriptor.ss_count = 5;
+    v2_descriptor.os_count = 2;
+    v2_descriptor.fs_descs = fs_descriptors;
+    v2_descriptor.hs_descs = hs_descriptors;
+    v2_descriptor.ss_descs = ss_descriptors;
+    v2_descriptor.os_header = os_desc_header;
+    v2_descriptor.os_desc = os_desc_compat;
+    v2_descriptor.os_prop_header = os_prop_header;
+    v2_descriptor.os_prop_values = os_prop_values;
+
+    if (out_control->get() < 0) {  // might have already done this before
+        LOG(INFO) << "opening control endpoint " << USB_FFS_ADB_EP0;
+        control.reset(adb_open(USB_FFS_ADB_EP0, O_RDWR));
+        if (control < 0) {
+            PLOG(ERROR) << "cannot open control endpoint " << USB_FFS_ADB_EP0;
+            return false;
+        }
+
+        if (adb_write(control.get(), &v2_descriptor, sizeof(v2_descriptor)) < 0) {
+            D("[ %s: Switching to V1_descriptor format errno=%s ]", USB_FFS_ADB_EP0,
+              strerror(errno));
+            v1_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC);
+            v1_descriptor.header.length = cpu_to_le32(sizeof(v1_descriptor));
+            v1_descriptor.header.fs_count = 3;
+            v1_descriptor.header.hs_count = 3;
+            v1_descriptor.fs_descs = fs_descriptors;
+            v1_descriptor.hs_descs = hs_descriptors;
+            if (adb_write(control.get(), &v1_descriptor, sizeof(v1_descriptor)) < 0) {
+                PLOG(ERROR) << "failed to write USB descriptors";
+                return false;
+            }
+        }
+
+        if (adb_write(control.get(), &strings, sizeof(strings)) < 0) {
+            PLOG(ERROR) << "failed to write USB strings";
+            return false;
+        }
+        // Signal only when writing the descriptors to ffs
+        android::base::SetProperty("sys.usb.ffs.ready", "1");
+        *out_control = std::move(control);
+    }
+
+    bulk_out.reset(adb_open(USB_FFS_ADB_OUT, O_RDONLY));
+    if (bulk_out < 0) {
+        PLOG(ERROR) << "cannot open bulk-out endpoint " << USB_FFS_ADB_OUT;
+        return false;
+    }
+
+    bulk_in.reset(adb_open(USB_FFS_ADB_IN, O_WRONLY));
+    if (bulk_in < 0) {
+        PLOG(ERROR) << "cannot open bulk-in endpoint " << USB_FFS_ADB_IN;
+        return false;
+    }
+
+    *out_bulk_in = std::move(bulk_in);
+    *out_bulk_out = std::move(bulk_out);
+    return true;
+}
diff --git a/adb/daemon/usb_legacy.cpp b/adb/daemon/usb_legacy.cpp
new file mode 100644
index 0000000..fe80e7d
--- /dev/null
+++ b/adb/daemon/usb_legacy.cpp
@@ -0,0 +1,327 @@
+/*
+ * 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 USB
+
+#include "sysdeps.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/functionfs.h>
+
+#include <algorithm>
+#include <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+#include "adb.h"
+#include "adbd/usb.h"
+#include "transport.h"
+
+using namespace std::chrono_literals;
+
+#define MAX_PACKET_SIZE_FS 64
+#define MAX_PACKET_SIZE_HS 512
+#define MAX_PACKET_SIZE_SS 1024
+
+#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)
+
+static unique_fd& dummy_fd = *new unique_fd();
+
+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";
+    if (!open_functionfs(&h->control, &h->bulk_out, &h->bulk_in)) {
+        return false;
+    }
+
+    h->read_aiob.fd = h->bulk_out.get();
+    h->write_aiob.fd = h->bulk_in.get();
+    h->reads_zero_packets = true;
+    return true;
+}
+
+static void usb_legacy_ffs_open_thread(usb_handle* usb) {
+    adb_thread_setname("usb legacy ffs open");
+
+    while (true) {
+        // wait until the USB device needs opening
+        std::unique_lock<std::mutex> lock(usb->lock);
+        while (!usb->open_new_connection) {
+            usb->notify.wait(lock);
+        }
+        usb->open_new_connection = false;
+        lock.unlock();
+
+        while (true) {
+            if (init_functionfs(usb)) {
+                LOG(INFO) << "functionfs successfully initialized";
+                break;
+            }
+            std::this_thread::sleep_for(1s);
+        }
+
+        LOG(INFO) << "registering usb transport";
+        register_usb_transport(usb, nullptr, nullptr, 1);
+    }
+
+    // never gets here
+    abort();
+}
+
+static int usb_ffs_write(usb_handle* h, const void* data, int len) {
+    D("about to write (fd=%d, len=%d)", h->bulk_in.get(), len);
+
+    const char* buf = static_cast<const char*>(data);
+    int orig_len = len;
+    while (len > 0) {
+        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.get(), n, strerror(errno));
+            return -1;
+        }
+        buf += n;
+        len -= n;
+    }
+
+    D("[ done fd=%d ]", h->bulk_in.get());
+    return orig_len;
+}
+
+static int usb_ffs_read(usb_handle* h, void* data, int len, bool allow_partial) {
+    D("about to read (fd=%d, len=%d)", h->bulk_out.get(), len);
+
+    char* buf = static_cast<char*>(data);
+    int orig_len = len;
+    unsigned count = 0;
+    while (len > 0) {
+        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.get(), n, strerror(errno));
+            return -1;
+        }
+        buf += n;
+        len -= n;
+        count += n;
+
+        // For fastbootd command such as "getvar all", len parameter is always set 64.
+        // But what we read is actually less than 64.
+        // For example, length 10 for "getvar all" command.
+        // If we get less data than expected, this means there should be no more data.
+        if (allow_partial && n < read_len) {
+            orig_len = count;
+            break;
+        }
+    }
+
+    D("[ done fd=%d ]", h->bulk_out.get());
+    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, bool allow_partial) {
+    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) {
+    int err;
+
+    err = ioctl(h->bulk_in.get(), FUNCTIONFS_CLEAR_HALT);
+    if (err < 0) {
+        D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in.get(), errno);
+    }
+
+    err = ioctl(h->bulk_out.get(), FUNCTIONFS_CLEAR_HALT);
+    if (err < 0) {
+        D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out.get(), errno);
+    }
+
+    // don't close ep0 here, since we may not need to reinitialize it with
+    // the same descriptors again. if however ep1/ep2 fail to re-open in
+    // init_functionfs, only then would we close and open ep0 again.
+    // Ditto the comment in usb_adb_kick.
+    h->kicked = true;
+    TEMP_FAILURE_RETRY(dup2(dummy_fd.get(), h->bulk_out.get()));
+    TEMP_FAILURE_RETRY(dup2(dummy_fd.get(), h->bulk_in.get()));
+}
+
+static void usb_ffs_close(usb_handle* h) {
+    LOG(INFO) << "closing functionfs transport";
+
+    h->kicked = false;
+    h->bulk_out.reset();
+    h->bulk_in.reset();
+
+    // Notify usb_adb_open_thread to open a new connection.
+    h->lock.lock();
+    h->open_new_connection = true;
+    h->lock.unlock();
+    h->notify.notify_one();
+}
+
+usb_handle* create_usb_handle(unsigned num_bufs, unsigned io_size) {
+    usb_handle* h = new usb_handle();
+
+    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;
+    return h;
+}
+
+void usb_init_legacy() {
+    D("[ usb_init - using legacy FunctionFS ]");
+    dummy_fd.reset(adb_open("/dev/null", O_WRONLY | O_CLOEXEC));
+    CHECK_NE(-1, dummy_fd.get());
+
+    std::thread(usb_legacy_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) {
+    return h->write(h, data, len);
+}
+
+int usb_read(usb_handle* h, void* data, int len) {
+    return h->read(h, data, len, false /* allow_partial */);
+}
+
+int usb_close(usb_handle* h) {
+    h->close(h);
+    return 0;
+}
+
+void usb_reset(usb_handle* h) {
+    usb_close(h);
+}
+
+void usb_kick(usb_handle* h) {
+    h->kick(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/fastdeploy/Android.bp b/adb/fastdeploy/Android.bp
new file mode 100644
index 0000000..1ba0de0
--- /dev/null
+++ b/adb/fastdeploy/Android.bp
@@ -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.
+//
+
+java_binary {
+    name: "deployagent",
+    sdk_version: "24",
+    srcs: ["deployagent/src/**/*.java", "deploylib/src/**/*.java", "proto/**/*.proto"],
+    static_libs: ["apkzlib_zip"],
+    wrapper: "deployagent/deployagent.sh",
+    proto: {
+        type: "lite",
+    },
+    dex_preopt: {
+        enabled: false,
+    }
+}
+
+java_binary_host {
+    name: "deploypatchgenerator",
+    srcs: ["deploypatchgenerator/src/**/*.java", "deploylib/src/**/*.java", "proto/**/*.proto"],
+    static_libs: ["apkzlib"],
+    manifest: "deploypatchgenerator/manifest.txt",
+    proto: {
+        type: "full",
+    }
+}
diff --git a/adb/fastdeploy/OWNERS b/adb/fastdeploy/OWNERS
new file mode 100644
index 0000000..d145834
--- /dev/null
+++ b/adb/fastdeploy/OWNERS
@@ -0,0 +1 @@
+idries@google.com
diff --git a/adb/fastdeploy/deployagent/deployagent.sh b/adb/fastdeploy/deployagent/deployagent.sh
new file mode 100755
index 0000000..91576ca
--- /dev/null
+++ b/adb/fastdeploy/deployagent/deployagent.sh
@@ -0,0 +1,4 @@
+#!/system/bin/sh
+base=/data/local/tmp
+export CLASSPATH=$base/deployagent.jar
+exec app_process $base com.android.fastdeploy.DeployAgent "$@"
diff --git a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
new file mode 100644
index 0000000..2d3b135
--- /dev/null
+++ b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
@@ -0,0 +1,321 @@
+/*
+ * 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 com.android.fastdeploy;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.TimeUnit;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.util.Set;
+
+import com.android.fastdeploy.APKMetaData;
+import com.android.fastdeploy.PatchUtils;
+
+public final class DeployAgent {
+    private static final int BUFFER_SIZE = 128 * 1024;
+    private static final int AGENT_VERSION = 0x00000002;
+
+    public static void main(String[] args) {
+        int exitCode = 0;
+        try {
+            if (args.length < 1) {
+                showUsage(0);
+            }
+
+            String commandString = args[0];
+
+            if (commandString.equals("extract")) {
+                if (args.length != 2) {
+                    showUsage(1);
+                }
+
+                String packageName = args[1];
+                extractMetaData(packageName);
+            } else if (commandString.equals("find")) {
+                if (args.length != 2) {
+                    showUsage(1);
+                }
+
+                String packageName = args[1];
+                if (getFilenameFromPackageName(packageName) == null) {
+                    exitCode = 3;
+                }
+            } else if (commandString.equals("apply")) {
+                if (args.length < 4) {
+                    showUsage(1);
+                }
+
+                String packageName = args[1];
+                String patchPath = args[2];
+                String outputParam = args[3];
+
+                InputStream deltaInputStream = null;
+                if (patchPath.compareTo("-") == 0) {
+                    deltaInputStream = System.in;
+                } else {
+                    deltaInputStream = new FileInputStream(patchPath);
+                }
+
+                if (outputParam.equals("-o")) {
+                    OutputStream outputStream = null;
+                    if (args.length > 4) {
+                        String outputPath = args[4];
+                        if (!outputPath.equals("-")) {
+                            outputStream = new FileOutputStream(outputPath);
+                        }
+                    }
+                    if (outputStream == null) {
+                        outputStream = System.out;
+                    }
+                    File deviceFile = getFileFromPackageName(packageName);
+                    writePatchToStream(
+                            new RandomAccessFile(deviceFile, "r"), deltaInputStream, outputStream);
+                } else if (outputParam.equals("-pm")) {
+                    String[] sessionArgs = null;
+                    if (args.length > 4) {
+                        int numSessionArgs = args.length-4;
+                        sessionArgs = new String[numSessionArgs];
+                        for (int i=0 ; i<numSessionArgs ; i++) {
+                            sessionArgs[i] = args[i+4];
+                        }
+                    }
+                    exitCode = applyPatch(packageName, deltaInputStream, sessionArgs);
+                }
+            } else if (commandString.equals("version")) {
+                System.out.printf("0x%08X\n", AGENT_VERSION);
+            } else {
+                showUsage(1);
+            }
+        } catch (Exception e) {
+            System.err.println("Error: " + e);
+            e.printStackTrace();
+            System.exit(2);
+        }
+        System.exit(exitCode);
+    }
+
+    private static void showUsage(int exitCode) {
+        System.err.println(
+            "usage: deployagent <command> [<args>]\n\n" +
+            "commands:\n" +
+            "version                             get the version\n" +
+            "find PKGNAME                        return zero if package found, else non-zero\n" +
+            "extract PKGNAME                     extract an installed package's metadata\n" +
+            "apply PKGNAME PATCHFILE [-o|-pm]    apply a patch from PATCHFILE (- for stdin) to an installed package\n" +
+            " -o <FILE> directs output to FILE, default or - for stdout\n" +
+            " -pm <ARGS> directs output to package manager, passes <ARGS> to 'pm install-create'\n"
+            );
+
+        System.exit(exitCode);
+    }
+
+    private static Process executeCommand(String command) throws IOException {
+        try {
+            Process p;
+            p = Runtime.getRuntime().exec(command);
+            p.waitFor();
+            return p;
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    }
+
+    private static String getFilenameFromPackageName(String packageName) throws IOException {
+        StringBuilder commandBuilder = new StringBuilder();
+        commandBuilder.append("pm list packages -f " + packageName);
+
+        Process p = executeCommand(commandBuilder.toString());
+        BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
+
+        String packagePrefix = "package:";
+        String packageSuffix = "=" + packageName;
+        String line = "";
+        while ((line = reader.readLine()) != null) {
+            if (line.endsWith(packageSuffix)) {
+                int packageIndex = line.indexOf(packagePrefix);
+                if (packageIndex == -1) {
+                    throw new IOException("error reading package list");
+                }
+                int equalsIndex = line.lastIndexOf(packageSuffix);
+                String fileName =
+                    line.substring(packageIndex + packagePrefix.length(), equalsIndex);
+                return fileName;
+            }
+        }
+        return null;
+    }
+
+    private static File getFileFromPackageName(String packageName) throws IOException {
+        String filename = getFilenameFromPackageName(packageName);
+        if (filename == null) {
+            // Should not happen (function is only called when we know the package exists)
+            throw new IOException("package not found");
+        }
+        return new File(filename);
+    }
+
+    private static void extractMetaData(String packageName) throws IOException {
+        File apkFile = getFileFromPackageName(packageName);
+        APKMetaData apkMetaData = PatchUtils.getAPKMetaData(apkFile);
+        apkMetaData.writeDelimitedTo(System.out);
+    }
+
+    private static int createInstallSession(String[] args) throws IOException {
+        StringBuilder commandBuilder = new StringBuilder();
+        commandBuilder.append("pm install-create ");
+        for (int i=0 ; args != null && i<args.length ; i++) {
+            commandBuilder.append(args[i] + " ");
+        }
+
+        Process p = executeCommand(commandBuilder.toString());
+
+        BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
+        String line = "";
+        String successLineStart = "Success: created install session [";
+        String successLineEnd = "]";
+        while ((line = reader.readLine()) != null) {
+            if (line.startsWith(successLineStart) && line.endsWith(successLineEnd)) {
+                return Integer.parseInt(line.substring(successLineStart.length(), line.lastIndexOf(successLineEnd)));
+            }
+        }
+
+        return -1;
+    }
+
+    private static int commitInstallSession(int sessionId) throws IOException {
+        StringBuilder commandBuilder = new StringBuilder();
+        commandBuilder.append(String.format("pm install-commit %d -- - ", sessionId));
+        Process p = executeCommand(commandBuilder.toString());
+        return p.exitValue();
+    }
+
+    private static int applyPatch(String packageName, InputStream deltaStream, String[] sessionArgs)
+            throws IOException, PatchFormatException {
+        File deviceFile = getFileFromPackageName(packageName);
+        int sessionId = createInstallSession(sessionArgs);
+        if (sessionId < 0) {
+            System.err.println("PM Create Session Failed");
+            return -1;
+        }
+
+        int writeExitCode = writePatchedDataToSession(new RandomAccessFile(deviceFile, "r"), deltaStream, sessionId);
+
+        if (writeExitCode == 0) {
+            return commitInstallSession(sessionId);
+        } else {
+            return -1;
+        }
+    }
+
+    private static long writePatchToStream(RandomAccessFile oldData, InputStream patchData,
+        OutputStream outputStream) throws IOException, PatchFormatException {
+        long newSize = readPatchHeader(patchData);
+        long bytesWritten = writePatchedDataToStream(oldData, newSize, patchData, outputStream);
+        outputStream.flush();
+        if (bytesWritten != newSize) {
+            throw new PatchFormatException(String.format(
+                "output size mismatch (expected %ld but wrote %ld)", newSize, bytesWritten));
+        }
+        return bytesWritten;
+    }
+
+    private static long readPatchHeader(InputStream patchData)
+        throws IOException, PatchFormatException {
+        byte[] signatureBuffer = new byte[PatchUtils.SIGNATURE.length()];
+        try {
+            PatchUtils.readFully(patchData, signatureBuffer, 0, signatureBuffer.length);
+        } catch (IOException e) {
+            throw new PatchFormatException("truncated signature");
+        }
+
+        String signature = new String(signatureBuffer, 0, signatureBuffer.length, "US-ASCII");
+        if (!PatchUtils.SIGNATURE.equals(signature)) {
+            throw new PatchFormatException("bad signature");
+        }
+
+        long newSize = PatchUtils.readBsdiffLong(patchData);
+        if (newSize < 0 || newSize > Integer.MAX_VALUE) {
+            throw new PatchFormatException("bad newSize");
+        }
+
+        return newSize;
+    }
+
+    // Note that this function assumes patchData has been seek'ed to the start of the delta stream
+    // (i.e. the signature has already been read by readPatchHeader). For a stream that points to the
+    // start of a patch file call writePatchToStream
+    private static long writePatchedDataToStream(RandomAccessFile oldData, long newSize,
+        InputStream patchData, OutputStream outputStream) throws IOException {
+        long newDataBytesWritten = 0;
+        byte[] buffer = new byte[BUFFER_SIZE];
+
+        while (newDataBytesWritten < newSize) {
+            long copyLen = PatchUtils.readFormattedLong(patchData);
+            if (copyLen > 0) {
+                PatchUtils.pipe(patchData, outputStream, buffer, (int) copyLen);
+            }
+
+            long oldDataOffset = PatchUtils.readFormattedLong(patchData);
+            long oldDataLen = PatchUtils.readFormattedLong(patchData);
+            oldData.seek(oldDataOffset);
+            if (oldDataLen > 0) {
+                PatchUtils.pipe(oldData, outputStream, buffer, (int) oldDataLen);
+            }
+
+            newDataBytesWritten += copyLen + oldDataLen;
+        }
+
+        return newDataBytesWritten;
+    }
+
+    private static int writePatchedDataToSession(RandomAccessFile oldData, InputStream patchData, int sessionId)
+            throws IOException, PatchFormatException {
+        try {
+            Process p;
+            long newSize = readPatchHeader(patchData);
+            StringBuilder commandBuilder = new StringBuilder();
+            commandBuilder.append(String.format("pm install-write -S %d %d -- -", newSize, sessionId));
+
+            String command = commandBuilder.toString();
+            p = Runtime.getRuntime().exec(command);
+
+            OutputStream sessionOutputStream = p.getOutputStream();
+            long bytesWritten = writePatchedDataToStream(oldData, newSize, patchData, sessionOutputStream);
+            sessionOutputStream.flush();
+            p.waitFor();
+            if (bytesWritten != newSize) {
+                throw new PatchFormatException(
+                        String.format("output size mismatch (expected %d but wrote %)", newSize, bytesWritten));
+            }
+            return p.exitValue();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+
+        return -1;
+    }
+}
diff --git a/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchFormatException.java b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchFormatException.java
new file mode 100644
index 0000000..f0655f3
--- /dev/null
+++ b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchFormatException.java
@@ -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.
+ */
+
+package com.android.fastdeploy;
+
+class PatchFormatException extends Exception {
+    /**
+     * Constructs a new exception with the specified message.
+     * @param message the message
+     */
+    public PatchFormatException(String message) { super(message); }
+
+    /**
+     * Constructs a new exception with the specified message and cause.
+     * @param message the message
+     * @param cause the cause of the error
+     */
+    public PatchFormatException(String message, Throwable cause) {
+        super(message);
+        initCause(cause);
+    }
+}
diff --git a/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java
new file mode 100644
index 0000000..f0f00e1
--- /dev/null
+++ b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java
@@ -0,0 +1,186 @@
+/*
+ * 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 com.android.fastdeploy;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+import com.android.tools.build.apkzlib.zip.ZFile;
+import com.android.tools.build.apkzlib.zip.ZFileOptions;
+import com.android.tools.build.apkzlib.zip.StoredEntry;
+import com.android.tools.build.apkzlib.zip.StoredEntryType;
+import com.android.tools.build.apkzlib.zip.CentralDirectoryHeaderCompressInfo;
+import com.android.tools.build.apkzlib.zip.CentralDirectoryHeader;
+
+import com.android.fastdeploy.APKMetaData;
+import com.android.fastdeploy.APKEntry;
+
+class PatchUtils {
+    private static final long NEGATIVE_MASK = 1L << 63;
+    private static final long NEGATIVE_LONG_SIGN_MASK = 1L << 63;
+    public static final String SIGNATURE = "HAMADI/IHD";
+
+    private static long getOffsetFromEntry(StoredEntry entry) {
+        return entry.getCentralDirectoryHeader().getOffset() + entry.getLocalHeaderSize();
+    }
+
+    public static APKMetaData getAPKMetaData(File apkFile) throws IOException {
+        APKMetaData.Builder apkEntriesBuilder = APKMetaData.newBuilder();
+        ZFileOptions options = new ZFileOptions();
+        ZFile zFile = new ZFile(apkFile, options);
+
+        ArrayList<StoredEntry> metaDataEntries = new ArrayList<StoredEntry>();
+
+        for (StoredEntry entry : zFile.entries()) {
+            if (entry.getType() != StoredEntryType.FILE) {
+                continue;
+            }
+            metaDataEntries.add(entry);
+        }
+
+        Collections.sort(metaDataEntries, new Comparator<StoredEntry>() {
+            private long getOffsetFromEntry(StoredEntry entry) {
+                return PatchUtils.getOffsetFromEntry(entry);
+            }
+
+            @Override
+            public int compare(StoredEntry lhs, StoredEntry rhs) {
+                // -1 - less than, 1 - greater than, 0 - equal, all inversed for descending
+                return Long.compare(getOffsetFromEntry(lhs), getOffsetFromEntry(rhs));
+            }
+        });
+
+        for (StoredEntry entry : metaDataEntries) {
+            CentralDirectoryHeader cdh = entry.getCentralDirectoryHeader();
+            CentralDirectoryHeaderCompressInfo cdhci = cdh.getCompressionInfoWithWait();
+
+            APKEntry.Builder entryBuilder = APKEntry.newBuilder();
+            entryBuilder.setCrc32(cdh.getCrc32());
+            entryBuilder.setFileName(cdh.getName());
+            entryBuilder.setCompressedSize(cdhci.getCompressedSize());
+            entryBuilder.setUncompressedSize(cdh.getUncompressedSize());
+            entryBuilder.setDataOffset(getOffsetFromEntry(entry));
+
+            apkEntriesBuilder.addEntries(entryBuilder);
+            apkEntriesBuilder.build();
+        }
+        return apkEntriesBuilder.build();
+    }
+
+    /**
+     * Writes a 64-bit signed integer to the specified {@link OutputStream}. The least significant
+     * byte is written first and the most significant byte is written last.
+     * @param value the value to write
+     * @param outputStream the stream to write to
+     */
+    static void writeFormattedLong(final long value, OutputStream outputStream) throws IOException {
+        long y = value;
+        if (y < 0) {
+            y = (-y) | NEGATIVE_MASK;
+        }
+
+        for (int i = 0; i < 8; ++i) {
+            outputStream.write((byte) (y & 0xff));
+            y >>>= 8;
+        }
+    }
+
+    /**
+     * Reads a 64-bit signed integer written by {@link #writeFormattedLong(long, OutputStream)} from
+     * the specified {@link InputStream}.
+     * @param inputStream the stream to read from
+     */
+    static long readFormattedLong(InputStream inputStream) throws IOException {
+        long result = 0;
+        for (int bitshift = 0; bitshift < 64; bitshift += 8) {
+            result |= ((long) inputStream.read()) << bitshift;
+        }
+
+        if ((result - NEGATIVE_MASK) > 0) {
+            result = (result & ~NEGATIVE_MASK) * -1;
+        }
+        return result;
+    }
+
+    static final long readBsdiffLong(InputStream in) throws PatchFormatException, IOException {
+        long result = 0;
+        for (int bitshift = 0; bitshift < 64; bitshift += 8) {
+            result |= ((long) in.read()) << bitshift;
+        }
+
+        if (result == NEGATIVE_LONG_SIGN_MASK) {
+            // "Negative zero", which is valid in signed-magnitude format.
+            // NB: No sane patch generator should ever produce such a value.
+            throw new PatchFormatException("read negative zero");
+        }
+
+        if ((result & NEGATIVE_LONG_SIGN_MASK) != 0) {
+            result = -(result & ~NEGATIVE_LONG_SIGN_MASK);
+        }
+
+        return result;
+    }
+
+    static void readFully(final InputStream in, final byte[] destination, final int startAt,
+        final int numBytes) throws IOException {
+        int numRead = 0;
+        while (numRead < numBytes) {
+            int readNow = in.read(destination, startAt + numRead, numBytes - numRead);
+            if (readNow == -1) {
+                throw new IOException("truncated input stream");
+            }
+            numRead += readNow;
+        }
+    }
+
+    static void pipe(final InputStream in, final OutputStream out, final byte[] buffer,
+        long copyLength) throws IOException {
+        while (copyLength > 0) {
+            int maxCopy = Math.min(buffer.length, (int) copyLength);
+            readFully(in, buffer, 0, maxCopy);
+            out.write(buffer, 0, maxCopy);
+            copyLength -= maxCopy;
+        }
+    }
+
+    static void pipe(final RandomAccessFile in, final OutputStream out, final byte[] buffer,
+        long copyLength) throws IOException {
+        while (copyLength > 0) {
+            int maxCopy = Math.min(buffer.length, (int) copyLength);
+            in.readFully(buffer, 0, maxCopy);
+            out.write(buffer, 0, maxCopy);
+            copyLength -= maxCopy;
+        }
+    }
+
+    static void fill(byte value, final OutputStream out, final byte[] buffer, long fillLength)
+        throws IOException {
+        while (fillLength > 0) {
+            int maxCopy = Math.min(buffer.length, (int) fillLength);
+            Arrays.fill(buffer, 0, maxCopy, value);
+            out.write(buffer, 0, maxCopy);
+            fillLength -= maxCopy;
+        }
+    }
+}
diff --git a/adb/fastdeploy/deploypatchgenerator/manifest.txt b/adb/fastdeploy/deploypatchgenerator/manifest.txt
new file mode 100644
index 0000000..5c00505
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/manifest.txt
@@ -0,0 +1 @@
+Main-Class: com.android.fastdeploy.DeployPatchGenerator
diff --git a/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java b/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java
new file mode 100644
index 0000000..24b2eab
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java
@@ -0,0 +1,208 @@
+/*
+ * 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 com.android.fastdeploy;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.StringBuilder;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.ArrayList;
+
+import java.nio.charset.StandardCharsets;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.AbstractMap.SimpleEntry;
+
+import com.android.fastdeploy.APKMetaData;
+import com.android.fastdeploy.APKEntry;
+
+public final class DeployPatchGenerator {
+    private static final int BUFFER_SIZE = 128 * 1024;
+
+    public static void main(String[] args) {
+        try {
+            if (args.length < 2) {
+                showUsage(0);
+            }
+
+            boolean verbose = false;
+            if (args.length > 2) {
+                String verboseFlag = args[2];
+                if (verboseFlag.compareTo("--verbose") == 0) {
+                    verbose = true;
+                }
+            }
+
+            StringBuilder sb = null;
+            String apkPath = args[0];
+            String deviceMetadataPath = args[1];
+            File hostFile = new File(apkPath);
+
+            List<APKEntry> deviceZipEntries = getMetadataFromFile(deviceMetadataPath);
+            System.err.println("Device Entries (" + deviceZipEntries.size() + ")");
+            if (verbose) {
+                sb = new StringBuilder();
+                for (APKEntry entry : deviceZipEntries) {
+                    APKEntryToString(entry, sb);
+                }
+                System.err.println(sb.toString());
+            }
+
+            List<APKEntry> hostFileEntries = PatchUtils.getAPKMetaData(hostFile).getEntriesList();
+            System.err.println("Host Entries (" + hostFileEntries.size() + ")");
+            if (verbose) {
+                sb = new StringBuilder();
+                for (APKEntry entry : hostFileEntries) {
+                    APKEntryToString(entry, sb);
+                }
+                System.err.println(sb.toString());
+            }
+
+            List<SimpleEntry<APKEntry, APKEntry>> identicalContentsEntrySet =
+                getIdenticalContents(deviceZipEntries, hostFileEntries);
+            reportIdenticalContents(identicalContentsEntrySet, hostFile);
+
+            if (verbose) {
+                sb = new StringBuilder();
+                for (SimpleEntry<APKEntry, APKEntry> identicalEntry : identicalContentsEntrySet) {
+                    APKEntry entry = identicalEntry.getValue();
+                    APKEntryToString(entry, sb);
+                }
+                System.err.println("Identical Entries (" + identicalContentsEntrySet.size() + ")");
+                System.err.println(sb.toString());
+            }
+
+            createPatch(identicalContentsEntrySet, hostFile, System.out);
+        } catch (Exception e) {
+            System.err.println("Error: " + e);
+            e.printStackTrace();
+            System.exit(2);
+        }
+        System.exit(0);
+    }
+
+    private static void showUsage(int exitCode) {
+        System.err.println("usage: deploypatchgenerator <apkpath> <deviceapkmetadata> [--verbose]");
+        System.err.println("");
+        System.exit(exitCode);
+    }
+
+    private static void APKEntryToString(APKEntry entry, StringBuilder outputString) {
+        outputString.append(String.format("Filename: %s\n", entry.getFileName()));
+        outputString.append(String.format("CRC32: 0x%08X\n", entry.getCrc32()));
+        outputString.append(String.format("Data Offset: %d\n", entry.getDataOffset()));
+        outputString.append(String.format("Compressed Size: %d\n", entry.getCompressedSize()));
+        outputString.append(String.format("Uncompressed Size: %d\n", entry.getUncompressedSize()));
+    }
+
+    private static List<APKEntry> getMetadataFromFile(String deviceMetadataPath) throws IOException {
+        InputStream is = new FileInputStream(new File(deviceMetadataPath));
+        APKMetaData apkMetaData = APKMetaData.parseDelimitedFrom(is);
+        return apkMetaData.getEntriesList();
+    }
+
+    private static List<SimpleEntry<APKEntry, APKEntry>> getIdenticalContents(
+        List<APKEntry> deviceZipEntries, List<APKEntry> hostZipEntries) throws IOException {
+        List<SimpleEntry<APKEntry, APKEntry>> identicalContents =
+            new ArrayList<SimpleEntry<APKEntry, APKEntry>>();
+
+        for (APKEntry deviceZipEntry : deviceZipEntries) {
+            for (APKEntry hostZipEntry : hostZipEntries) {
+                if (deviceZipEntry.getCrc32() == hostZipEntry.getCrc32() &&
+                    deviceZipEntry.getFileName().equals(hostZipEntry.getFileName())) {
+                    identicalContents.add(new SimpleEntry(deviceZipEntry, hostZipEntry));
+                }
+            }
+        }
+
+        Collections.sort(identicalContents, new Comparator<SimpleEntry<APKEntry, APKEntry>>() {
+            @Override
+            public int compare(
+                SimpleEntry<APKEntry, APKEntry> p1, SimpleEntry<APKEntry, APKEntry> p2) {
+                return Long.compare(p1.getValue().getDataOffset(), p2.getValue().getDataOffset());
+            }
+        });
+
+        return identicalContents;
+    }
+
+    private static void reportIdenticalContents(
+        List<SimpleEntry<APKEntry, APKEntry>> identicalContentsEntrySet, File hostFile)
+        throws IOException {
+        long totalEqualBytes = 0;
+        int totalEqualFiles = 0;
+        for (SimpleEntry<APKEntry, APKEntry> entries : identicalContentsEntrySet) {
+            APKEntry hostAPKEntry = entries.getValue();
+            totalEqualBytes += hostAPKEntry.getCompressedSize();
+            totalEqualFiles++;
+        }
+
+        float savingPercent = (float) (totalEqualBytes * 100) / hostFile.length();
+
+        System.err.println("Detected " + totalEqualFiles + " equal APK entries");
+        System.err.println(totalEqualBytes + " bytes are equal out of " + hostFile.length() + " ("
+            + savingPercent + "%)");
+    }
+
+    static void createPatch(List<SimpleEntry<APKEntry, APKEntry>> zipEntrySimpleEntrys,
+        File hostFile, OutputStream patchStream) throws IOException, PatchFormatException {
+        FileInputStream hostFileInputStream = new FileInputStream(hostFile);
+
+        patchStream.write(PatchUtils.SIGNATURE.getBytes(StandardCharsets.US_ASCII));
+        PatchUtils.writeFormattedLong(hostFile.length(), patchStream);
+
+        byte[] buffer = new byte[BUFFER_SIZE];
+        long totalBytesWritten = 0;
+        Iterator<SimpleEntry<APKEntry, APKEntry>> entrySimpleEntryIterator =
+            zipEntrySimpleEntrys.iterator();
+        while (entrySimpleEntryIterator.hasNext()) {
+            SimpleEntry<APKEntry, APKEntry> entrySimpleEntry = entrySimpleEntryIterator.next();
+            APKEntry deviceAPKEntry = entrySimpleEntry.getKey();
+            APKEntry hostAPKEntry = entrySimpleEntry.getValue();
+
+            long newDataLen = hostAPKEntry.getDataOffset() - totalBytesWritten;
+            long oldDataOffset = deviceAPKEntry.getDataOffset();
+            long oldDataLen = deviceAPKEntry.getCompressedSize();
+
+            PatchUtils.writeFormattedLong(newDataLen, patchStream);
+            PatchUtils.pipe(hostFileInputStream, patchStream, buffer, newDataLen);
+            PatchUtils.writeFormattedLong(oldDataOffset, patchStream);
+            PatchUtils.writeFormattedLong(oldDataLen, patchStream);
+
+            long skip = hostFileInputStream.skip(oldDataLen);
+            if (skip != oldDataLen) {
+                throw new PatchFormatException("skip error: attempted to skip " + oldDataLen
+                    + " bytes but return code was " + skip);
+            }
+            totalBytesWritten += oldDataLen + newDataLen;
+        }
+        long remainderLen = hostFile.length() - totalBytesWritten;
+        PatchUtils.writeFormattedLong(remainderLen, patchStream);
+        PatchUtils.pipe(hostFileInputStream, patchStream, buffer, remainderLen);
+        PatchUtils.writeFormattedLong(0, patchStream);
+        PatchUtils.writeFormattedLong(0, patchStream);
+        patchStream.flush();
+    }
+}
diff --git a/adb/fastdeploy/proto/ApkEntry.proto b/adb/fastdeploy/proto/ApkEntry.proto
new file mode 100644
index 0000000..9460d15
--- /dev/null
+++ b/adb/fastdeploy/proto/ApkEntry.proto
@@ -0,0 +1,18 @@
+syntax = "proto2";
+
+package com.android.fastdeploy;
+
+option java_package = "com.android.fastdeploy";
+option java_multiple_files = true;
+
+message APKEntry {
+    required int64 crc32 = 1;
+    required string fileName = 2;
+    required int64 dataOffset = 3;
+    required int64 compressedSize = 4;
+    required int64 uncompressedSize = 5;
+}
+
+message APKMetaData {
+    repeated APKEntry entries = 1;
+}
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 42d851a..32f9086 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -21,6 +21,8 @@
 #include "fdevent.h"
 
 #include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -30,17 +32,24 @@
 #include <functional>
 #include <list>
 #include <mutex>
+#include <optional>
 #include <unordered_map>
+#include <utility>
+#include <variant>
 #include <vector>
 
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
 #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"
+#include "sysdeps/chrono.h"
 
 #define FDE_EVENTMASK  0x00ff
 #define FDE_STATEMASK  0xff00
@@ -55,7 +64,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.
@@ -71,21 +80,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::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) {
@@ -108,37 +120,19 @@
     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());
+    return android::base::StringPrintf("(fdevent %" PRIu64 ": fd %d %s)", fde->id, fde->fd.get(),
+                                       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);
-}
-
-void fdevent_install(fdevent* fde, int fd, fd_func func, void* arg) {
+template <typename F>
+static fdevent* fdevent_create_impl(int fd, F func, void* arg) {
     check_main_thread();
     CHECK_GE(fd, 0);
-    memset(fde, 0, sizeof(fdevent));
+
+    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)) {
@@ -147,30 +141,53 @@
         // 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) {
+fdevent* fdevent_create(int fd, fd_func func, void* arg) {
+    return fdevent_create_impl(fd, func, arg);
+}
+
+fdevent* fdevent_create(int fd, fd_func2 func, void* arg) {
+    return fdevent_create_impl(fd, func, arg);
+}
+
+unique_fd fdevent_release(fdevent* fde) {
     check_main_thread();
-    D("fdevent_remove %s", dump_fde(fde).c_str());
+    if (!fde) {
+        return {};
+    }
+
+    if (!(fde->state & FDE_CREATED)) {
+        LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
+    }
+
+    unique_fd result = std::move(fde->fd);
     if (fde->state & FDE_ACTIVE) {
-        g_poll_node_map.erase(fde->fd);
+        g_poll_node_map.erase(result.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->state = 0;
         fde->events = 0;
     }
+
+    delete fde;
+    return result;
+}
+
+void fdevent_destroy(fdevent* fde) {
+    // Release, and then let unique_fd's destructor cleanup.
+    fdevent_release(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) {
@@ -209,14 +226,22 @@
 
 void fdevent_add(fdevent* fde, unsigned events) {
     check_main_thread();
+    CHECK(!(events & FDE_TIMEOUT));
     fdevent_set(fde, (fde->state & FDE_EVENTMASK) | events);
 }
 
 void fdevent_del(fdevent* fde, unsigned events) {
     check_main_thread();
+    CHECK(!(events & FDE_TIMEOUT));
     fdevent_set(fde, (fde->state & FDE_EVENTMASK) & ~events);
 }
 
+void fdevent_set_timeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout) {
+    check_main_thread();
+    fde->timeout = timeout;
+    fde->last_active = std::chrono::steady_clock::now();
+}
+
 static std::string dump_pollfds(const std::vector<adb_pollfd>& pollfds) {
     std::string result;
     for (const auto& pollfd : pollfds) {
@@ -232,6 +257,32 @@
     return result;
 }
 
+static std::optional<std::chrono::milliseconds> calculate_timeout() {
+    std::optional<std::chrono::milliseconds> result = std::nullopt;
+    auto now = std::chrono::steady_clock::now();
+    check_main_thread();
+
+    for (const auto& [fd, pollnode] : g_poll_node_map) {
+        UNUSED(fd);
+        auto timeout_opt = pollnode.fde->timeout;
+        if (timeout_opt) {
+            auto deadline = pollnode.fde->last_active + *timeout_opt;
+            auto time_left = std::chrono::duration_cast<std::chrono::milliseconds>(deadline - now);
+            if (time_left < std::chrono::milliseconds::zero()) {
+                time_left = std::chrono::milliseconds::zero();
+            }
+
+            if (!result) {
+                result = time_left;
+            } else {
+                result = std::min(*result, time_left);
+            }
+        }
+    }
+
+    return result;
+}
+
 static void fdevent_process() {
     std::vector<adb_pollfd> pollfds;
     for (const auto& pair : g_poll_node_map) {
@@ -239,11 +290,23 @@
     }
     CHECK_GT(pollfds.size(), 0u);
     D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
-    int ret = adb_poll(&pollfds[0], pollfds.size(), -1);
+
+    auto timeout = calculate_timeout();
+    int timeout_ms;
+    if (!timeout) {
+        timeout_ms = -1;
+    } else {
+        timeout_ms = timeout->count();
+    }
+
+    int ret = adb_poll(&pollfds[0], pollfds.size(), timeout_ms);
     if (ret == -1) {
         PLOG(ERROR) << "poll(), ret = " << ret;
         return;
     }
+
+    auto post_poll = std::chrono::steady_clock::now();
+
     for (const auto& pollfd : pollfds) {
         if (pollfd.revents != 0) {
             D("for fd %d, revents = %x", pollfd.fd, pollfd.revents);
@@ -265,12 +328,24 @@
             events |= FDE_READ | FDE_ERROR;
         }
 #endif
+        auto it = g_poll_node_map.find(pollfd.fd);
+        CHECK(it != g_poll_node_map.end());
+        fdevent* fde = it->second.fde;
+
+        if (events == 0) {
+            // Check for timeout.
+            if (fde->timeout) {
+                auto deadline = fde->last_active + *fde->timeout;
+                if (deadline < post_poll) {
+                    events |= FDE_TIMEOUT;
+                }
+            }
+        }
+
         if (events != 0) {
-            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;
+            fde->last_active = post_poll;
             D("%s got events %x", dump_fde(fde).c_str(), events);
             fde->state |= FDE_PENDING;
             g_pending_list.push_back(fde);
@@ -278,13 +353,27 @@
     }
 }
 
+template <class T>
+struct always_false : std::false_type {};
+
 static void fdevent_call_fdfunc(fdevent* fde) {
     unsigned events = fde->events;
     fde->events = 0;
     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);
+    std::visit(
+            [&](auto&& f) {
+                using F = std::decay_t<decltype(f)>;
+                if constexpr (std::is_same_v<fd_func, F>) {
+                    f(fde->fd.get(), events, fde->arg);
+                } else if constexpr (std::is_same_v<fd_func2, F>) {
+                    f(fde, events, fde->arg);
+                } else {
+                    static_assert(always_false<F>::value, "non-exhaustive visitor");
+                }
+            },
+            fde->func);
 }
 
 static void fdevent_run_flush() EXCLUDES(run_queue_mutex) {
@@ -315,7 +404,8 @@
         PLOG(FATAL) << "failed to empty run queue notify fd";
     }
 
-    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() {
@@ -358,10 +448,66 @@
     }
 }
 
+static void fdevent_check_spin(uint64_t cycle) {
+    // Check to see if we're spinning because we forgot about an fdevent
+    // by keeping track of how long fdevents have been continuously pending.
+    struct SpinCheck {
+        fdevent* fde;
+        android::base::boot_clock::time_point timestamp;
+        uint64_t cycle;
+    };
+    static auto& g_continuously_pending = *new std::unordered_map<uint64_t, SpinCheck>();
+    static auto last_cycle = android::base::boot_clock::now();
+
+    auto now = android::base::boot_clock::now();
+    if (now - last_cycle > 10ms) {
+        // We're not spinning.
+        g_continuously_pending.clear();
+        last_cycle = now;
+        return;
+    }
+    last_cycle = now;
+
+    for (auto* fde : g_pending_list) {
+        auto it = g_continuously_pending.find(fde->id);
+        if (it == g_continuously_pending.end()) {
+            g_continuously_pending[fde->id] =
+                    SpinCheck{.fde = fde, .timestamp = now, .cycle = cycle};
+        } else {
+            it->second.cycle = cycle;
+        }
+    }
+
+    for (auto it = g_continuously_pending.begin(); it != g_continuously_pending.end();) {
+        if (it->second.cycle != cycle) {
+            it = g_continuously_pending.erase(it);
+        } else {
+            // Use an absurdly long window, since all we really care about is
+            // getting a bugreport eventually.
+            if (now - it->second.timestamp > 300s) {
+                LOG(FATAL_WITHOUT_ABORT)
+                        << "detected spin in fdevent: " << dump_fde(it->second.fde);
+#if defined(__linux__)
+                int fd = it->second.fde->fd.get();
+                std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
+                std::string path;
+                if (!android::base::Readlink(fd_path, &path)) {
+                    PLOG(FATAL_WITHOUT_ABORT) << "readlink of fd " << fd << " failed";
+                }
+                LOG(FATAL_WITHOUT_ABORT) << "fd " << fd << " = " << path;
+#endif
+                abort();
+            }
+            ++it;
+        }
+    }
+}
+
 void fdevent_loop() {
     set_main_thread();
     fdevent_run_setup();
 
+    uint64_t cycle = 0;
     while (true) {
         if (terminate_loop) {
             return;
@@ -371,11 +517,18 @@
 
         fdevent_process();
 
+        fdevent_check_spin(cycle++);
+
         while (!g_pending_list.empty()) {
             fdevent* fde = g_pending_list.front();
             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..42dbb9e 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -18,64 +18,62 @@
 #define __FDEVENT_H
 
 #include <stddef.h>
-#include <stdint.h>  /* for int64_t */
+#include <stdint.h>
 
+#include <chrono>
 #include <functional>
+#include <optional>
+#include <variant>
 
-/* events that may be observed */
-#define FDE_READ              0x0001
-#define FDE_WRITE             0x0002
-#define FDE_ERROR             0x0004
+#include "adb_unique_fd.h"
 
-/* features that may be set (via the events set/add/del interface) */
-#define FDE_DONT_CLOSE        0x0080
+// Events that may be observed
+#define FDE_READ 0x0001
+#define FDE_WRITE 0x0002
+#define FDE_ERROR 0x0004
+#define FDE_TIMEOUT 0x0008
 
 typedef void (*fd_func)(int fd, unsigned events, void *userdata);
+typedef void (*fd_func2)(struct fdevent* fde, 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;
+    std::optional<std::chrono::milliseconds> timeout;
+    std::chrono::steady_clock::time_point last_active;
 
-    fd_func func;
-    void *arg;
+    std::variant<fd_func, fd_func2> func;
+    void* arg = nullptr;
 };
 
-/* Allocate and initialize a new fdevent object
- * Note: use FD_TIMER as 'fd' to create a fd-less object
- * (used to implement timers).
-*/
+// Allocate and initialize a new fdevent object
+// TODO: Switch these to unique_fd.
 fdevent *fdevent_create(int fd, fd_func func, void *arg);
+fdevent* fdevent_create(int fd, fd_func2 func, void* arg);
 
-/* Uninitialize and deallocate an fdevent object that was
-** created by fdevent_create()
-*/
+// Deallocate an fdevent object that was created by fdevent_create.
 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);
+// fdevent_destroy, except releasing the file descriptor previously owned by the fdevent.
+unique_fd fdevent_release(fdevent* fde);
 
-/* Uninitialize an fdevent object that was initialized by
-** fdevent_install()
-*/
-void fdevent_remove(fdevent *item);
-
-/* Change which events should cause notifications
-*/
+// Change which events should cause notifications
 void fdevent_set(fdevent *fde, unsigned events);
 void fdevent_add(fdevent *fde, unsigned events);
 void fdevent_del(fdevent *fde, unsigned events);
 
-void fdevent_set_timeout(fdevent *fde, int64_t  timeout_ms);
+// Set a timeout on an fdevent.
+// If no events are triggered by the timeout, an FDE_TIMEOUT will be generated.
+// Note timeouts are not defused automatically; if a timeout is set on an fdevent, it will
+// trigger repeatedly every |timeout| ms.
+void fdevent_set_timeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout);
 
-/* loop forever, handling events.
-*/
+// Loop forever, handling events.
 void fdevent_loop();
 
 void check_main_thread();
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index e3d5a35..682f061 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -18,7 +18,9 @@
 
 #include <gtest/gtest.h>
 
+#include <chrono>
 #include <limits>
+#include <memory>
 #include <queue>
 #include <string>
 #include <thread>
@@ -26,19 +28,26 @@
 
 #include "adb_io.h"
 #include "fdevent_test.h"
-#include "sysdeps/memory.h"
+
+using namespace std::chrono_literals;
 
 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);
+    FdHandler(int read_fd, int write_fd, bool use_new_callback)
+        : read_fd_(read_fd), write_fd_(write_fd) {
+        if (use_new_callback) {
+            read_fde_ = fdevent_create(read_fd_, FdEventNewCallback, this);
+            write_fde_ = fdevent_create(write_fd_, FdEventNewCallback, this);
+        } else {
+            read_fde_ = fdevent_create(read_fd_, FdEventCallback, this);
+            write_fde_ = fdevent_create(write_fd_, FdEventCallback, this);
+        }
+        fdevent_add(read_fde_, FDE_READ);
     }
 
     ~FdHandler() {
-        fdevent_remove(&read_fde_);
-        fdevent_remove(&write_fde_);
+        fdevent_destroy(read_fde_);
+        fdevent_destroy(write_fde_);
     }
 
   private:
@@ -50,7 +59,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_);
@@ -59,7 +68,30 @@
             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);
+            }
+        }
+    }
+
+    static void FdEventNewCallback(fdevent* fde, unsigned events, void* userdata) {
+        int fd = fde->fd.get();
+        FdHandler* handler = reinterpret_cast<FdHandler*>(userdata);
+        ASSERT_EQ(0u, (events & ~(FDE_READ | FDE_WRITE))) << "unexpected events: " << events;
+        if (events & FDE_READ) {
+            ASSERT_EQ(fd, handler->read_fd_);
+            char c;
+            ASSERT_EQ(1, adb_read(fd, &c, 1));
+            handler->queue_.push(c);
+            fdevent_add(handler->write_fde_, FDE_WRITE);
+        }
+        if (events & FDE_WRITE) {
+            ASSERT_EQ(fd, handler->write_fd_);
+            ASSERT_FALSE(handler->queue_.empty());
+            char c = handler->queue_.front();
+            handler->queue_.pop();
+            ASSERT_EQ(1, adb_write(fd, &c, 1));
+            if (handler->queue_.empty()) {
+                fdevent_del(handler->write_fde_, FDE_WRITE);
             }
         }
     }
@@ -67,8 +99,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_;
 };
 
@@ -80,73 +112,76 @@
 
 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::make_unique<FdHandler>(read_fds[i], write_fds[i]));
-    }
-
-    fdevent_loop();
+    TerminateThread();
 }
 
 TEST_F(FdeventTest, smoke) {
-    const size_t PIPE_COUNT = 10;
-    const size_t MESSAGE_LOOP_COUNT = 100;
-    const std::string MESSAGE = "fdevent_test";
-    int fd_pair1[2];
-    int fd_pair2[2];
-    ASSERT_EQ(0, adb_socketpair(fd_pair1));
-    ASSERT_EQ(0, adb_socketpair(fd_pair2));
-    ThreadArg thread_arg;
-    thread_arg.first_read_fd = fd_pair1[0];
-    thread_arg.last_write_fd = fd_pair2[1];
-    thread_arg.middle_pipe_count = PIPE_COUNT;
-    int writer = fd_pair1[1];
-    int reader = fd_pair2[0];
+    for (bool use_new_callback : {true, false}) {
+        fdevent_reset();
+        const size_t PIPE_COUNT = 10;
+        const size_t MESSAGE_LOOP_COUNT = 100;
+        const std::string MESSAGE = "fdevent_test";
+        int fd_pair1[2];
+        int fd_pair2[2];
+        ASSERT_EQ(0, adb_socketpair(fd_pair1));
+        ASSERT_EQ(0, adb_socketpair(fd_pair2));
+        ThreadArg thread_arg;
+        thread_arg.first_read_fd = fd_pair1[0];
+        thread_arg.last_write_fd = fd_pair2[1];
+        thread_arg.middle_pipe_count = PIPE_COUNT;
+        int writer = fd_pair1[1];
+        int reader = fd_pair2[0];
 
-    PrepareThread();
-    std::thread thread(FdEventThreadFunc, &thread_arg);
+        PrepareThread();
 
-    for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
-        std::string read_buffer = MESSAGE;
-        std::string write_buffer(MESSAGE.size(), 'a');
-        ASSERT_TRUE(WriteFdExactly(writer, read_buffer.c_str(), read_buffer.size()));
-        ASSERT_TRUE(ReadFdExactly(reader, &write_buffer[0], write_buffer.size()));
-        ASSERT_EQ(read_buffer, write_buffer);
+        std::vector<std::unique_ptr<FdHandler>> fd_handlers;
+        fdevent_run_on_main_thread([&thread_arg, &fd_handlers, use_new_callback]() {
+            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], use_new_callback));
+            }
+        });
+        WaitForFdeventLoop();
+
+        for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
+            std::string read_buffer = MESSAGE;
+            std::string write_buffer(MESSAGE.size(), 'a');
+            ASSERT_TRUE(WriteFdExactly(writer, read_buffer.c_str(), read_buffer.size()));
+            ASSERT_TRUE(ReadFdExactly(reader, &write_buffer[0], write_buffer.size()));
+            ASSERT_EQ(read_buffer, write_buffer);
+        }
+
+        fdevent_run_on_main_thread([&fd_handlers]() { fd_handlers.clear(); });
+        WaitForFdeventLoop();
+
+        TerminateThread();
+        ASSERT_EQ(0, adb_close(writer));
+        ASSERT_EQ(0, adb_close(reader));
     }
-
-    TerminateThread(thread);
-    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();
     }
@@ -158,15 +193,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();
 }
 
@@ -179,7 +214,6 @@
     std::vector<int> vec;
 
     PrepareThread();
-    std::thread thread(fdevent_loop);
 
     // Block the main thread for a long time while we queue our callbacks.
     fdevent_run_on_main_thread([]() {
@@ -194,7 +228,7 @@
         });
     }
 
-    TerminateThread(thread);
+    TerminateThread();
 
     ASSERT_EQ(1000000u, vec.size());
     for (int i = 0; i < 1000000; ++i) {
@@ -218,14 +252,108 @@
     std::vector<int> vec;
 
     PrepareThread();
-    std::thread thread(fdevent_loop);
-
     fdevent_run_on_main_thread(make_appender(&vec, 0));
-
-    TerminateThread(thread);
+    TerminateThread();
 
     ASSERT_EQ(100u, vec.size());
     for (int i = 0; i < 100; ++i) {
         ASSERT_EQ(i, vec[i]);
     }
 }
+
+TEST_F(FdeventTest, timeout) {
+    fdevent_reset();
+    PrepareThread();
+
+    enum class TimeoutEvent {
+        read,
+        timeout,
+        done,
+    };
+
+    struct TimeoutTest {
+        std::vector<std::pair<TimeoutEvent, std::chrono::steady_clock::time_point>> events;
+        fdevent* fde;
+    };
+    TimeoutTest test;
+
+    int fds[2];
+    ASSERT_EQ(0, adb_socketpair(fds));
+    static constexpr auto delta = 100ms;
+    fdevent_run_on_main_thread([&]() {
+        test.fde = fdevent_create(fds[0], [](fdevent* fde, unsigned events, void* arg) {
+            auto test = static_cast<TimeoutTest*>(arg);
+            auto now = std::chrono::steady_clock::now();
+            CHECK((events & FDE_READ) ^ (events & FDE_TIMEOUT));
+            TimeoutEvent event;
+            if ((events & FDE_READ)) {
+                char buf[2];
+                ssize_t rc = adb_read(fde->fd.get(), buf, sizeof(buf));
+                if (rc == 0) {
+                    event = TimeoutEvent::done;
+                } else if (rc == 1) {
+                    event = TimeoutEvent::read;
+                } else {
+                    abort();
+                }
+            } else if ((events & FDE_TIMEOUT)) {
+                event = TimeoutEvent::timeout;
+            } else {
+                abort();
+            }
+
+            CHECK_EQ(fde, test->fde);
+            test->events.emplace_back(event, now);
+
+            if (event == TimeoutEvent::done) {
+                fdevent_destroy(fde);
+            }
+        }, &test);
+        fdevent_add(test.fde, FDE_READ);
+        fdevent_set_timeout(test.fde, delta);
+    });
+
+    ASSERT_EQ(1, adb_write(fds[1], "", 1));
+
+    // Timeout should happen here
+    std::this_thread::sleep_for(delta);
+
+    // and another.
+    std::this_thread::sleep_for(delta);
+
+    // No timeout should happen here.
+    std::this_thread::sleep_for(delta / 2);
+    adb_close(fds[1]);
+
+    TerminateThread();
+
+    ASSERT_EQ(4ULL, test.events.size());
+    ASSERT_EQ(TimeoutEvent::read, test.events[0].first);
+    ASSERT_EQ(TimeoutEvent::timeout, test.events[1].first);
+    ASSERT_EQ(TimeoutEvent::timeout, test.events[2].first);
+    ASSERT_EQ(TimeoutEvent::done, test.events[3].first);
+
+    std::vector<int> time_deltas;
+    for (size_t i = 0; i < test.events.size() - 1; ++i) {
+        auto before = test.events[i].second;
+        auto after = test.events[i + 1].second;
+        auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(after - before);
+        time_deltas.push_back(diff.count());
+    }
+
+    std::vector<int> expected = {
+        delta.count(),
+        delta.count(),
+        delta.count() / 2,
+    };
+
+    std::vector<int> diff;
+    ASSERT_EQ(time_deltas.size(), expected.size());
+    for (size_t i = 0; i < time_deltas.size(); ++i) {
+        diff.push_back(std::abs(time_deltas[i] - expected[i]));
+    }
+
+    ASSERT_LT(diff[0], delta.count() * 0.5);
+    ASSERT_LT(diff[1], delta.count() * 0.5);
+    ASSERT_LT(diff[2], delta.count() * 0.5);
+}
diff --git a/adb/fdevent_test.h b/adb/fdevent_test.h
index 1a2d41c..24bce59 100644
--- a/adb/fdevent_test.h
+++ b/adb/fdevent_test.h
@@ -16,14 +16,37 @@
 
 #include <gtest/gtest.h>
 
+#include <condition_variable>
+#include <mutex>
 #include <thread>
 
+#include "adb_io.h"
+#include "adb_unique_fd.h"
 #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:
-    int dummy = -1;
+    unique_fd dummy;
 
     static void SetUpTestCase() {
 #if !defined(_WIN32)
@@ -43,12 +66,15 @@
             FAIL() << "failed to create socketpair: " << strerror(errno);
         }
 
-        asocket* dummy_socket = create_local_socket(dummy_fds[1]);
+        asocket* dummy_socket = create_local_socket(unique_fd(dummy_fds[1]));
         if (!dummy_socket) {
             FAIL() << "failed to create local socket: " << strerror(errno);
         }
         dummy_socket->ready(dummy_socket);
-        dummy = dummy_fds[0];
+        dummy.reset(dummy_fds[0]);
+
+        thread_ = std::thread([]() { fdevent_loop(); });
+        WaitForFdeventLoop();
     }
 
     size_t GetAdditionalLocalSocketCount() {
@@ -56,10 +82,12 @@
         return 2;
     }
 
-    void TerminateThread(std::thread& thread) {
+    void TerminateThread() {
         fdevent_terminate_loop();
         ASSERT_TRUE(WriteFdExactly(dummy, "", 1));
-        thread.join();
-        ASSERT_EQ(0, adb_close(dummy));
+        thread_.join();
+        dummy.reset();
     }
+
+    std::thread thread_;
 };
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
deleted file mode 100644
index 26f8d83..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 - 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__(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 1128993..0000000
--- a/adb/file_sync_service.cpp
+++ /dev/null
@@ -1,530 +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 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) {
-    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 (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(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 6c3a225..0000000
--- a/adb/framebuffer_service.cpp
+++ /dev/null
@@ -1,188 +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 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(int fd, void *cookie)
-{
-    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) 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 & 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, &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 5f070d9..0000000
--- a/adb/jdwp_service.cpp
+++ /dev/null
@@ -1,590 +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
- **/
-
-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);
-    }
-
-    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 = 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;
-            }
-
-            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 = 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, std::string) {
-    /* 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) {
-        std::string 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 std::vector<std::unique_ptr<JdwpTracker>> _jdwp_trackers;
-
-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.
-            t->peer->enqueue(t->peer, data);
-        }
-    }
-}
-
-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) {
-        std::string 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, std::string) {
-    /* 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/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/line_printer.h b/adb/line_printer.h
deleted file mode 100644
index 42345e2..0000000
--- a/adb/line_printer.h
+++ /dev/null
@@ -1,50 +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.
-
-#ifndef NINJA_LINE_PRINTER_H_
-#define NINJA_LINE_PRINTER_H_
-
-#include <stddef.h>
-#include <string>
-
-/// Prints lines of text, possibly overprinting previously printed lines
-/// if the terminal supports it.
-struct LinePrinter {
-  LinePrinter();
-
-  bool is_smart_terminal() const { return smart_terminal_; }
-  void set_smart_terminal(bool smart) { smart_terminal_ = smart; }
-
-  enum LineType { INFO, WARNING, ERROR };
-
-  /// Outputs the given line. INFO output will be overwritten.
-  /// WARNING and ERROR appear on a line to themselves.
-  void Print(std::string to_print, LineType type);
-
-  /// If there's an INFO line, keep it. If not, do nothing.
-  void KeepInfoLine();
-
- private:
-  /// Whether we can do fancy terminal control codes.
-  bool smart_terminal_;
-
-  /// Whether the caret is at the beginning of a blank line.
-  bool have_blank_line_;
-
-#ifdef _WIN32
-  void* console_;
-#endif
-};
-
-#endif  // NINJA_LINE_PRINTER_H_
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/range.h b/adb/range.h
deleted file mode 100644
index 7a0b822..0000000
--- a/adb/range.h
+++ /dev/null
@@ -1,65 +0,0 @@
-#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 <string>
-
-#include <android-base/logging.h>
-
-struct Range {
-    explicit Range(std::string data) : data_(std::move(data)) {}
-
-    Range(const Range& copy) = delete;
-    Range& operator=(const Range& copy) = delete;
-
-    Range(Range&& move) = default;
-    Range& operator=(Range&& move) = default;
-
-    bool empty() const {
-        return size() == 0;
-    }
-
-    size_t size() const {
-        return data_.size() - begin_offset_ - end_offset_;
-    };
-
-    void drop_front(size_t n) {
-        CHECK_GE(size(), n);
-        begin_offset_ += n;
-    }
-
-    void drop_end(size_t n) {
-        CHECK_GE(size(), n);
-        end_offset_ += n;
-    }
-
-    char* data() {
-        return &data_[0] + begin_offset_;
-    }
-
-    std::string::iterator begin() {
-        return data_.begin() + begin_offset_;
-    }
-
-    std::string::iterator end() {
-        return data_.end() - end_offset_;
-    }
-
-    std::string data_;
-    size_t begin_offset_ = 0;
-    size_t end_offset_ = 0;
-};
diff --git a/adb/remount_service.cpp b/adb/remount_service.cpp
deleted file mode 100644
index d679a6d..0000000
--- a/adb/remount_service.cpp
+++ /dev/null
@@ -1,156 +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, 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 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;
-    }
-    if (mount(dev.c_str(), dir, "none", MS_REMOUNT | MS_BIND, nullptr) == -1) {
-        // This is useful for cases where the superblock is already marked as
-        // read-write, but the mount itself is read-only, such as containers
-        // where the remount with just MS_REMOUNT is forbidden by the kernel.
-        WriteFdFmt(fd, "remount of the %s mount failed: %s.\n", dir, strerror(errno));
-        return false;
-    }
-    if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) {
-        WriteFdFmt(fd, "remount of the %s superblock 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, "/odm");
-    success &= remount_partition(fd, "/oem");
-    success &= remount_partition(fd, "/product");
-    success &= remount_partition(fd, "/vendor");
-
-    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 0b0c161..6185aa6 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -24,312 +24,70 @@
 #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 {
-    const char* service_name;
-    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("%s svc %d", sti->service_name, 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();
-
-    if (!reboot_arg || !reboot_arg[0]) reboot_arg = "adb";
-    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, atransport* transport) {
-    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, transport, 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(const char* service_name, 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));
         adb_setsockopt(s[1], SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
     }
-#endif // !ADB_HOST
+#endif  // !ADB_HOST
 
-    stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
-    if (sti == nullptr) {
-        fatal("cannot allocate stinfo");
-    }
-    sti->service_name = service_name;
-    sti->func = func;
-    sti->cookie = cookie;
-    sti->fd = s[1];
+    std::thread(service_bootstrap_func, service_name, func, unique_fd(s[1])).detach();
 
-    std::thread(service_bootstrap_func, sti).detach();
-
-    D("service thread started, %d:%d",s[0], s[1]);
-    return s[0];
+    D("service thread started, %d:%d", s[0], s[1]);
+    return unique_fd(s[0]);
 }
 
-int service_to_fd(const char* name, atransport* transport) {
-    int ret = -1;
+unique_fd service_to_fd(std::string_view name, atransport* transport) {
+    unique_fd ret;
 
     if (is_socket_spec(name)) {
         std::string error;
-        ret = socket_spec_connect(name, &error);
-        if (ret < 0) {
+        if (!socket_spec_connect(&ret, name, nullptr, nullptr, &error)) {
             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("fb", framebuffer_service, nullptr);
-    } 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("sync", file_sync_service, nullptr);
-    } else if(!strncmp(name, "remount:", 8)) {
-        ret = create_service_thread("remount", remount_service, nullptr);
-    } else if(!strncmp(name, "reboot:", 7)) {
-        void* arg = strdup(name + 7);
-        if (arg == NULL) return -1;
-        ret = create_service_thread("reboot", reboot_service, arg);
-        if (ret < 0) free(arg);
-    } else if(!strncmp(name, "root:", 5)) {
-        ret = create_service_thread("root", restart_root_service, nullptr);
-    } else if(!strncmp(name, "unroot:", 7)) {
-        ret = create_service_thread("unroot", restart_unroot_service, nullptr);
-    } 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("tcp", restart_tcp_service, reinterpret_cast<void*>(port));
-    } else if(!strncmp(name, "usb:", 4)) {
-        ret = create_service_thread("usb", restart_usb_service, nullptr);
-    } else if (!strncmp(name, "reverse:", 8)) {
-        ret = reverse_service(name + 8, transport);
-    } else if(!strncmp(name, "disable-verity:", 15)) {
-        ret = create_service_thread("verity-on", set_verity_enabled_state_service,
-                                    reinterpret_cast<void*>(0));
-    } else if(!strncmp(name, "enable-verity:", 15)) {
-        ret = create_service_thread("verity-off", set_verity_enabled_state_service,
-                                    reinterpret_cast<void*>(1));
-    } else if (!strcmp(name, "reconnect")) {
-        ret = create_service_thread("reconnect", reconnect_service, transport);
+        ret = daemon_service_to_fd(name, transport);
 #endif
     }
+
     if (ret >= 0) {
-        close_on_exec(ret);
+        close_on_exec(ret.get());
     }
     return ret;
 }
@@ -342,23 +100,30 @@
     ConnectionState state;
 };
 
-static void wait_for_state(int fd, void* data) {
-    std::unique_ptr<state_info> sinfo(reinterpret_cast<state_info*>(data));
-
+static void wait_for_state(unique_fd fd, state_info* sinfo) {
     D("wait_for_state %d", sinfo->state);
 
     while (true) {
         bool is_ambiguous = false;
         std::string error = "unknown error";
-        const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : NULL;
+        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())) {
+        if (sinfo->state == kCsOffline) {
+            // wait-for-disconnect uses kCsOffline, we don't actually want to wait for 'offline'.
+            if (t == nullptr) {
+                SendOkay(fd);
+                break;
+            }
+        } else if (t != nullptr &&
+                   (sinfo->state == kCsAny || sinfo->state == t->GetConnectionState())) {
             SendOkay(fd);
             break;
-        } else if (!is_ambiguous) {
-            adb_pollfd pfd = {.fd = fd, .events = POLLIN };
-            int rc = adb_poll(&pfd, 1, 1000);
+        }
+
+        if (!is_ambiguous) {
+            adb_pollfd pfd = {.fd = fd.get(), .events = POLLIN};
+            int rc = adb_poll(&pfd, 1, 100);
             if (rc < 0) {
                 SendFail(fd, error);
                 break;
@@ -375,7 +140,6 @@
         }
     }
 
-    adb_close(fd);
     D("wait_for_state is done");
 }
 
@@ -387,8 +151,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;
@@ -418,80 +182,73 @@
     }
 }
 
-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);
     }
-    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, TransportId transport_id) {
-    if (!strcmp(name,"track-devices")) {
+asocket* host_service_to_socket(std::string_view name, std::string_view serial,
+                                TransportId transport_id) {
+    if (name == "track-devices") {
         return create_device_tracker(false);
-    } else if (!strcmp(name, "track-devices-l")) {
+    } else if (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);
+    } else if (android::base::ConsumePrefix(&name, "wait-for-")) {
+        std::shared_ptr<state_info> sinfo = std::make_shared<state_info>();
         if (sinfo == nullptr) {
             fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
             return nullptr;
         }
 
-        if (serial) sinfo->serial = serial;
+        sinfo->serial = serial;
         sinfo->transport_id = transport_id;
 
-        if (android::base::StartsWith(name, "local")) {
-            name += strlen("local");
+        if (android::base::ConsumePrefix(&name, "local")) {
             sinfo->transport_type = kTransportLocal;
-        } else if (android::base::StartsWith(name, "usb")) {
-            name += strlen("usb");
+        } else if (android::base::ConsumePrefix(&name, "usb")) {
             sinfo->transport_type = kTransportUsb;
-        } else if (android::base::StartsWith(name, "any")) {
-            name += strlen("any");
+        } else if (android::base::ConsumePrefix(&name, "any")) {
             sinfo->transport_type = kTransportAny;
         } else {
             return nullptr;
         }
 
-        if (!strcmp(name, "-device")) {
+        if (name == "-device") {
             sinfo->state = kCsDevice;
-        } else if (!strcmp(name, "-recovery")) {
+        } else if (name == "-recovery") {
             sinfo->state = kCsRecovery;
-        } else if (!strcmp(name, "-sideload")) {
+        } else if (name == "-rescue") {
+            sinfo->state = kCsRescue;
+        } else if (name == "-sideload") {
             sinfo->state = kCsSideload;
-        } else if (!strcmp(name, "-bootloader")) {
+        } else if (name == "-bootloader") {
             sinfo->state = kCsBootloader;
-        } else if (!strcmp(name, "-any")) {
+        } else if (name == "-any") {
             sinfo->state = kCsAny;
+        } else if (name == "-disconnect") {
+            sinfo->state = kCsOffline;
         } else {
             return nullptr;
         }
 
-        int fd = create_service_thread("wait", wait_for_state, sinfo.get());
-        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", connect_service, host);
-        if (fd == -1) {
-            free(host);
-        }
-        return create_local_socket(fd);
+        unique_fd fd = create_service_thread(
+                "wait", [sinfo](unique_fd fd) { wait_for_state(std::move(fd), sinfo.get()); });
+        return create_local_socket(std::move(fd));
+    } else if (android::base::ConsumePrefix(&name, "connect:")) {
+        std::string host(name);
+        unique_fd fd = create_service_thread(
+                "connect", std::bind(connect_service, std::placeholders::_1, host));
+        return create_local_socket(std::move(fd));
     }
-    return NULL;
+    return nullptr;
 }
 #endif /* ADB_HOST */
diff --git a/adb/services.h b/adb/services.h
index 0428ca4..6fc89d7 100644
--- a/adb/services.h
+++ b/adb/services.h
@@ -17,8 +17,16 @@
 #ifndef SERVICES_H_
 #define SERVICES_H_
 
+#include "adb_unique_fd.h"
+
 constexpr char kShellServiceArgRaw[] = "raw";
 constexpr char kShellServiceArgPty[] = "pty";
 constexpr char kShellServiceArgShellProtocol[] = "v2";
 
+// Special flags sent by minadbd. They indicate the end of sideload transfer and the result of
+// installation or wipe.
+constexpr char kMinadbdServicesExitSuccess[] = "DONEDONE";
+constexpr char kMinadbdServicesExitFailure[] = "FAILFAIL";
+
+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 0fcf89b..0000000
--- a/adb/set_verity_enable_state_service.cpp
+++ /dev/null
@@ -1,202 +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.
- */
-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()) {
-        WriteFdFmt(fd, "Device is locked. Please unlock the device first\n");
-        return false;
-    }
-
-    if (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
-        WriteFdFmt(fd, "Error getting verity state. 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)) {
-        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..4aab813
--- /dev/null
+++ b/adb/shell_protocol.h
@@ -0,0 +1,118 @@
+/*
+ * 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"
+#include "adb_unique_fd.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(borrowed_fd 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)
+    };
+
+    borrowed_fd 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 da1222b..0000000
--- a/adb/shell_service.cpp
+++ /dev/null
@@ -1,757 +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/properties.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();
-}
-
-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)));
-        }
-
-        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_ = 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 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..95afaff 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>
 
@@ -22,7 +22,7 @@
 
 #include "adb_io.h"
 
-ShellProtocol::ShellProtocol(int fd) : fd_(fd) {
+ShellProtocol::ShellProtocol(borrowed_fd fd) : fd_(fd) {
     buffer_[0] = kIdInvalid;
 }
 
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 4e27822..0000000
--- a/adb/shell_service_test.cpp
+++ /dev/null
@@ -1,270 +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;
-};
-
-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() {
-    if (subprocess_fd_ >= 0) {
-        adb_close(subprocess_fd_);
-        subprocess_fd_ = -1;
-    }
-}
-
-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 88499b5..b8c559a 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -23,10 +23,10 @@
 #include <memory>
 #include <string>
 
+#include "adb_unique_fd.h"
 #include "fdevent.h"
-#include "range.h"
+#include "types.h"
 
-struct apacket;
 class atransport;
 
 /* An asocket represents one half of a connection between a local and
@@ -59,11 +59,11 @@
      * us to our fd event system.  For remote asockets
      * these fields are not used.
      */
-    fdevent fde = {};
-    int fd = 0;
+    fdevent* fde = nullptr;
+    int fd = -1;
 
     // queue of data waiting to be written
-    std::deque<Range> packet_queue;
+    IOVector packet_queue;
 
     std::string smart_socket_data;
 
@@ -73,7 +73,7 @@
      * peer->ready() when we once again are ready to
      * receive data.
      */
-    int (*enqueue)(asocket* s, std::string data) = nullptr;
+    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
@@ -103,18 +103,19 @@
 void remove_socket(asocket *s);
 void close_all_sockets(atransport *t);
 
-asocket *create_local_socket(int fd);
-asocket* create_local_service_socket(const char* destination, atransport* transport);
+asocket* create_local_socket(unique_fd fd);
+asocket* create_local_service_socket(std::string_view destination, atransport* transport);
 
 asocket *create_remote_socket(unsigned id, atransport *t);
-void connect_to_remote(asocket *s, const char *destination);
+void connect_to_remote(asocket* s, std::string_view destination);
 void connect_to_smartsocket(asocket *s);
 
 // Internal functions that are only made available here for testing purposes.
 namespace internal {
 
 #if ADB_HOST
-char* skip_host_serial(char* service);
+bool parse_host_service(std::string_view* out_serial, std::string_view* out_command,
+                        std::string_view service);
 #endif
 
 }  // namespace internal
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index eb4df97..1333724 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -17,6 +17,7 @@
 #include "socket_spec.h"
 
 #include <string>
+#include <string_view>
 #include <unordered_map>
 #include <vector>
 
@@ -29,7 +30,8 @@
 #include "adb.h"
 #include "sysdeps.h"
 
-using android::base::StartsWith;
+using namespace std::string_literals;
+
 using android::base::StringPrintf;
 
 #if defined(__linux__)
@@ -44,6 +46,11 @@
 #define ADB_WINDOWS 0
 #endif
 
+#if ADB_LINUX
+#include <sys/socket.h>
+#include "sysdeps/vm_sockets.h"
+#endif
+
 // Not static because it is used in commandline.c.
 int gListenAll = 0;
 
@@ -64,10 +71,11 @@
     { "localfilesystem", { ANDROID_SOCKET_NAMESPACE_FILESYSTEM, !ADB_WINDOWS } },
 });
 
-bool parse_tcp_socket_spec(const std::string& spec, std::string* hostname, int* port,
-                           std::string* error) {
-    if (!StartsWith(spec, "tcp:")) {
-        *error = StringPrintf("specification is not tcp: '%s'", spec.c_str());
+bool parse_tcp_socket_spec(std::string_view spec, std::string* hostname, int* port,
+                           std::string* serial, std::string* error) {
+    if (!spec.starts_with("tcp:")) {
+        *error = "specification is not tcp: ";
+        *error += spec;
         return false;
     }
 
@@ -84,17 +92,18 @@
             return false;
         }
     } else {
-        std::string addr = spec.substr(4);
+        std::string addr(spec.substr(4));
         port_value = -1;
 
         // FIXME: ParseNetAddress rejects port 0. This currently doesn't hurt, because listening
         //        on an address that isn't 'localhost' is unsupported.
-        if (!android::base::ParseNetAddress(addr, &hostname_value, &port_value, nullptr, error)) {
+        if (!android::base::ParseNetAddress(addr, &hostname_value, &port_value, serial, error)) {
             return false;
         }
 
         if (port_value == -1) {
-            *error = StringPrintf("missing port in specification: '%s'", spec.c_str());
+            *error = "missing port in specification: ";
+            *error += spec;
             return false;
         }
     }
@@ -110,87 +119,152 @@
     return true;
 }
 
-static bool tcp_host_is_local(const std::string& hostname) {
+static bool tcp_host_is_local(std::string_view hostname) {
     // FIXME
     return hostname.empty() || hostname == "localhost";
 }
 
-bool is_socket_spec(const std::string& spec) {
+bool is_socket_spec(std::string_view spec) {
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (StartsWith(spec, prefix)) {
+        if (spec.starts_with(prefix)) {
             return true;
         }
     }
-    return StartsWith(spec, "tcp:");
+    return spec.starts_with("tcp:");
 }
 
-bool is_local_socket_spec(const std::string& spec) {
+bool is_local_socket_spec(std::string_view spec) {
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (StartsWith(spec, prefix)) {
+        if (spec.starts_with(prefix)) {
             return true;
         }
     }
 
     std::string error;
     std::string hostname;
-    if (!parse_tcp_socket_spec(spec, &hostname, nullptr, &error)) {
+    if (!parse_tcp_socket_spec(spec, &hostname, nullptr, nullptr, &error)) {
         return false;
     }
     return tcp_host_is_local(hostname);
 }
 
-int socket_spec_connect(const std::string& spec, std::string* error) {
-    if (StartsWith(spec, "tcp:")) {
+bool socket_spec_connect(unique_fd* fd, std::string_view address, int* port, std::string* serial,
+                         std::string* error) {
+    if (address.starts_with("tcp:")) {
         std::string hostname;
-        int port;
-        if (!parse_tcp_socket_spec(spec, &hostname, &port, error)) {
-            return -1;
+        int port_value = port ? *port : 0;
+        if (!parse_tcp_socket_spec(address, &hostname, &port_value, serial, error)) {
+            return false;
         }
 
-        int result;
         if (tcp_host_is_local(hostname)) {
-            result = network_loopback_client(port, SOCK_STREAM, error);
+            fd->reset(network_loopback_client(port_value, SOCK_STREAM, error));
         } else {
 #if ADB_HOST
-            result = network_connect(hostname, port, SOCK_STREAM, 0, error);
+            fd->reset(network_connect(hostname, port_value, SOCK_STREAM, 0, error));
 #else
             // Disallow arbitrary connections in adbd.
             *error = "adbd does not support arbitrary tcp connections";
-            return -1;
+            return false;
 #endif
         }
 
-        if (result >= 0) {
-            disable_tcp_nagle(result);
+        if (fd->get() > 0) {
+            disable_tcp_nagle(fd->get());
+            if (port) {
+                *port = port_value;
+            }
+            return true;
         }
-        return result;
+        return false;
+    } else if (address.starts_with("vsock:")) {
+#if ADB_LINUX
+        std::string spec_str(address);
+        std::vector<std::string> fragments = android::base::Split(spec_str, ":");
+        unsigned int port_value = port ? *port : 0;
+        if (fragments.size() != 2 && fragments.size() != 3) {
+            *error = android::base::StringPrintf("expected vsock:cid or vsock:port:cid in '%s'",
+                                                 spec_str.c_str());
+            errno = EINVAL;
+            return false;
+        }
+        unsigned int cid = 0;
+        if (!android::base::ParseUint(fragments[1], &cid)) {
+            *error = android::base::StringPrintf("could not parse vsock cid in '%s'",
+                                                 spec_str.c_str());
+            errno = EINVAL;
+            return false;
+        }
+        if (fragments.size() == 3 && !android::base::ParseUint(fragments[2], &port_value)) {
+            *error = android::base::StringPrintf("could not parse vsock port in '%s'",
+                                                 spec_str.c_str());
+            errno = EINVAL;
+            return false;
+        }
+        if (port_value == 0) {
+            *error = android::base::StringPrintf("vsock port was not provided.");
+            errno = EINVAL;
+            return false;
+        }
+        fd->reset(socket(AF_VSOCK, SOCK_STREAM, 0));
+        if (fd->get() == -1) {
+            *error = "could not open vsock socket";
+            return false;
+        }
+        sockaddr_vm addr{};
+        addr.svm_family = AF_VSOCK;
+        addr.svm_port = port_value;
+        addr.svm_cid = cid;
+        if (serial) {
+            *serial = android::base::StringPrintf("vsock:%u:%d", cid, port_value);
+        }
+        if (connect(fd->get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr))) {
+            int error_num = errno;
+            *error = android::base::StringPrintf("could not connect to vsock address '%s'",
+                                                 spec_str.c_str());
+            errno = error_num;
+            return false;
+        }
+        if (port) {
+            *port = port_value;
+        }
+        return true;
+#else   // ADB_LINUX
+        *error = "vsock is only supported on linux";
+        return false;
+#endif  // ADB_LINUX
     }
 
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (StartsWith(spec, prefix)) {
+        if (address.starts_with(prefix)) {
             if (!it.second.available) {
                 *error = StringPrintf("socket type %s is unavailable on this platform",
                                       it.first.c_str());
-                return -1;
+                return false;
             }
 
-            return network_local_client(&spec[prefix.length()], it.second.socket_namespace,
-                                        SOCK_STREAM, error);
+            fd->reset(network_local_client(&address[prefix.length()], it.second.socket_namespace,
+                                           SOCK_STREAM, error));
+            if (serial) {
+                *serial = address;
+            }
+            return true;
         }
     }
 
-    *error = StringPrintf("unknown socket specification '%s'", spec.c_str());
-    return -1;
+    *error = "unknown socket specification: ";
+    *error += address;
+    return false;
 }
 
-int socket_spec_listen(const std::string& spec, std::string* error, int* resolved_tcp_port) {
-    if (StartsWith(spec, "tcp:")) {
+int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_port) {
+    if (spec.starts_with("tcp:")) {
         std::string hostname;
         int port;
-        if (!parse_tcp_socket_spec(spec, &hostname, &port, error)) {
+        if (!parse_tcp_socket_spec(spec, &hostname, &port, nullptr, error)) {
             return -1;
         }
 
@@ -205,18 +279,67 @@
             return -1;
         }
 
-        if (result >= 0 && port == 0 && resolved_tcp_port) {
-            *resolved_tcp_port = adb_socket_get_local_port(result);
+        if (result >= 0 && resolved_port) {
+            *resolved_port = adb_socket_get_local_port(result);
         }
         return result;
+    } else if (spec.starts_with("vsock:")) {
+#if ADB_LINUX
+        std::string spec_str(spec);
+        std::vector<std::string> fragments = android::base::Split(spec_str, ":");
+        if (fragments.size() != 2) {
+            *error = "given vsock server socket string was invalid";
+            return -1;
+        }
+        int port;
+        if (!android::base::ParseInt(fragments[1], &port)) {
+            *error = "could not parse vsock port";
+            errno = EINVAL;
+            return -1;
+        } else if (port < 0) {
+            *error = "vsock port was negative.";
+            errno = EINVAL;
+            return -1;
+        }
+        unique_fd serverfd(socket(AF_VSOCK, SOCK_STREAM, 0));
+        if (serverfd == -1) {
+            int error_num = errno;
+            *error = android::base::StringPrintf("could not create vsock server: '%s'",
+                                                 strerror(error_num));
+            errno = error_num;
+            return -1;
+        }
+        sockaddr_vm addr{};
+        addr.svm_family = AF_VSOCK;
+        addr.svm_port = port == 0 ? VMADDR_PORT_ANY : port;
+        addr.svm_cid = VMADDR_CID_ANY;
+        socklen_t addr_len = sizeof(addr);
+        if (bind(serverfd.get(), reinterpret_cast<struct sockaddr*>(&addr), addr_len)) {
+            return -1;
+        }
+        if (listen(serverfd.get(), 4)) {
+            return -1;
+        }
+        if (serverfd >= 0 && resolved_port) {
+            if (getsockname(serverfd.get(), reinterpret_cast<sockaddr*>(&addr), &addr_len) == 0) {
+                *resolved_port = addr.svm_port;
+            } else {
+                return -1;
+            }
+        }
+        return serverfd.release();
+#else   // ADB_LINUX
+        *error = "vsock is only supported on linux";
+        return -1;
+#endif  // ADB_LINUX
     }
 
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (StartsWith(spec, prefix)) {
+        if (spec.starts_with(prefix)) {
             if (!it.second.available) {
-                *error = StringPrintf("attempted to listen on unavailable socket type: '%s'",
-                                      spec.c_str());
+                *error = "attempted to listen on unavailable socket type: ";
+                *error += spec;
                 return -1;
             }
 
@@ -225,6 +348,7 @@
         }
     }
 
-    *error = StringPrintf("unknown socket specification '%s'", spec.c_str());
+    *error = "unknown socket specification:";
+    *error += spec;
     return -1;
 }
diff --git a/adb/socket_spec.h b/adb/socket_spec.h
index 6920e91..7cc2fac 100644
--- a/adb/socket_spec.h
+++ b/adb/socket_spec.h
@@ -17,15 +17,17 @@
 #pragma once
 
 #include <string>
+#include <tuple>
+
+#include "adb_unique_fd.h"
 
 // Returns true if the argument starts with a plausible socket prefix.
-bool is_socket_spec(const std::string& spec);
-bool is_local_socket_spec(const std::string& spec);
+bool is_socket_spec(std::string_view spec);
+bool is_local_socket_spec(std::string_view spec);
 
-int socket_spec_connect(const std::string& spec, std::string* error);
-int socket_spec_listen(const std::string& spec, std::string* error,
-                       int* resolved_tcp_port = nullptr);
+bool socket_spec_connect(unique_fd* fd, std::string_view address, int* port, std::string* serial,
+                         std::string* error);
+int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_tcp_port = nullptr);
 
-// Exposed for testing.
-bool parse_tcp_socket_spec(const std::string& spec, std::string* hostname, int* port,
-                           std::string* error);
+bool parse_tcp_socket_spec(std::string_view spec, std::string* hostname, int* port,
+                           std::string* serial, std::string* error);
diff --git a/adb/socket_spec_test.cpp b/adb/socket_spec_test.cpp
index 40ce21c..f5ec0f1 100644
--- a/adb/socket_spec_test.cpp
+++ b/adb/socket_spec_test.cpp
@@ -21,34 +21,37 @@
 #include <gtest/gtest.h>
 
 TEST(socket_spec, parse_tcp_socket_spec) {
-    std::string hostname, error;
+    std::string hostname, error, serial;
     int port;
-    EXPECT_TRUE(parse_tcp_socket_spec("tcp:5037", &hostname, &port, &error));
+    EXPECT_TRUE(parse_tcp_socket_spec("tcp:5037", &hostname, &port, &serial, &error));
     EXPECT_EQ("", hostname);
     EXPECT_EQ(5037, port);
+    EXPECT_EQ("", serial);
 
     // Bad ports:
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:-1", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:65536", &hostname, &port, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:-1", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:65536", &hostname, &port, &serial, &error));
 
-    EXPECT_TRUE(parse_tcp_socket_spec("tcp:localhost:1234", &hostname, &port, &error));
+    EXPECT_TRUE(parse_tcp_socket_spec("tcp:localhost:1234", &hostname, &port, &serial, &error));
     EXPECT_EQ("localhost", hostname);
     EXPECT_EQ(1234, port);
+    EXPECT_EQ("localhost:1234", serial);
 
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:-1", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:65536", &hostname, &port, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:-1", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:65536", &hostname, &port, &serial, &error));
 
     // IPv6:
-    EXPECT_TRUE(parse_tcp_socket_spec("tcp:[::1]:1234", &hostname, &port, &error));
+    EXPECT_TRUE(parse_tcp_socket_spec("tcp:[::1]:1234", &hostname, &port, &serial, &error));
     EXPECT_EQ("::1", hostname);
     EXPECT_EQ(1234, port);
+    EXPECT_EQ("[::1]:1234", serial);
 
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:-1", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:::1", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:::1:1234", &hostname, &port, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:-1", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:::1", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:::1:1234", &hostname, &port, &serial, &error));
 }
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index 6b40056..5e28f76 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -34,6 +34,9 @@
 #include "sysdeps.h"
 #include "sysdeps/chrono.h"
 
+using namespace std::string_literals;
+using namespace std::string_view_literals;
+
 struct ThreadArg {
     int first_read_fd;
     int last_write_fd;
@@ -42,8 +45,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];
@@ -57,7 +58,7 @@
     intermediates.resize(INTERMEDIATE_COUNT);
     ASSERT_EQ(0, adb_socketpair(first)) << strerror(errno);
     ASSERT_EQ(0, adb_socketpair(last)) << strerror(errno);
-    asocket* prev_tail = create_local_socket(first[1]);
+    asocket* prev_tail = create_local_socket(unique_fd(first[1]));
     ASSERT_NE(nullptr, prev_tail);
 
     auto connect = [](asocket* tail, asocket* head) {
@@ -69,22 +70,21 @@
     for (auto& intermediate : intermediates) {
         ASSERT_EQ(0, adb_socketpair(intermediate.data())) << strerror(errno);
 
-        asocket* head = create_local_socket(intermediate[0]);
+        asocket* head = create_local_socket(unique_fd(intermediate[0]));
         ASSERT_NE(nullptr, head);
 
-        asocket* tail = create_local_socket(intermediate[1]);
+        asocket* tail = create_local_socket(unique_fd(intermediate[1]));
         ASSERT_NE(nullptr, tail);
 
         connect(prev_tail, head);
         prev_tail = tail;
     }
 
-    asocket* end = create_local_socket(last[0]);
+    asocket* end = create_local_socket(unique_fd(last[0]));
     ASSERT_NE(nullptr, end);
     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,35 +98,50 @@
     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 {
-    int socket_fd;
+    unique_fd socket_fd;
     size_t bytes_written;
-    int cause_close_fd;
+    unique_fd cause_close_fd;
 };
 
-static void CloseWithPacketThreadFunc(CloseWithPacketArg* arg) {
-    asocket* s = create_local_socket(arg->socket_fd);
-    ASSERT_TRUE(s != nullptr);
-    arg->bytes_written = 0;
+static void CreateCloser(CloseWithPacketArg* arg) {
+    fdevent_run_on_main_thread([arg]() {
+        asocket* s = create_local_socket(std::move(arg->socket_fd));
+        ASSERT_TRUE(s != nullptr);
+        arg->bytes_written = 0;
 
-    std::string data;
-    data.resize(MAX_PAYLOAD);
-    arg->bytes_written += data.size();
-    int ret = s->enqueue(s, std::move(data));
-    ASSERT_EQ(1, ret);
+        // 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);
 
-    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);
+            std::this_thread::sleep_for(250ms);
+        }
+        ASSERT_TRUE(socket_filled);
 
-    fdevent_loop();
+        asocket* cause_close_s = create_local_socket(std::move(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:
@@ -139,20 +154,21 @@
     int cause_close_fd[2];
     ASSERT_EQ(0, adb_socketpair(cause_close_fd));
     CloseWithPacketArg arg;
-    arg.socket_fd = socket_fd[1];
-    arg.cause_close_fd = cause_close_fd[1];
+    arg.socket_fd.reset(socket_fd[1]);
+    arg.cause_close_fd.reset(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.
@@ -162,15 +178,16 @@
     int cause_close_fd[2];
     ASSERT_EQ(0, adb_socketpair(cause_close_fd));
     CloseWithPacketArg arg;
-    arg.socket_fd = socket_fd[1];
-    arg.cause_close_fd = cause_close_fd[1];
+    arg.socket_fd.reset(socket_fd[1]);
+    arg.cause_close_fd.reset(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.
@@ -179,9 +196,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:
@@ -194,19 +211,21 @@
     int cause_close_fd[2];
     ASSERT_EQ(0, adb_socketpair(cause_close_fd));
     CloseWithPacketArg arg;
-    arg.socket_fd = socket_fd[1];
-    arg.cause_close_fd = cause_close_fd[1];
+    arg.socket_fd.reset(socket_fd[1]);
+    arg.cause_close_fd.reset(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);
+    std::this_thread::sleep_for(2s);
+
+    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.
@@ -216,8 +235,8 @@
     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]);
+    asocket* head = create_local_socket(unique_fd(head_fd[1]));
+    asocket* tail = create_local_socket(unique_fd(tail_fd[1]));
 
     head->peer = tail;
     head->ready(head);
@@ -226,7 +245,6 @@
     tail->ready(tail);
 
     PrepareThread();
-    std::thread thread(fdevent_loop);
 
     EXPECT_TRUE(WriteFdExactly(head_fd[0], "foo", 3));
 
@@ -242,10 +260,9 @@
     adb_close(head_fd[0]);
     adb_close(tail_fd[0]);
 
-    // Wait until the local sockets are closed.
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+    WaitForFdeventLoop();
     ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
-    TerminateThread(thread);
+    TerminateThread();
 }
 
 #if defined(__linux__)
@@ -254,21 +271,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;
@@ -279,78 +285,101 @@
 
     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(unique_fd(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__)
 
 #if ADB_HOST
 
-// Checks that skip_host_serial(serial) returns a pointer to the part of |serial| which matches
-// |expected|, otherwise logs the failure to gtest.
-void VerifySkipHostSerial(std::string serial, const char* expected) {
-    char* result = internal::skip_host_serial(&serial[0]);
-    if (expected == nullptr) {
-        EXPECT_EQ(nullptr, result);
-    } else {
-        EXPECT_STREQ(expected, result);
-    }
-}
+#define VerifyParseHostServiceFailed(s)                                         \
+    do {                                                                        \
+        std::string service(s);                                                 \
+        std::string_view serial, command;                                       \
+        bool result = internal::parse_host_service(&serial, &command, service); \
+        EXPECT_FALSE(result);                                                   \
+    } while (0)
+
+#define VerifyParseHostService(s, expected_serial, expected_command)            \
+    do {                                                                        \
+        std::string service(s);                                                 \
+        std::string_view serial, command;                                       \
+        bool result = internal::parse_host_service(&serial, &command, service); \
+        EXPECT_TRUE(result);                                                    \
+        EXPECT_EQ(std::string(expected_serial), std::string(serial));           \
+        EXPECT_EQ(std::string(expected_command), std::string(command));         \
+    } while (0);
 
 // Check [tcp:|udp:]<serial>[:<port>]:<command> format.
-TEST(socket_test, test_skip_host_serial) {
+TEST(socket_test, test_parse_host_service) {
     for (const std::string& protocol : {"", "tcp:", "udp:"}) {
-        VerifySkipHostSerial(protocol, nullptr);
-        VerifySkipHostSerial(protocol + "foo", nullptr);
+        VerifyParseHostServiceFailed(protocol);
+        VerifyParseHostServiceFailed(protocol + "foo");
 
-        VerifySkipHostSerial(protocol + "foo:bar", ":bar");
-        VerifySkipHostSerial(protocol + "foo:bar:baz", ":bar:baz");
+        {
+            std::string serial = protocol + "foo";
+            VerifyParseHostService(serial + ":bar", serial, "bar");
+            VerifyParseHostService(serial + " :bar:baz", serial, "bar:baz");
+        }
 
-        VerifySkipHostSerial(protocol + "foo:123:bar", ":bar");
-        VerifySkipHostSerial(protocol + "foo:123:456", ":456");
-        VerifySkipHostSerial(protocol + "foo:123:bar:baz", ":bar:baz");
+        {
+            // With port.
+            std::string serial = protocol + "foo:123";
+            VerifyParseHostService(serial + ":bar", serial, "bar");
+            VerifyParseHostService(serial + ":456", serial, "456");
+            VerifyParseHostService(serial + ":bar:baz", serial, "bar:baz");
+        }
 
         // Don't register a port unless it's all numbers and ends with ':'.
-        VerifySkipHostSerial(protocol + "foo:123", ":123");
-        VerifySkipHostSerial(protocol + "foo:123bar:baz", ":123bar:baz");
+        VerifyParseHostService(protocol + "foo:123", protocol + "foo", "123");
+        VerifyParseHostService(protocol + "foo:123bar:baz", protocol + "foo", "123bar:baz");
 
-        VerifySkipHostSerial(protocol + "100.100.100.100:5555:foo", ":foo");
-        VerifySkipHostSerial(protocol + "[0123:4567:89ab:CDEF:0:9:a:f]:5555:foo", ":foo");
-        VerifySkipHostSerial(protocol + "[::1]:5555:foo", ":foo");
+        std::string addresses[] = {"100.100.100.100", "[0123:4567:89ab:CDEF:0:9:a:f]", "[::1]"};
+        for (const std::string& address : addresses) {
+            std::string serial = protocol + address;
+            std::string serial_with_port = protocol + address + ":5555";
+            VerifyParseHostService(serial + ":foo", serial, "foo");
+            VerifyParseHostService(serial_with_port + ":foo", serial_with_port, "foo");
+        }
 
         // If we can't find both [] then treat it as a normal serial with [ in it.
-        VerifySkipHostSerial(protocol + "[0123:foo", ":foo");
+        VerifyParseHostService(protocol + "[0123:foo", protocol + "[0123", "foo");
 
         // Don't be fooled by random IPv6 addresses in the command string.
-        VerifySkipHostSerial(protocol + "foo:ping [0123:4567:89ab:CDEF:0:9:a:f]:5555",
-                             ":ping [0123:4567:89ab:CDEF:0:9:a:f]:5555");
+        VerifyParseHostService(protocol + "foo:ping [0123:4567:89ab:CDEF:0:9:a:f]:5555",
+                               protocol + "foo", "ping [0123:4567:89ab:CDEF:0:9:a:f]:5555");
+
+        // Handle embedded NULs properly.
+        VerifyParseHostService(protocol + "foo:echo foo\0bar"s, protocol + "foo",
+                               "echo foo\0bar"sv);
     }
 }
 
 // Check <prefix>:<serial>:<command> format.
-TEST(socket_test, test_skip_host_serial_prefix) {
+TEST(socket_test, test_parse_host_service_prefix) {
     for (const std::string& prefix : {"usb:", "product:", "model:", "device:"}) {
-        VerifySkipHostSerial(prefix, nullptr);
-        VerifySkipHostSerial(prefix + "foo", nullptr);
+        VerifyParseHostServiceFailed(prefix);
+        VerifyParseHostServiceFailed(prefix + "foo");
 
-        VerifySkipHostSerial(prefix + "foo:bar", ":bar");
-        VerifySkipHostSerial(prefix + "foo:bar:baz", ":bar:baz");
-        VerifySkipHostSerial(prefix + "foo:123:bar", ":123:bar");
+        VerifyParseHostService(prefix + "foo:bar", prefix + "foo", "bar");
+        VerifyParseHostService(prefix + "foo:bar:baz", prefix + "foo", "bar:baz");
+        VerifyParseHostService(prefix + "foo:123:bar", prefix + "foo", "123:bar");
     }
 }
 
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 04bd080..75993b3 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -26,10 +26,13 @@
 #include <unistd.h>
 
 #include <algorithm>
+#include <chrono>
 #include <mutex>
 #include <string>
 #include <vector>
 
+#include <android-base/strings.h>
+
 #if !ADB_HOST
 #include <android-base/properties.h>
 #include <log/log_properties.h>
@@ -37,8 +40,11 @@
 
 #include "adb.h"
 #include "adb_io.h"
-#include "range.h"
+#include "adb_utils.h"
 #include "transport.h"
+#include "types.h"
+
+using namespace std::chrono_literals;
 
 static std::recursive_mutex& local_socket_list_lock = *new std::recursive_mutex();
 static unsigned local_socket_next_id = 1;
@@ -78,7 +84,7 @@
 
     // Socket ids should never be 0.
     if (local_socket_next_id == 0) {
-        fatal("local socket id overflow");
+        LOG(FATAL) << "local socket id overflow";
     }
 
     local_socket_list.push_back(s);
@@ -113,25 +119,24 @@
 };
 
 static SocketFlushResult local_socket_flush_incoming(asocket* s) {
-    while (!s->packet_queue.empty()) {
-        Range& r = s->packet_queue.front();
-
-        int rc = adb_write(s->fd, r.data(), r.size());
-        if (rc == static_cast<int>(r.size())) {
-            s->packet_queue.pop_front();
+    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) {
-            r.drop_front(rc);
-            fdevent_add(&s->fde, FDE_WRITE);
+            // 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);
+            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;
         }
-
-        // We failed to write, but it's possible that we can still read from the socket.
-        // Give that a try before giving up.
-        s->has_write_error = true;
-        break;
     }
 
     // If we sent the last packet of a closing socket, we can now destroy it.
@@ -140,14 +145,14 @@
         return SocketFlushResult::Destroyed;
     }
 
-    fdevent_del(&s->fde, FDE_WRITE);
+    fdevent_del(s->fde, FDE_WRITE);
     return SocketFlushResult::Completed;
 }
 
 // Returns false if the socket has been closed and destroyed as a side-effect of this function.
 static bool local_socket_flush_outgoing(asocket* s) {
     const size_t max_payload = s->get_max_payload();
-    std::string data;
+    apacket::payload_type data;
     data.resize(max_payload);
     char* x = &data[0];
     size_t avail = max_payload;
@@ -173,7 +178,7 @@
         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);
+      s->fde->force_eof);
 
     if (avail != max_payload && s->peer) {
         data.resize(max_payload - avail);
@@ -200,13 +205,13 @@
             ** we disable notification of READs.  They'll
             ** be enabled again when we get a call to ready()
             */
-            fdevent_del(&s->fde, FDE_READ);
+            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);
+    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;
     }
@@ -214,11 +219,10 @@
     return true;
 }
 
-static int local_socket_enqueue(asocket* s, std::string data) {
+static int local_socket_enqueue(asocket* s, apacket::payload_type data) {
     D("LS(%d): enqueue %zu", s->id, data.size());
 
-    Range r(std::move(data));
-    s->packet_queue.push_back(std::move(r));
+    s->packet_queue.append(std::move(data));
     switch (local_socket_flush_incoming(s)) {
         case SocketFlushResult::Destroyed:
             return -1;
@@ -236,19 +240,67 @@
 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);
+}
+
+struct ClosingSocket {
+    std::chrono::steady_clock::time_point begin;
+};
+
+// The standard (RFC 1122 - 4.2.2.13) says that if we call close on a
+// socket while we have pending data, a TCP RST should be sent to the
+// other end to notify it that we didn't read all of its data. However,
+// this can result in data that we've successfully written out to be dropped
+// on the other end. To avoid this, instead of immediately closing a
+// socket, call shutdown on it instead, and then read from the file
+// descriptor until we hit EOF or an error before closing.
+static void deferred_close(unique_fd fd) {
+    // Shutdown the socket in the outgoing direction only, so that
+    // we don't have the same problem on the opposite end.
+    adb_shutdown(fd.get(), SHUT_WR);
+    auto callback = [](fdevent* fde, unsigned event, void* arg) {
+        auto socket_info = static_cast<ClosingSocket*>(arg);
+        if (event & FDE_READ) {
+            ssize_t rc;
+            char buf[BUFSIZ];
+            while ((rc = adb_read(fde->fd.get(), buf, sizeof(buf))) > 0) {
+                continue;
+            }
+
+            if (rc == -1 && errno == EAGAIN) {
+                // There's potentially more data to read.
+                auto duration = std::chrono::steady_clock::now() - socket_info->begin;
+                if (duration > 1s) {
+                    LOG(WARNING) << "timeout expired while flushing socket, closing";
+                } else {
+                    return;
+                }
+            }
+        } else if (event & FDE_TIMEOUT) {
+            LOG(WARNING) << "timeout expired while flushing socket, closing";
+        }
+
+        // Either there was an error, we hit the end of the socket, or our timeout expired.
+        fdevent_destroy(fde);
+        delete socket_info;
+    };
+
+    ClosingSocket* socket_info = new ClosingSocket{
+            .begin = std::chrono::steady_clock::now(),
+    };
+
+    fdevent* fde = fdevent_create(fd.release(), callback, socket_info);
+    fdevent_add(fde, FDE_READ);
+    fdevent_set_timeout(fde, 1s);
 }
 
 // be sure to hold the socket list lock when calling this
 static void local_socket_destroy(asocket* s) {
     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);
+    deferred_close(fdevent_release(s->fde));
 
     remove_socket(s);
     delete s;
@@ -290,11 +342,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);
     local_socket_closing_list.push_back(s);
-    CHECK_EQ(FDE_WRITE, s->fde.state & FDE_WRITE);
+    CHECK_EQ(FDE_WRITE, s->fde->state & FDE_WRITE);
 }
 
 static void local_socket_event_func(int fd, unsigned ev, void* _s) {
@@ -334,42 +386,40 @@
     }
 }
 
-asocket* create_local_socket(int fd) {
+asocket* create_local_socket(unique_fd ufd) {
+    int fd = ufd.release();
     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, atransport* transport) {
+asocket* create_local_service_socket(std::string_view name, atransport* transport) {
 #if !ADB_HOST
-    if (!strcmp(name, "jdwp")) {
-        return create_jdwp_service_socket();
-    }
-    if (!strcmp(name, "track-jdwp")) {
-        return create_jdwp_tracker_service_socket();
+    if (asocket* s = daemon_service_to_socket(name); s) {
+        return s;
     }
 #endif
-    int fd = service_to_fd(name, transport);
+    unique_fd fd = service_to_fd(name, transport);
     if (fd < 0) {
         return nullptr;
     }
 
-    asocket* s = create_local_socket(fd);
-    D("LS(%d): bound to '%s' via %d", s->id, name, fd);
+    int fd_value = fd.get();
+    asocket* s = create_local_socket(std::move(fd));
+    LOG(VERBOSE) << "LS(" << s->id << "): bound to '" << name << "' via " << fd_value;
 
 #if !ADB_HOST
-    if ((!strncmp(name, "root:", 5) && getuid() != 0 && __android_log_is_debuggable()) ||
-        (!strncmp(name, "unroot:", 7) && getuid() == 0) ||
-        !strncmp(name, "usb:", 4) ||
-        !strncmp(name, "tcpip:", 6)) {
+    if ((name.starts_with("root:") && getuid() != 0 && __android_log_is_debuggable()) ||
+        (name.starts_with("unroot:") && getuid() == 0) || name.starts_with("usb:") ||
+        name.starts_with("tcpip:")) {
         D("LS(%d): enabling exit_on_close", s->id);
         s->exit_on_close = 1;
     }
@@ -378,23 +428,7 @@
     return s;
 }
 
-#if ADB_HOST
-static asocket* create_host_service_socket(const char* name, const char* serial,
-                                           TransportId transport_id) {
-    asocket* s;
-
-    s = host_service_to_socket(name, serial, transport_id);
-
-    if (s != NULL) {
-        D("LS(%d) bound to '%s'", s->id, name);
-        return s;
-    }
-
-    return s;
-}
-#endif /* ADB_HOST */
-
-static int remote_socket_enqueue(asocket* s, std::string data) {
+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();
 
@@ -437,7 +471,7 @@
 
 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);
     }
@@ -453,7 +487,7 @@
 // Returns a new non-NULL asocket handle.
 asocket* create_remote_socket(unsigned id, atransport* t) {
     if (id == 0) {
-        fatal("invalid remote socket id (0)");
+        LOG(FATAL) << "invalid remote socket id (0)";
     }
     asocket* s = new asocket();
     s->id = id;
@@ -467,22 +501,22 @@
     return s;
 }
 
-void connect_to_remote(asocket* s, const char* destination) {
+void connect_to_remote(asocket* s, std::string_view destination) {
     D("Connect_to_remote call RS(%d) fd=%d", s->id, s->fd);
     apacket* p = get_apacket();
 
-    D("LS(%d): connect('%s')", s->id, destination);
+    LOG(VERBOSE) << "LS(" << s->id << ": connect(" << destination << ")";
     p->msg.command = A_OPEN;
     p->msg.arg0 = s->id;
 
-    // adbd expects a null-terminated string.
-    p->payload = destination;
-    p->payload.push_back('\0');
+    // adbd used to expect a null-terminated string.
+    // Keep doing so to maintain backward compatibility.
+    p->payload.resize(destination.size() + 1);
+    memcpy(p->payload.data(), destination.data(), destination.size());
+    p->payload[destination.size()] = '\0';
     p->msg.data_length = p->payload.size();
 
-    if (p->msg.data_length > s->get_max_payload()) {
-        fatal("destination oversized");
-    }
+    CHECK_LE(p->msg.data_length, s->get_max_payload());
 
     send_packet(p, s->transport);
 }
@@ -491,7 +525,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);
@@ -502,7 +536,7 @@
    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);
@@ -555,67 +589,137 @@
 
 namespace internal {
 
-// Returns the position in |service| following the target serial parameter. Serial format can be
-// any of:
+// Parses a host service string of the following format:
 //   * [tcp:|udp:]<serial>[:<port>]:<command>
 //   * <prefix>:<serial>:<command>
 // Where <port> must be a base-10 number and <prefix> may be any of {usb,product,model,device}.
-//
-// The returned pointer will point to the ':' just before <command>, or nullptr if not found.
-char* skip_host_serial(char* service) {
-    static const std::vector<std::string>& prefixes =
-        *(new std::vector<std::string>{"usb:", "product:", "model:", "device:"});
+bool parse_host_service(std::string_view* out_serial, std::string_view* out_command,
+                        std::string_view full_service) {
+    if (full_service.empty()) {
+        return false;
+    }
 
-    for (const std::string& prefix : prefixes) {
-        if (!strncmp(service, prefix.c_str(), prefix.length())) {
-            return strchr(service + prefix.length(), ':');
+    std::string_view serial;
+    std::string_view command = full_service;
+    // Remove |count| bytes from the beginning of command and add them to |serial|.
+    auto consume = [&full_service, &serial, &command](size_t count) {
+        CHECK_LE(count, command.size());
+        if (!serial.empty()) {
+            CHECK_EQ(serial.data() + serial.size(), command.data());
+        }
+
+        serial = full_service.substr(0, serial.size() + count);
+        command.remove_prefix(count);
+    };
+
+    // Remove the trailing : from serial, and assign the values to the output parameters.
+    auto finish = [out_serial, out_command, &serial, &command] {
+        if (serial.empty() || command.empty()) {
+            return false;
+        }
+
+        CHECK_EQ(':', serial.back());
+        serial.remove_suffix(1);
+
+        *out_serial = serial;
+        *out_command = command;
+        return true;
+    };
+
+    static constexpr std::string_view prefixes[] = {"usb:", "product:", "model:", "device:"};
+    for (std::string_view prefix : prefixes) {
+        if (command.starts_with(prefix)) {
+            consume(prefix.size());
+
+            size_t offset = command.find_first_of(':');
+            if (offset == std::string::npos) {
+                return false;
+            }
+            consume(offset + 1);
+            return finish();
         }
     }
 
     // For fastboot compatibility, ignore protocol prefixes.
-    if (!strncmp(service, "tcp:", 4) || !strncmp(service, "udp:", 4)) {
-        service += 4;
+    if (command.starts_with("tcp:") || command.starts_with("udp:")) {
+        consume(4);
+        if (command.empty()) {
+            return false;
+        }
+    }
+    if (command.starts_with("vsock:")) {
+        // vsock serials are vsock:cid:port, which have an extra colon compared to tcp.
+        size_t next_colon = command.find(':');
+        if (next_colon == std::string::npos) {
+            return false;
+        }
+        consume(next_colon + 1);
     }
 
-    // Check for an IPv6 address. `adb connect` creates the serial number from the canonical
-    // network address so it will always have the [] delimiters.
-    if (service[0] == '[') {
-        char* ipv6_end = strchr(service, ']');
-        if (ipv6_end != nullptr) {
-            service = ipv6_end;
+    bool found_address = false;
+    if (command[0] == '[') {
+        // Read an IPv6 address. `adb connect` creates the serial number from the canonical
+        // network address so it will always have the [] delimiters.
+        size_t ipv6_end = command.find_first_of(']');
+        if (ipv6_end != std::string::npos) {
+            consume(ipv6_end + 1);
+            if (command.empty()) {
+                // Nothing after the IPv6 address.
+                return false;
+            } else if (command[0] != ':') {
+                // Garbage after the IPv6 address.
+                return false;
+            }
+            consume(1);
+            found_address = true;
         }
     }
 
-    // The next colon we find must either begin the port field or the command field.
-    char* colon_ptr = strchr(service, ':');
-    if (!colon_ptr) {
-        // No colon in service string.
-        return nullptr;
+    if (!found_address) {
+        // Scan ahead to the next colon.
+        size_t offset = command.find_first_of(':');
+        if (offset == std::string::npos) {
+            return false;
+        }
+        consume(offset + 1);
     }
 
-    // If the next field is only decimal digits and ends with another colon, it's a port.
-    char* serial_end = colon_ptr;
-    if (isdigit(serial_end[1])) {
-        serial_end++;
-        while (*serial_end && isdigit(*serial_end)) {
-            serial_end++;
-        }
-        if (*serial_end != ':') {
-            // Something other than "<port>:" was found, this must be the command field instead.
-            serial_end = colon_ptr;
+    // We're either at the beginning of a port, or the command itself.
+    // Look for a port in between colons.
+    size_t next_colon = command.find_first_of(':');
+    if (next_colon == std::string::npos) {
+        // No colon, we must be at the command.
+        return finish();
+    }
+
+    bool port_valid = true;
+    if (command.size() <= next_colon) {
+        return false;
+    }
+
+    std::string_view port = command.substr(0, next_colon);
+    for (auto digit : port) {
+        if (!isdigit(digit)) {
+            // Port isn't a number.
+            port_valid = false;
+            break;
         }
     }
-    return serial_end;
+
+    if (port_valid) {
+        consume(next_colon + 1);
+    }
+    return finish();
 }
 
 }  // namespace internal
 
 #endif  // ADB_HOST
 
-static int smart_socket_enqueue(asocket* s, std::string data) {
+static int smart_socket_enqueue(asocket* s, apacket::payload_type data) {
 #if ADB_HOST
-    char* service = nullptr;
-    char* serial = nullptr;
+    std::string_view service;
+    std::string_view serial;
     TransportId transport_id = 0;
     TransportType type = kTransportAny;
 #endif
@@ -623,7 +727,8 @@
     D("SS(%d): enqueue %zu", s->id, data.size());
 
     if (s->smart_socket_data.empty()) {
-        s->smart_socket_data = std::move(data);
+        // TODO: Make this an IOVector?
+        s->smart_socket_data.assign(data.begin(), data.end());
     } else {
         std::copy(data.begin(), data.end(), std::back_inserter(s->smart_socket_data));
     }
@@ -651,65 +756,63 @@
     D("SS(%d): '%s'", s->id, (char*)(s->smart_socket_data.data() + 4));
 
 #if ADB_HOST
-    service = &s->smart_socket_data[4];
-    if (!strncmp(service, "host-serial:", strlen("host-serial:"))) {
-        char* serial_end;
-        service += strlen("host-serial:");
-
+    service = std::string_view(s->smart_socket_data).substr(4);
+    if (android::base::ConsumePrefix(&service, "host-serial:")) {
         // serial number should follow "host:" and could be a host:port string.
-        serial_end = internal::skip_host_serial(service);
-        if (serial_end) {
-            *serial_end = 0;  // terminate string
-            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:");
-    } else if (!strncmp(service, "host-local:", strlen("host-local:"))) {
-        type = kTransportLocal;
-        service += strlen("host-local:");
-    } else if (!strncmp(service, "host:", strlen("host:"))) {
-        type = kTransportAny;
-        service += strlen("host:");
-    } else {
-        service = nullptr;
-    }
-
-    if (service) {
-        asocket* s2;
-
-        /* some requests are handled immediately -- in that
-        ** case the handle_host_request() routine has sent
-        ** the OKAY or FAIL message and all we have to do
-        ** is clean up.
-        */
-        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);
+        if (!internal::parse_host_service(&serial, &service, service)) {
+            LOG(ERROR) << "SS(" << s->id << "): failed to parse host service: " << service;
             goto fail;
         }
-        if (!strncmp(service, "transport", strlen("transport"))) {
-            D("SS(%d): okay transport", s->id);
-            s->smart_socket_data.clear();
-            return 0;
+    } else if (android::base::ConsumePrefix(&service, "host-transport-id:")) {
+        if (!ParseUint(&transport_id, service, &service)) {
+            LOG(ERROR) << "SS(" << s->id << "): failed to parse host transport id: " << service;
+            return -1;
+        }
+        if (!android::base::ConsumePrefix(&service, ":")) {
+            LOG(ERROR) << "SS(" << s->id << "): host-transport-id without command";
+            return -1;
+        }
+    } else if (android::base::ConsumePrefix(&service, "host-usb:")) {
+        type = kTransportUsb;
+    } else if (android::base::ConsumePrefix(&service, "host-local:")) {
+        type = kTransportLocal;
+    } else if (android::base::ConsumePrefix(&service, "host:")) {
+        type = kTransportAny;
+    } else {
+        service = std::string_view{};
+    }
+
+    if (!service.empty()) {
+        asocket* s2;
+
+        // Some requests are handled immediately -- in that case the handle_host_request() routine
+        // has sent the OKAY or FAIL message and all we have to do is clean up.
+        auto host_request_result = handle_host_request(
+                service, type, serial.empty() ? nullptr : std::string(serial).c_str(), transport_id,
+                s->peer->fd, s);
+
+        switch (host_request_result) {
+            case HostRequestResult::Handled:
+                LOG(VERBOSE) << "SS(" << s->id << "): handled host service '" << service << "'";
+                goto fail;
+
+            case HostRequestResult::SwitchedTransport:
+                D("SS(%d): okay transport", s->id);
+                s->smart_socket_data.clear();
+                return 0;
+
+            case HostRequestResult::Unhandled:
+                break;
         }
 
         /* try to find a local service with this name.
         ** if no such service exists, we'll fail out
         ** and tear down here.
         */
-        s2 = create_host_service_socket(service, serial, transport_id);
-        if (s2 == 0) {
-            D("SS(%d): couldn't create host service '%s'", s->id, service);
+        // TODO: Convert to string_view.
+        s2 = host_service_to_socket(service, serial, transport_id);
+        if (s2 == nullptr) {
+            LOG(VERBOSE) << "SS(" << s->id << "): couldn't create host service '" << service << "'";
             SendFail(s->peer->fd, "unknown host service");
             goto fail;
         }
@@ -728,7 +831,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);
 
@@ -750,7 +853,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
          */
@@ -766,12 +869,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, s->smart_socket_data.data() + 4);
-    s->peer = 0;
+    connect_to_remote(s->peer, std::string_view(s->smart_socket_data).substr(4));
+    s->peer = nullptr;
     s->close(s);
     return 1;
 
@@ -791,9 +894,9 @@
 static void smart_socket_close(asocket* s) {
     D("SS(%d): closed", s->id);
     if (s->peer) {
-        s->peer->peer = 0;
+        s->peer->peer = nullptr;
         s->peer->close(s->peer);
-        s->peer = 0;
+        s->peer = nullptr;
     }
     delete s;
 }
@@ -803,7 +906,7 @@
     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 edeacc1..78abba5 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -27,6 +27,7 @@
 #include <errno.h>
 
 #include <string>
+#include <string_view>
 #include <vector>
 
 // Include this before open/close/unlink are defined as macros below.
@@ -35,23 +36,11 @@
 #include <android-base/unique_fd.h>
 #include <android-base/utf8.h>
 
+#include "adb_unique_fd.h"
 #include "sysdeps/errno.h"
 #include "sysdeps/network.h"
 #include "sysdeps/stat.h"
 
-// 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
@@ -65,10 +54,11 @@
 #include <fcntl.h>
 #include <io.h>
 #include <process.h>
+#include <stdint.h>
 #include <sys/stat.h>
 #include <utime.h>
-#include <winsock2.h>
 #include <windows.h>
+#include <winsock2.h>
 #include <ws2tcpip.h>
 
 #include <memory>   // unique_ptr
@@ -85,54 +75,42 @@
     return c == '\\' || c == '/';
 }
 
-static __inline__ int adb_thread_setname(const std::string& name) {
-    // TODO: See https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx for how to set
-    // the thread name in Windows. Unfortunately, it only works during debugging, but
-    // our build process doesn't generate PDB files needed for debugging.
-    return 0;
-}
+extern int adb_thread_setname(const std::string& name);
 
-static __inline__  unsigned long adb_thread_id()
-{
-    return GetCurrentThreadId();
-}
-
-static __inline__ void  close_on_exec(int  fd)
-{
+static __inline__ void close_on_exec(borrowed_fd fd) {
     /* nothing really */
 }
 
-extern int  adb_unlink(const char*  path);
-#undef  unlink
-#define unlink  ___xxx_unlink
+extern int adb_unlink(const char* path);
+#undef unlink
+#define unlink ___xxx_unlink
 
 extern int adb_mkdir(const std::string& path, int mode);
-#undef   mkdir
-#define  mkdir  ___xxx_mkdir
+#undef mkdir
+#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, int direction = SHUT_RDWR);
+extern int adb_read(borrowed_fd fd, void* buf, int len);
+extern int adb_write(borrowed_fd fd, const void* buf, int len);
+extern int64_t adb_lseek(borrowed_fd fd, int64_t pos, int where);
+extern int adb_shutdown(borrowed_fd 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)
-{
+static __inline__ int unix_close(int fd) {
     return close(fd);
 }
-#undef   close
-#define  close   ____xxx_close
+#undef close
+#define close ____xxx_close
 
 // Like unix_read(), but may return EINTR.
-extern int  unix_read_interruptible(int  fd, void*  buf, size_t  len);
+extern int unix_read_interruptible(borrowed_fd fd, void* buf, size_t len);
 
 // See the comments for the !defined(_WIN32) version of unix_read().
-static __inline__ int unix_read(int fd, void* buf, size_t len) {
+static __inline__ int unix_read(borrowed_fd fd, void* buf, size_t len) {
     return TEMP_FAILURE_RETRY(unix_read_interruptible(fd, buf, len));
 }
 
@@ -140,21 +118,26 @@
 #define  read  ___xxx_read
 
 // See the comments for the !defined(_WIN32) version of unix_write().
-static __inline__  int  unix_write(int  fd, const void*  buf, size_t  len)
-{
-    return write(fd, buf, len);
+static __inline__ int unix_write(borrowed_fd fd, const void* buf, size_t len) {
+    return write(fd.get(), buf, len);
 }
 #undef   write
 #define  write  ___xxx_write
 
+// See the comments for the !defined(_WIN32) version of unix_lseek().
+static __inline__ int unix_lseek(borrowed_fd fd, int pos, int where) {
+    return lseek(fd.get(), pos, where);
+}
+#undef lseek
+#define lseek ___xxx_lseek
+
 // See the comments for the !defined(_WIN32) version of adb_open_mode().
-static __inline__ int  adb_open_mode(const char* path, int options, int mode)
-{
+static __inline__ int adb_open_mode(const char* path, int options, int mode) {
     return adb_open(path, options);
 }
 
 // See the comments for the !defined(_WIN32) version of unix_open().
-extern int unix_open(const char* path, int options, ...);
+extern int unix_open(std::string_view path, int options, ...);
 #define  open    ___xxx_unix_open
 
 // Checks if |fd| corresponds to a console.
@@ -166,7 +149,7 @@
 // with |fd| must have GENERIC_READ access (which console FDs have by default).
 // Returns 1 if |fd| is a console FD, 0 otherwise. The value of errno after
 // calling this function is unreliable and should not be used.
-int unix_isatty(int fd);
+int unix_isatty(borrowed_fd fd);
 #define  isatty  ___xxx_isatty
 
 int network_inaddr_any_server(int port, int type, std::string* error);
@@ -182,20 +165,21 @@
 int network_connect(const std::string& host, int port, int type, int timeout,
                     std::string* error);
 
-extern int  adb_socket_accept(int  serverfd, struct sockaddr*  addr, socklen_t  *addrlen);
+extern int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr, socklen_t* addrlen);
 
 #undef   accept
 #define  accept  ___xxx_accept
 
 // Returns the local port number of a bound socket, or -1 on failure.
-int adb_socket_get_local_port(int fd);
+int adb_socket_get_local_port(borrowed_fd fd);
 
-extern int  adb_setsockopt(int  fd, int  level, int  optname, const void*  optval, socklen_t  optlen);
+extern int adb_setsockopt(borrowed_fd fd, int level, int optname, const void* optval,
+                          socklen_t optlen);
 
 #undef   setsockopt
 #define  setsockopt  ___xxx_setsockopt
 
-extern int  adb_socketpair( int  sv[2] );
+extern int adb_socketpair(int sv[2]);
 
 struct adb_pollfd {
     int fd;
@@ -217,21 +201,18 @@
 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);
 extern int adb_putchar(int ch);
 extern int adb_puts(const char* buf);
-extern size_t adb_fwrite(const void* ptr, size_t size, size_t nmemb,
-                         FILE* stream);
+extern size_t adb_fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream);
 
 extern FILE* adb_fopen(const char* f, const char* m);
 
@@ -333,26 +314,24 @@
 
 #else /* !_WIN32 a.k.a. Unix */
 
-#include <cutils/sockets.h>
-#include <cutils/threads.h>
 #include <fcntl.h>
-#include <poll.h>
-#include <signal.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-
-#include <pthread.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdarg.h>
 #include <netdb.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
+#include <poll.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdint.h>
 #include <string.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
 #include <unistd.h>
 
 #include <string>
 
+#include <cutils/sockets.h>
+
 #define OS_PATH_SEPARATORS "/"
 #define OS_PATH_SEPARATOR '/'
 #define OS_PATH_SEPARATOR_STR "/"
@@ -362,9 +341,8 @@
     return c == '/';
 }
 
-static __inline__ void  close_on_exec(int  fd)
-{
-    fcntl( fd, F_SETFD, FD_CLOEXEC );
+static __inline__ void close_on_exec(borrowed_fd fd) {
+    fcntl(fd.get(), F_SETFD, FD_CLOEXEC);
 }
 
 // Open a file and return a file descriptor that may be used with unix_read(),
@@ -376,31 +354,26 @@
 // by unix_read(), unix_write(), unix_close()). Also, the C Runtime has
 // configurable CR/LF translation which defaults to text mode, but is settable
 // with _setmode().
-static __inline__ int  unix_open(const char*  path, int options,...)
-{
-    if ((options & O_CREAT) == 0)
-    {
-        return  TEMP_FAILURE_RETRY( open(path, options) );
-    }
-    else
-    {
-        int      mode;
-        va_list  args;
-        va_start( args, options );
-        mode = va_arg( args, int );
-        va_end( args );
-        return TEMP_FAILURE_RETRY( open( path, options, mode ) );
+static __inline__ int unix_open(std::string_view path, int options, ...) {
+    std::string zero_terminated(path.begin(), path.end());
+    if ((options & O_CREAT) == 0) {
+        return TEMP_FAILURE_RETRY(open(zero_terminated.c_str(), options));
+    } else {
+        int mode;
+        va_list args;
+        va_start(args, options);
+        mode = va_arg(args, int);
+        va_end(args);
+        return TEMP_FAILURE_RETRY(open(zero_terminated.c_str(), options, mode));
     }
 }
 
 // Similar to the two-argument adb_open(), but takes a mode parameter for file
 // creation. See adb_open() for more info.
-static __inline__ int  adb_open_mode( const char*  pathname, int  options, int  mode )
-{
-    return TEMP_FAILURE_RETRY( open( pathname, options, mode ) );
+static __inline__ int adb_open_mode(const char* pathname, int options, int mode) {
+    return TEMP_FAILURE_RETRY(open(pathname, options, mode));
 }
 
-
 // Open a file and return a file descriptor that may be used with adb_read(),
 // adb_write(), adb_close(), but not unix_read(), unix_write(), unix_close().
 //
@@ -408,23 +381,21 @@
 // sysdeps_win32.cpp) uses Windows native file I/O and bypasses the C Runtime
 // and its CR/LF translation. The returned file descriptor should be used with
 // adb_read(), adb_write(), adb_close(), etc.
-static __inline__ int  adb_open( const char*  pathname, int  options )
-{
-    int  fd = TEMP_FAILURE_RETRY( open( pathname, options ) );
-    if (fd < 0)
-        return -1;
-    close_on_exec( fd );
+static __inline__ int adb_open(const char* pathname, int options) {
+    int fd = TEMP_FAILURE_RETRY(open(pathname, options));
+    if (fd < 0) return -1;
+    close_on_exec(fd);
     return fd;
 }
-#undef   open
-#define  open    ___xxx_open
+#undef open
+#define open ___xxx_open
 
-static __inline__ int adb_shutdown(int fd, int direction = SHUT_RDWR) {
-    return shutdown(fd, direction);
+static __inline__ int adb_shutdown(borrowed_fd fd, int direction = SHUT_RDWR) {
+    return shutdown(fd.get(), direction);
 }
 
-#undef   shutdown
-#define  shutdown   ____xxx_shutdown
+#undef shutdown
+#define shutdown ____xxx_shutdown
 
 // Closes a file descriptor that came from adb_open() or adb_open_mode(), but
 // not designed to take a file descriptor from unix_open(). See the comments
@@ -432,78 +403,76 @@
 __inline__ int adb_close(int fd) {
     return close(fd);
 }
-#undef   close
-#define  close   ____xxx_close
+#undef close
+#define close ____xxx_close
 
 // On Windows, ADB has an indirection layer for file descriptors. If we get a
 // Win32 SOCKET object from an external library, we have to map it in to that
 // indirection layer, which this does.
-__inline__ int  adb_register_socket(int s) {
+__inline__ int adb_register_socket(int s) {
     return s;
 }
 
-static __inline__  int  adb_read(int  fd, void*  buf, size_t  len)
-{
-    return TEMP_FAILURE_RETRY( read( fd, buf, len ) );
+static __inline__ int adb_read(borrowed_fd fd, void* buf, size_t len) {
+    return TEMP_FAILURE_RETRY(read(fd.get(), buf, len));
 }
 
 // Like unix_read(), but does not handle EINTR.
-static __inline__ int unix_read_interruptible(int fd, void* buf, size_t len) {
-    return read(fd, buf, len);
+static __inline__ int unix_read_interruptible(borrowed_fd fd, void* buf, size_t len) {
+    return read(fd.get(), buf, len);
 }
 
-#undef   read
-#define  read  ___xxx_read
+#undef read
+#define read ___xxx_read
 
-static __inline__  int  adb_write(int  fd, const void*  buf, size_t  len)
-{
-    return TEMP_FAILURE_RETRY( write( fd, buf, len ) );
+static __inline__ int adb_write(borrowed_fd fd, const void* buf, size_t len) {
+    return TEMP_FAILURE_RETRY(write(fd.get(), buf, len));
 }
 #undef   write
 #define  write  ___xxx_write
 
-static __inline__ int   adb_lseek(int  fd, int  pos, int  where)
-{
-    return lseek(fd, pos, where);
+static __inline__ int64_t adb_lseek(borrowed_fd fd, int64_t pos, int where) {
+#if defined(__APPLE__)
+    return lseek(fd.get(), pos, where);
+#else
+    return lseek64(fd.get(), pos, where);
+#endif
 }
-#undef   lseek
-#define  lseek   ___xxx_lseek
+#undef lseek
+#define lseek ___xxx_lseek
 
-static __inline__  int    adb_unlink(const char*  path)
-{
-    return  unlink(path);
+static __inline__ int adb_unlink(const char* path) {
+    return unlink(path);
 }
-#undef  unlink
-#define unlink  ___xxx_unlink
+#undef unlink
+#define unlink ___xxx_unlink
 
-static __inline__  int  adb_creat(const char*  path, int  mode)
-{
-    int  fd = TEMP_FAILURE_RETRY( creat( path, mode ) );
+static __inline__ int adb_creat(const char* path, int mode) {
+    int fd = TEMP_FAILURE_RETRY(creat(path, mode));
 
-    if ( fd < 0 )
-        return -1;
+    if (fd < 0) return -1;
 
     close_on_exec(fd);
     return fd;
 }
-#undef   creat
-#define  creat  ___xxx_creat
+#undef creat
+#define creat ___xxx_creat
 
-static __inline__ int unix_isatty(int fd) {
-    return isatty(fd);
+static __inline__ int unix_isatty(borrowed_fd fd) {
+    return isatty(fd.get());
 }
-#define  isatty  ___xxx_isatty
+#define isatty ___xxx_isatty
 
 // Helper for network_* functions.
 inline int _fd_set_error_str(int fd, std::string* error) {
-  if (fd == -1) {
-    *error = strerror(errno);
-  }
-  return fd;
+    if (fd == -1) {
+        *error = strerror(errno);
+    }
+    return fd;
 }
 
 inline int network_inaddr_any_server(int port, int type, std::string* error) {
-  return _fd_set_error_str(socket_inaddr_any_server(port, type), error);
+    return _fd_set_error_str(socket_inaddr_any_server(port, type), error);
 }
 
 inline int network_local_client(const char* name, int namespace_id, int type, std::string* error) {
@@ -514,38 +483,23 @@
     return _fd_set_error_str(socket_local_server(name, namespace_id, type), error);
 }
 
-inline int network_connect(const std::string& host, int port, int type,
-                           int timeout, std::string* error) {
-  int getaddrinfo_error = 0;
-  int fd = socket_network_client_timeout(host.c_str(), port, type, timeout,
-                                         &getaddrinfo_error);
-  if (fd != -1) {
-    return fd;
-  }
-  if (getaddrinfo_error != 0) {
-    *error = gai_strerror(getaddrinfo_error);
-  } else {
-    *error = strerror(errno);
-  }
-  return -1;
-}
+int network_connect(const std::string& host, int port, int type, int timeout, std::string* error);
 
-static __inline__ int  adb_socket_accept(int  serverfd, struct sockaddr*  addr, socklen_t  *addrlen)
-{
+static __inline__ int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr,
+                                        socklen_t* addrlen) {
     int fd;
 
-    fd = TEMP_FAILURE_RETRY( accept( serverfd, addr, addrlen ) );
-    if (fd >= 0)
-        close_on_exec(fd);
+    fd = TEMP_FAILURE_RETRY(accept(serverfd.get(), addr, addrlen));
+    if (fd >= 0) close_on_exec(fd);
 
     return fd;
 }
 
-#undef   accept
-#define  accept  ___xxx_accept
+#undef accept
+#define accept ___xxx_accept
 
-inline int adb_socket_get_local_port(int fd) {
-    return socket_get_local_port(fd);
+inline int adb_socket_get_local_port(borrowed_fd fd) {
+    return socket_get_local_port(fd.get());
 }
 
 // Operate on a file descriptor returned from unix_open() or a well-known file
@@ -556,9 +510,10 @@
 // Windows implementations (in the ifdef above and in sysdeps_win32.cpp) call
 // into the C Runtime and its configurable CR/LF translation (which is settable
 // via _setmode()).
-#define  unix_read   adb_read
-#define  unix_write  adb_write
-#define  unix_close  adb_close
+#define unix_read adb_read
+#define unix_write adb_write
+#define unix_lseek adb_lseek
+#define unix_close adb_close
 
 static __inline__ int adb_thread_setname(const std::string& name) {
 #ifdef __APPLE__
@@ -573,34 +528,31 @@
 #endif
 }
 
-static __inline__ int  adb_setsockopt( int  fd, int  level, int  optname, const void*  optval, socklen_t  optlen )
-{
-    return setsockopt( fd, level, optname, optval, optlen );
+static __inline__ int adb_setsockopt(borrowed_fd fd, int level, int optname, const void* optval,
+                                     socklen_t optlen) {
+    return setsockopt(fd.get(), level, optname, optval, optlen);
 }
 
-#undef   setsockopt
-#define  setsockopt  ___xxx_setsockopt
+#undef setsockopt
+#define setsockopt ___xxx_setsockopt
 
-static __inline__ int  unix_socketpair( int  d, int  type, int  protocol, int sv[2] )
-{
-    return socketpair( d, type, protocol, sv );
+static __inline__ int unix_socketpair(int d, int type, int protocol, int sv[2]) {
+    return socketpair(d, type, protocol, sv);
 }
 
-static __inline__ int  adb_socketpair( int  sv[2] )
-{
-    int  rc;
+static __inline__ int adb_socketpair(int sv[2]) {
+    int rc;
 
-    rc = unix_socketpair( AF_UNIX, SOCK_STREAM, 0, sv );
-    if (rc < 0)
-        return -1;
+    rc = unix_socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+    if (rc < 0) return -1;
 
-    close_on_exec( sv[0] );
-    close_on_exec( sv[1] );
+    close_on_exec(sv[0]);
+    close_on_exec(sv[1]);
     return 0;
 }
 
-#undef   socketpair
-#define  socketpair   ___xxx_socketpair
+#undef socketpair
+#define socketpair ___xxx_socketpair
 
 typedef struct pollfd adb_pollfd;
 static __inline__ int adb_poll(adb_pollfd* fds, size_t nfds, int timeout) {
@@ -609,34 +561,28 @@
 
 #define poll ___xxx_poll
 
-static __inline__ int  adb_mkdir(const std::string& path, int mode)
-{
+static __inline__ int adb_mkdir(const std::string& path, int mode) {
     return mkdir(path.c_str(), mode);
 }
 
-#undef   mkdir
-#define  mkdir  ___xxx_mkdir
+#undef mkdir
+#define mkdir ___xxx_mkdir
 
 static __inline__ int adb_is_absolute_host_path(const char* path) {
     return path[0] == '/';
 }
 
-static __inline__ unsigned long adb_thread_id()
-{
-    return (unsigned long)gettid();
-}
-
 #endif /* !_WIN32 */
 
-static inline void disable_tcp_nagle(int fd) {
+static inline void disable_tcp_nagle(borrowed_fd fd) {
     int off = 1;
-    adb_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &off, sizeof(off));
+    adb_setsockopt(fd.get(), IPPROTO_TCP, TCP_NODELAY, &off, sizeof(off));
 }
 
 // Sets TCP socket |fd| to send a keepalive TCP message every |interval_sec| seconds. Set
 // |interval_sec| to 0 to disable keepalives. If keepalives are enabled, the connection will be
 // configured to drop after 10 missed keepalives. Returns true on success.
-bool set_tcp_keepalive(int fd, int interval_sec);
+bool set_tcp_keepalive(borrowed_fd fd, int interval_sec);
 
 #if defined(_WIN32)
 // Win32 defines ERROR, which we don't need, but which conflicts with google3 logging.
diff --git a/adb/sysdeps/chrono.h b/adb/sysdeps/chrono.h
index c73a638..5c5af7c 100644
--- a/adb/sysdeps/chrono.h
+++ b/adb/sysdeps/chrono.h
@@ -18,29 +18,4 @@
 
 #include <chrono>
 
-#if defined(_WIN32)
-// We don't have C++14 on Windows yet.
-// Reimplement std::chrono_literals ourselves until we do.
-
-// Silence the following warning (which gets promoted to an error):
-// error: literal operator suffixes not preceded by ‘_’ are reserved for future standardization
-#pragma GCC system_header
-
-constexpr std::chrono::seconds operator"" s(unsigned long long s) {
-    return std::chrono::seconds(s);
-}
-
-constexpr std::chrono::duration<long double> operator"" s(long double s) {
-    return std::chrono::duration<long double>(s);
-}
-
-constexpr std::chrono::milliseconds operator"" ms(unsigned long long ms) {
-    return std::chrono::milliseconds(ms);
-}
-
-constexpr std::chrono::duration<long double, std::milli> operator"" ms(long double ms) {
-    return std::chrono::duration<long double, std::milli>(ms);
-}
-#else
 using namespace std::chrono_literals;
-#endif
diff --git a/adb/sysdeps/errno.cpp b/adb/sysdeps/errno.cpp
index 6869947..9a37ea2 100644
--- a/adb/sysdeps/errno.cpp
+++ b/adb/sysdeps/errno.cpp
@@ -24,10 +24,6 @@
 
 #include "adb.h"
 
-#if defined(_WIN32)
-#define ETXTBSY EBUSY
-#endif
-
 // Use the linux asm-generic values for errno (which are used on all android archs but mips).
 #define ERRNO_VALUES()             \
     ERRNO_VALUE(EACCES, 13);       \
diff --git a/adb/sysdeps/memory.h b/adb/sysdeps/memory.h
deleted file mode 100644
index 0e4c509..0000000
--- a/adb/sysdeps/memory.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#pragma once
-
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <memory>
-#include <type_traits>
-
-#if defined(_WIN32)
-// We don't have C++14 on Windows yet.
-// Reimplement std::make_unique ourselves until we do.
-
-namespace std {
-
-template <typename T, typename... Args>
-typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type make_unique(
-    Args&&... args) {
-    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-}
-
-}  // namespace std
-
-#endif
diff --git a/adb/sysdeps/posix/network.cpp b/adb/sysdeps/posix/network.cpp
index ecd1fd2..c5c2275 100644
--- a/adb/sysdeps/posix/network.cpp
+++ b/adb/sysdeps/posix/network.cpp
@@ -17,11 +17,16 @@
 #include "sysdeps/network.h"
 
 #include <errno.h>
+#include <netdb.h>
 #include <netinet/in.h>
 #include <sys/socket.h>
 
 #include <string>
 
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/sockets.h>
+
 #include "adb_unique_fd.h"
 
 static void set_error(std::string* error) {
@@ -99,13 +104,13 @@
     socklen_t addrlen = sizeof(addr_storage);
     sockaddr* addr = (ipv6 ? loopback_addr6 : loopback_addr4)(&addr_storage, &addrlen, port);
 
-    if (bind(s, addr, addrlen) != 0) {
+    if (bind(s.get(), addr, addrlen) != 0) {
         set_error(error);
         return -1;
     }
 
     if (type == SOCK_STREAM || type == SOCK_SEQPACKET) {
-        if (listen(s, SOMAXCONN) != 0) {
+        if (listen(s.get(), SOMAXCONN) != 0) {
             set_error(error);
             return -1;
         }
@@ -124,3 +129,21 @@
     }
     return rc;
 }
+
+int network_connect(const std::string& host, int port, int type, int timeout, std::string* error) {
+    int getaddrinfo_error = 0;
+    int fd = socket_network_client_timeout(host.c_str(), port, type, timeout, &getaddrinfo_error);
+    if (fd != -1) {
+        return fd;
+    }
+    if (getaddrinfo_error != 0) {
+        *error = android::base::StringPrintf("failed to resolve host: '%s': %s", host.c_str(),
+                                             gai_strerror(getaddrinfo_error));
+        LOG(WARNING) << *error;
+    } else {
+        *error = android::base::StringPrintf("failed to connect to '%s:%d': %s", host.c_str(), port,
+                                             strerror(errno));
+        LOG(WARNING) << *error;
+    }
+    return -1;
+}
diff --git a/adb/sysdeps/stat_test.cpp b/adb/sysdeps/stat_test.cpp
index 2c2e0ee..67155d9 100644
--- a/adb/sysdeps/stat_test.cpp
+++ b/adb/sysdeps/stat_test.cpp
@@ -16,7 +16,7 @@
 
 #include <string>
 
-#include <android-base/test_utils.h>
+#include <android-base/file.h>
 #include <gtest/gtest.h>
 
 #include "adb_utils.h"
diff --git a/adb/sysdeps/uio.h b/adb/sysdeps/uio.h
new file mode 100644
index 0000000..ced884b
--- /dev/null
+++ b/adb/sysdeps/uio.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.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include "adb_unique_fd.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(borrowed_fd fd, const adb_iovec* iov, int iovcnt);
+
+#else
+
+#include <sys/uio.h>
+using adb_iovec = struct iovec;
+inline ssize_t adb_writev(borrowed_fd fd, const adb_iovec* iov, int iovcnt) {
+    return writev(fd.get(), iov, iovcnt);
+}
+
+#endif
+
+#pragma GCC poison writev
diff --git a/adb/sysdeps/vm_sockets.h b/adb/sysdeps/vm_sockets.h
new file mode 100644
index 0000000..75c5f44
--- /dev/null
+++ b/adb/sysdeps/vm_sockets.h
@@ -0,0 +1,49 @@
+#if __BIONIC__
+#include <linux/vm_sockets.h>
+#else
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   Copied and modified from bionic/libc/kernel/uapi/linux/vm_sockets.h
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _UAPI_VM_SOCKETS_H
+#define _UAPI_VM_SOCKETS_H
+#include <linux/socket.h>
+#define SO_VM_SOCKETS_BUFFER_SIZE 0
+#define SO_VM_SOCKETS_BUFFER_MIN_SIZE 1
+#define SO_VM_SOCKETS_BUFFER_MAX_SIZE 2
+#define SO_VM_SOCKETS_PEER_HOST_VM_ID 3
+#define SO_VM_SOCKETS_TRUSTED 5
+#define SO_VM_SOCKETS_CONNECT_TIMEOUT 6
+#define SO_VM_SOCKETS_NONBLOCK_TXRX 7
+#define VMADDR_CID_ANY -1U
+#define VMADDR_PORT_ANY -1U
+#define VMADDR_CID_HYPERVISOR 0
+#define VMADDR_CID_RESERVED 1
+#define VMADDR_CID_HOST 2
+#define VM_SOCKETS_INVALID_VERSION -1U
+#define VM_SOCKETS_VERSION_EPOCH(_v) (((_v)&0xFF000000) >> 24)
+#define VM_SOCKETS_VERSION_MAJOR(_v) (((_v)&0x00FF0000) >> 16)
+#define VM_SOCKETS_VERSION_MINOR(_v) (((_v)&0x0000FFFF))
+struct sockaddr_vm {
+    __kernel_sa_family_t svm_family;
+    unsigned short svm_reserved1;
+    unsigned int svm_port;
+    unsigned int svm_cid;
+    unsigned char svm_zero[sizeof(struct sockaddr) - sizeof(sa_family_t) - sizeof(unsigned short) -
+                           sizeof(unsigned int) - sizeof(unsigned int)];
+};
+#define IOCTL_VM_SOCKETS_GET_LOCAL_CID _IO(7, 0xb9)
+#ifndef AF_VSOCK
+#define AF_VSOCK 40
+#endif
+#endif
+#endif
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_unix.cpp b/adb/sysdeps_unix.cpp
index 4445a44..3fdc917 100644
--- a/adb/sysdeps_unix.cpp
+++ b/adb/sysdeps_unix.cpp
@@ -16,7 +16,7 @@
 
 #include "sysdeps.h"
 
-bool set_tcp_keepalive(int fd, int interval_sec) {
+bool set_tcp_keepalive(borrowed_fd fd, int interval_sec) {
     int enable = (interval_sec > 0);
     if (adb_setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable))) {
         return false;
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 62f4ac8..f86cd03 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -29,13 +29,16 @@
 #include <memory>
 #include <mutex>
 #include <string>
+#include <string_view>
 #include <unordered_map>
 #include <vector>
 
 #include <cutils/sockets.h>
 
 #include <android-base/errors.h>
+#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/utf8.h>
@@ -43,27 +46,28 @@
 #include "adb.h"
 #include "adb_utils.h"
 
-extern void fatal(const char *fmt, ...);
+#include "sysdeps/uio.h"
 
 /* forward declarations */
 
 typedef const struct FHClassRec_* FHClass;
 typedef struct FHRec_* FH;
-typedef struct EventHookRec_* EventHook;
 
 typedef struct FHClassRec_ {
     void (*_fh_init)(FH);
     int (*_fh_close)(FH);
-    int (*_fh_lseek)(FH, int, int);
+    int64_t (*_fh_lseek)(FH, int64_t, 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);
 static int _fh_file_close(FH);
-static int _fh_file_lseek(FH, int, int);
+static int64_t _fh_file_lseek(FH, int64_t, 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,13 +75,15 @@
     _fh_file_lseek,
     _fh_file_read,
     _fh_file_write,
+    _fh_file_writev,
 };
 
 static void _fh_socket_init(FH);
 static int _fh_socket_close(FH);
-static int _fh_socket_lseek(FH, int, int);
+static int64_t _fh_socket_lseek(FH, int64_t, 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,12 +91,12 @@
     _fh_socket_lseek,
     _fh_socket_read,
     _fh_socket_write,
+    _fh_socket_writev,
 };
 
-#define assert(cond)                                                                       \
-    do {                                                                                   \
-        if (!(cond)) fatal("assertion failed '%s' on %s:%d\n", #cond, __FILE__, __LINE__); \
-    } while (0)
+#if defined(assert)
+#undef assert
+#endif
 
 void handle_deleter::operator()(HANDLE h) {
     // CreateFile() is documented to return INVALID_HANDLE_FILE on error,
@@ -139,51 +145,43 @@
 static  FHRec        _win32_fhs[ WIN32_MAX_FHS ];
 static  int          _win32_fh_next;  // where to start search for free FHRec
 
-static FH
-_fh_from_int( int   fd, const char*   func )
-{
-    FH  f;
+static FH _fh_from_int(borrowed_fd bfd, const char* func) {
+    FH f;
 
+    int fd = bfd.get();
     fd -= WIN32_FH_BASE;
 
     if (fd < 0 || fd >= WIN32_MAX_FHS) {
-        D( "_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE,
-           func );
+        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];
 
     if (f->used == 0) {
-        D( "_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE,
-           func );
+        D("_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE, func);
         errno = EBADF;
-        return NULL;
+        return nullptr;
     }
 
     return f;
 }
 
-
-static int
-_fh_to_int( FH  f )
-{
+static int _fh_to_int(FH f) {
     if (f && f->used && f >= _win32_fhs && f < _win32_fhs + WIN32_MAX_FHS)
         return (int)(f - _win32_fhs) + WIN32_FH_BASE;
 
     return -1;
 }
 
-static FH
-_fh_alloc( FHClass  clazz )
-{
-    FH   f = NULL;
+static FH _fh_alloc(FHClass clazz) {
+    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;
@@ -200,10 +198,7 @@
     return nullptr;
 }
 
-
-static int
-_fh_close( FH   f )
-{
+static int _fh_close(FH f) {
     // Use lock so that closing only happens once and so that _fh_alloc can't
     // allocate a FH that we're in the middle of closing.
     std::lock_guard<std::mutex> lock(_win32_lock);
@@ -218,7 +213,7 @@
         f->name[0] = '\0';
         f->eof     = 0;
         f->used    = 0;
-        f->clazz   = NULL;
+        f->clazz   = nullptr;
     }
     return 0;
 }
@@ -248,67 +243,94 @@
 /**************************************************************************/
 /**************************************************************************/
 
-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 int64_t _fh_file_lseek(FH f, int64_t pos, int origin) {
+    DWORD method;
+    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 );
-    if (result == INVALID_SET_FILE_POINTER) {
+    LARGE_INTEGER li = {.QuadPart = pos};
+    if (!SetFilePointerEx(f->fh_handle, li, &li, method)) {
         errno = EIO;
         return -1;
-    } else {
-        f->eof = 0;
     }
-    return (int)result;
+    f->eof = 0;
+    return li.QuadPart;
 }
 
-
 /**************************************************************************/
 /**************************************************************************/
 /*****                                                                *****/
@@ -317,12 +339,14 @@
 /**************************************************************************/
 /**************************************************************************/
 
-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;
+
+    // CreateFileW is inherently O_CLOEXEC by default.
+    options &= ~O_CLOEXEC;
 
     switch (options) {
         case O_RDONLY:
@@ -340,8 +364,8 @@
             return -1;
     }
 
-    f = _fh_alloc( &_fh_file_class );
-    if ( !f ) {
+    f = _fh_alloc(&_fh_file_class);
+    if (!f) {
         return -1;
     }
 
@@ -349,21 +373,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 +398,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 +416,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 +440,62 @@
                 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(borrowed_fd 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(borrowed_fd 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(borrowed_fd 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) {
+    if (f == nullptr) {
+        errno = EBADF;
         return -1;
     }
 
+    return f->clazz->_fh_writev(f, iov, iovcnt);
+}
+
+int64_t adb_lseek(borrowed_fd fd, int64_t 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 +555,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 +576,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 +592,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 +608,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 +629,13 @@
     return 0;
 }
 
-static int _fh_socket_lseek( FH  f, int pos, int origin ) {
+static int64_t _fh_socket_lseek(FH f, int64_t 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 +647,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 +665,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 +711,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 void _init_winsock() {
+    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());
+            LOG(FATAL) << "could not initialize Winsock: "
+                       << android::base::SystemErrorCodeToString(rc);
         }
-        _winsock_init = 1;
 
         // Note that we do not call atexit() to register WSACleanup to be called
         // at normal process termination because:
@@ -679,7 +734,7 @@
         //    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.
-    }
+    });
 }
 
 // Map a socket type to an explicit socket protocol instead of using the socket
@@ -709,8 +764,6 @@
         return -1;
     }
 
-    if (!_winsock_init) _init_winsock();
-
     memset(&addr, 0, sizeof(addr));
     addr.sin_family = AF_INET;
     addr.sin_port = htons(port);
@@ -759,8 +812,6 @@
         return -1;
     }
 
-    if (!_winsock_init) _init_winsock();
-
     memset(&addr, 0, sizeof(addr));
     addr.sin_family = AF_INET;
     addr.sin_port = htons(port);
@@ -837,8 +888,6 @@
         return -1;
     }
 
-    if (!_winsock_init) _init_winsock();
-
     struct addrinfo hints;
     memset(&hints, 0, sizeof(hints));
     hints.ai_family = AF_UNSPEC;
@@ -906,53 +955,50 @@
     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(borrowed_fd serverfd, struct sockaddr* addr, socklen_t* addrlen) {
+    FH serverfh = _fh_from_int(serverfd, __func__);
 
-    if ( !serverfh || serverfh->clazz != &_fh_socket_class ) {
-        D("adb_socket_accept: invalid fd %d", serverfd);
+    if (!serverfh || serverfh->clazz != &_fh_socket_class) {
+        D("adb_socket_accept: invalid fd %d", serverfd.get());
         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.get()
+                   << " 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.get(), fd);
     fh.release();
-    return  fd;
+    return fd;
 }
 
+int adb_setsockopt(borrowed_fd 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 ) {
-        D("adb_setsockopt: invalid fd %d", fd);
+    if (!fh || fh->clazz != &_fh_socket_class) {
+        D("adb_setsockopt: invalid fd %d", fd.get());
         errno = EBADF;
         return -1;
     }
@@ -961,23 +1007,23 @@
     // 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.get(), level,
+          optname, android::base::SystemErrorCodeToString(err).c_str());
+        _socket_set_errno(err);
         result = -1;
     }
     return result;
 }
 
-int adb_getsockname(int fd, struct sockaddr* sockaddr, socklen_t* optlen) {
+static int adb_getsockname(borrowed_fd fd, struct sockaddr* sockaddr, socklen_t* optlen) {
     FH fh = _fh_from_int(fd, __func__);
 
     if (!fh || fh->clazz != &_fh_socket_class) {
-        D("adb_getsockname: invalid fd %d", fd);
+        D("adb_getsockname: invalid fd %d", fd.get());
         errno = EBADF;
         return -1;
     }
@@ -985,7 +1031,7 @@
     int result = getsockname(fh->fh_socket, sockaddr, optlen);
     if (result == SOCKET_ERROR) {
         const DWORD err = WSAGetLastError();
-        D("adb_getsockname: setsockopt on fd %d failed: %s\n", fd,
+        D("adb_getsockname: setsockopt on fd %d failed: %s\n", fd.get(),
           android::base::SystemErrorCodeToString(err).c_str());
         _socket_set_errno(err);
         result = -1;
@@ -993,7 +1039,7 @@
     return result;
 }
 
-int adb_socket_get_local_port(int fd) {
+int adb_socket_get_local_port(borrowed_fd fd) {
     sockaddr_storage addr_storage;
     socklen_t addr_len = sizeof(addr_storage);
 
@@ -1011,11 +1057,11 @@
     return ntohs(reinterpret_cast<sockaddr_in*>(&addr_storage)->sin_port);
 }
 
-int adb_shutdown(int fd, int direction) {
+int adb_shutdown(borrowed_fd fd, int direction) {
     FH f = _fh_from_int(fd, __func__);
 
     if (!f || f->clazz != &_fh_socket_class) {
-        D("adb_shutdown: invalid fd %d", fd);
+        D("adb_shutdown: invalid fd %d", fd.get());
         errno = EBADF;
         return -1;
     }
@@ -1023,7 +1069,7 @@
     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,
+        D("socket shutdown fd %d failed: %s", fd.get(),
           android::base::SystemErrorCodeToString(err).c_str());
         _socket_set_errno(err);
         return -1;
@@ -1081,12 +1127,12 @@
     return -1;
 }
 
-bool set_file_block_mode(int fd, bool block) {
+bool set_file_block_mode(borrowed_fd fd, bool block) {
     FH fh = _fh_from_int(fd, __func__);
 
     if (!fh || !fh->used) {
         errno = EBADF;
-        D("Setting nonblocking on bad file descriptor %d", fd);
+        D("Setting nonblocking on bad file descriptor %d", fd.get());
         return false;
     }
 
@@ -1095,22 +1141,22 @@
         if (ioctlsocket(fh->u.socket, FIONBIO, &x) != 0) {
             int error = WSAGetLastError();
             _socket_set_errno(error);
-            D("Setting %d nonblocking failed (%d)", fd, error);
+            D("Setting %d nonblocking failed (%d)", fd.get(), error);
             return false;
         }
         return true;
     } else {
         errno = ENOTSOCK;
-        D("Setting nonblocking on non-socket %d", fd);
+        D("Setting nonblocking on non-socket %d", fd.get());
         return false;
     }
 }
 
-bool set_tcp_keepalive(int fd, int interval_sec) {
+bool set_tcp_keepalive(borrowed_fd fd, int interval_sec) {
     FH fh = _fh_from_int(fd, __func__);
 
     if (!fh || fh->clazz != &_fh_socket_class) {
-        D("set_tcp_keepalive(%d) failed: invalid fd", fd);
+        D("set_tcp_keepalive(%d) failed: invalid fd", fd.get());
         errno = EBADF;
         return false;
     }
@@ -1124,7 +1170,7 @@
     if (WSAIoctl(fh->fh_socket, SIO_KEEPALIVE_VALS, &keepalive, sizeof(keepalive), nullptr, 0,
                  &bytes_returned, nullptr, nullptr) != 0) {
         const DWORD err = WSAGetLastError();
-        D("set_tcp_keepalive(%d) failed: %s", fd,
+        D("set_tcp_keepalive(%d) failed: %s", fd.get(),
           android::base::SystemErrorCodeToString(err).c_str());
         _socket_set_errno(err);
         return false;
@@ -1171,12 +1217,12 @@
 // Returns a console HANDLE if |fd| is a console, otherwise returns nullptr.
 // If a valid HANDLE is returned and |mode| is not null, |mode| is also filled
 // with the console mode. Requires GENERIC_READ access to the underlying HANDLE.
-static HANDLE _get_console_handle(int fd, DWORD* mode=nullptr) {
+static HANDLE _get_console_handle(borrowed_fd fd, DWORD* mode = nullptr) {
     // First check isatty(); this is very fast and eliminates most non-console
     // FDs, but returns 1 for both consoles and character devices like NUL.
 #pragma push_macro("isatty")
 #undef isatty
-    if (!isatty(fd)) {
+    if (!isatty(fd.get())) {
         return nullptr;
     }
 #pragma pop_macro("isatty")
@@ -1184,7 +1230,7 @@
     // To differentiate between character devices and consoles we need to get
     // the underlying HANDLE and use GetConsoleMode(), which is what requires
     // GENERIC_READ permissions.
-    const intptr_t intptr_handle = _get_osfhandle(fd);
+    const intptr_t intptr_handle = _get_osfhandle(fd.get());
     if (intptr_handle == -1) {
         return nullptr;
     }
@@ -1208,7 +1254,7 @@
     return _get_console_handle(fd);
 }
 
-int unix_isatty(int fd) {
+int unix_isatty(borrowed_fd fd) {
     return _get_console_handle(fd) ? 1 : 0;
 }
 
@@ -1225,11 +1271,11 @@
         }
 
         if (read_count == 0) {   // should be impossible
-            fatal("ReadConsoleInputA returned 0");
+            LOG(FATAL) << "ReadConsoleInputA returned 0";
         }
 
         if (read_count != 1) {   // should be impossible
-            fatal("ReadConsoleInputA did not return one input record");
+            LOG(FATAL) << "ReadConsoleInputA did not return one input record";
         }
 
         // If the console window is resized, emulate SIGWINCH by breaking out
@@ -1247,8 +1293,7 @@
         if ((input_record->EventType == KEY_EVENT) &&
             (input_record->Event.KeyEvent.bKeyDown)) {
             if (input_record->Event.KeyEvent.wRepeatCount == 0) {
-                fatal("ReadConsoleInputA returned a key event with zero repeat"
-                      " count");
+                LOG(FATAL) << "ReadConsoleInputA returned a key event with zero repeat count";
             }
 
             // Got an interesting INPUT_RECORD, so return
@@ -1589,7 +1634,7 @@
 
 // Prefix the len bytes in buf with the escape character, and then return the
 // new buffer length.
-size_t _escape_prefix(char* const buf, const size_t len) {
+static size_t _escape_prefix(char* const buf, const size_t len) {
     // If nothing to prefix, don't do anything. We might be called with
     // len == 0, if alt was held down with a dead key which produced nothing.
     if (len == 0) {
@@ -1636,7 +1681,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];
@@ -1934,7 +1979,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) {
@@ -2004,9 +2049,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.
@@ -2017,8 +2062,8 @@
 }
 
 // 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)) {
+int unix_read_interruptible(borrowed_fd fd, void* buf, size_t len) {
+    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
@@ -2037,7 +2082,7 @@
         // plain read() in favor of unix_read() or adb_read().
 #pragma push_macro("read")
 #undef read
-        return read(fd, buf, len);
+        return read(fd.get(), buf, len);
 #pragma pop_macro("read")
     }
 }
@@ -2131,7 +2176,7 @@
     for (int i = 0; i < argc; ++i) {
         std::string arg_narrow;
         if (!android::base::WideToUTF8(argv[i], &arg_narrow)) {
-            fatal_errno("cannot convert argument from UTF-16 to UTF-8");
+            PLOG(FATAL) << "cannot convert argument from UTF-16 to UTF-8";
         }
         narrow_args[i] = strdup(arg_narrow.c_str());
     }
@@ -2148,15 +2193,15 @@
     }
 }
 
-int unix_open(const char* path, int options, ...) {
+int unix_open(std::string_view path, int options, ...) {
     std::wstring path_wide;
-    if (!android::base::UTF8ToWide(path, &path_wide)) {
+    if (!android::base::UTF8ToWide(path.data(), path.size(), &path_wide)) {
         return -1;
     }
     if ((options & O_CREAT) == 0) {
         return _wopen(path_wide.c_str(), options);
     } else {
-        int      mode;
+        int mode;
         va_list  args;
         va_start(args, options);
         mode = va_arg(args, int);
@@ -2378,7 +2423,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;
     }
@@ -2391,9 +2436,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.
@@ -2423,7 +2467,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
@@ -2514,7 +2558,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
@@ -2562,18 +2606,19 @@
 extern "C" int wmain(int argc, wchar_t **argv) {
     // Convert args from UTF-16 to UTF-8 and pass that to main().
     NarrowArgs narrow_args(argc, argv);
-    return main(argc, narrow_args.data());
+
+    // Avoid destructing NarrowArgs: argv might have been mutated to point to string literals.
+    _exit(main(argc, narrow_args.data()));
 }
 
 // Shadow UTF-8 environment variable name/value pairs that are created from
-// _wenviron the first time that adb_getenv() is called. Note that this is not
-// currently updated if putenv, setenv, unsetenv are called. Note that no
-// thread synchronization is done, but we're called early enough in
+// _wenviron by _init_env(). Note that this is not currently updated if putenv, setenv, unsetenv are
+// called. Note that no thread synchronization is done, but we're called early enough in
 // single-threaded startup that things work ok.
 static auto& g_environ_utf8 = *new std::unordered_map<std::string, char*>();
 
-// Make sure that shadow UTF-8 environment variables are setup.
-static void _ensure_env_setup() {
+// Setup shadow UTF-8 environment variables.
+static void _init_env() {
     // If some name/value pairs exist, then we've already done the setup below.
     if (g_environ_utf8.size() != 0) {
         return;
@@ -2583,7 +2628,7 @@
         // If _wenviron is null, then -municode probably wasn't used. That
         // linker flag will cause the entry point to setup _wenviron. It will
         // also require an implementation of wmain() (which we provide above).
-        fatal("_wenviron is not set, did you link with -municode?");
+        LOG(FATAL) << "_wenviron is not set, did you link with -municode?";
     }
 
     // Read name/value pairs from UTF-16 _wenviron and write new name/value
@@ -2626,8 +2671,6 @@
 // Version of getenv() that takes a UTF-8 environment variable name and
 // retrieves a UTF-8 value. Case-insensitive to match getenv() on Windows.
 char* adb_getenv(const char* name) {
-    _ensure_env_setup();
-
     // Case-insensitive search by searching for lowercase name in a map of
     // lowercase names.
     const auto it = g_environ_utf8.find(ToLower(std::string(name)));
@@ -2679,3 +2722,88 @@
 
     return buf;
 }
+
+// The SetThreadDescription API was brought in version 1607 of Windows 10.
+typedef HRESULT(WINAPI* SetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription);
+
+// Based on PlatformThread::SetName() from
+// https://cs.chromium.org/chromium/src/base/threading/platform_thread_win.cc
+int adb_thread_setname(const std::string& name) {
+    // The SetThreadDescription API works even if no debugger is attached.
+    auto set_thread_description_func = reinterpret_cast<SetThreadDescription>(
+            ::GetProcAddress(::GetModuleHandleW(L"Kernel32.dll"), "SetThreadDescription"));
+    if (set_thread_description_func) {
+        std::wstring name_wide;
+        if (!android::base::UTF8ToWide(name.c_str(), &name_wide)) {
+            return errno;
+        }
+        set_thread_description_func(::GetCurrentThread(), name_wide.c_str());
+    }
+
+    // Don't use the thread naming SEH exception because we're compiled with -fno-exceptions.
+    // https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code?view=vs-2017
+
+    return 0;
+}
+
+#if !defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
+#endif
+
+#if !defined(DISABLE_NEWLINE_AUTO_RETURN)
+#define DISABLE_NEWLINE_AUTO_RETURN 0x0008
+#endif
+
+static void _init_console() {
+    DWORD old_out_console_mode;
+
+    const HANDLE out = _get_console_handle(STDOUT_FILENO, &old_out_console_mode);
+    if (out == nullptr) {
+        return;
+    }
+
+    // Try to use ENABLE_VIRTUAL_TERMINAL_PROCESSING on the output console to process virtual
+    // terminal sequences on newer versions of Windows 10 and later.
+    // https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
+    // On older OSes that don't support the flag, SetConsoleMode() will return an error.
+    // ENABLE_VIRTUAL_TERMINAL_PROCESSING also solves a problem where the last column of the
+    // console cannot be overwritten.
+    //
+    // Note that we don't use DISABLE_NEWLINE_AUTO_RETURN because it doesn't seem to be necessary.
+    // If we use DISABLE_NEWLINE_AUTO_RETURN, _console_write_utf8() would need to be modified to
+    // translate \n to \r\n.
+    if (!SetConsoleMode(out, old_out_console_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) {
+        return;
+    }
+
+    // If SetConsoleMode() succeeded, the console supports virtual terminal processing, so we
+    // should set the TERM env var to match so that it will be propagated to adbd on devices.
+    //
+    // Below's direct manipulation of env vars and not g_environ_utf8 assumes that _init_env() has
+    // not yet been called. If this fails, _init_env() should be called after _init_console().
+    if (g_environ_utf8.size() > 0) {
+        LOG(FATAL) << "environment variables have already been converted to UTF-8";
+    }
+
+#pragma push_macro("getenv")
+#undef getenv
+#pragma push_macro("putenv")
+#undef putenv
+    if (getenv("TERM") == nullptr) {
+        // This is the same TERM value used by Gnome Terminal and the version of ssh included with
+        // Windows.
+        putenv("TERM=xterm-256color");
+    }
+#pragma pop_macro("putenv")
+#pragma pop_macro("getenv")
+}
+
+static bool _init_sysdeps() {
+    // _init_console() depends on _init_env() not being called yet.
+    _init_console();
+    _init_env();
+    _init_winsock();
+    return true;
+}
+
+static bool _sysdeps_init = _init_sysdeps();
diff --git a/adb/sysdeps_win32_test.cpp b/adb/sysdeps_win32_test.cpp
index 529b212..183cd5b 100644
--- a/adb/sysdeps_win32_test.cpp
+++ b/adb/sysdeps_win32_test.cpp
@@ -18,7 +18,7 @@
 
 #include "sysdeps.h"
 
-#include <android-base/test_utils.h>
+#include <android-base/file.h>
 
 TEST(sysdeps_win32, adb_getenv) {
     // Insert all test env vars before first call to adb_getenv() which will
diff --git a/adb/test_adb.py b/adb/test_adb.py
old mode 100644
new mode 100755
index e771106..8272722
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (C) 2015 The Android Open Source Project
 #
@@ -19,65 +19,197 @@
 This differs from things in test_device.py in that there is no API for these
 things. Most of these tests involve specific error messages or the help text.
 """
-from __future__ import print_function
 
 import contextlib
 import os
 import random
+import select
 import socket
 import struct
 import subprocess
+import sys
 import threading
+import time
 import unittest
-
-import adb
+import warnings
 
 
-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.
+    readsock, writesock = socket.socketpair()
+
+    def _adb_packet(command: bytes, arg0: int, arg1: int, data: bytes) -> bytes:
+        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(sock):
+        with contextlib.closing(sock) as serversock:
+            rlist = [readsock, serversock]
+            cnxn_sent = {}
+            while True:
+                read_ready, _, _ = select.select(rlist, [], [])
+                for ready in read_ready:
+                    if ready == readsock:
+                        # Closure pipe
+                        for f in rlist:
+                            f.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(b"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(b"CNXN", 0x01000001, 1024 * 1024,
+                                                  b"device::ro.product.name=fakeadb"))
+
+    port = serversock.getsockname()[1]
+    server_thread = threading.Thread(target=_handle, args=(serversock,))
+    server_thread.start()
+
+    try:
+        yield port, writesock
+    finally:
+        writesock.close()
+        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).encode("utf8"))
+
+    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()
+
+    if sys.platform == "win32":
+        import msvcrt
+        write_handle = msvcrt.get_osfhandle(write_pipe)
+        os.set_handle_inheritable(write_handle, True)
+        reply_fd = str(write_handle)
+    else:
+        os.set_inheritable(write_pipe, True)
+        reply_fd = str(write_pipe)
+
+    proc = subprocess.Popen(["adb", "-L", "tcp:localhost:{}".format(port),
+                             "fork-server", "server",
+                             "--reply-fd", reply_fd], close_fds=False)
+    try:
+        os.close(write_pipe)
+        greeting = os.read(read_pipe, 1024)
+        assert greeting == b"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."""
         out = subprocess.check_output(
-            ['adb', 'help'], stderr=subprocess.STDOUT)
+            ["adb", "help"], stderr=subprocess.STDOUT)
         self.assertGreater(len(out), 0)
 
     def test_version(self):
         """Get a version number out of the output of adb."""
-        lines = subprocess.check_output(['adb', 'version']).splitlines()
+        lines = subprocess.check_output(["adb", "version"]).splitlines()
         version_line = lines[0]
-        self.assertRegexpMatches(
-            version_line, r'^Android Debug Bridge version \d+\.\d+\.\d+$')
+        self.assertRegex(
+            version_line, rb"^Android Debug Bridge version \d+\.\d+\.\d+$")
         if len(lines) == 2:
             # Newer versions of ADB have a second line of output for the
             # version that includes a specific revision (git SHA).
             revision_line = lines[1]
-            self.assertRegexpMatches(
-                revision_line, r'^Revision [0-9a-f]{12}-android$')
+            self.assertRegex(
+                revision_line, rb"^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('requires an argument', 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(b"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('invalid port', out)
+        proc = subprocess.Popen(["adb", "tcpip", "foo"],
+                                stdout=subprocess.PIPE,
+                                stderr=subprocess.STDOUT)
+        out, _ = proc.communicate()
+        self.assertEqual(1, proc.returncode)
+        self.assertIn(b"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;
@@ -94,34 +226,37 @@
 
         port = 5038
         # Kill any existing server on this non-default port.
-        subprocess.check_output(['adb', '-P', str(port), 'kill-server'],
+        subprocess.check_output(["adb", "-P", str(port), "kill-server"],
                                 stderr=subprocess.STDOUT)
 
         try:
+            # We get warnings for unclosed files for the subprocess's pipes,
+            # and it's somewhat cumbersome to close them, so just ignore this.
+            warnings.simplefilter("ignore", ResourceWarning)
+
             # 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))
-            stdout_thread.daemon = True
+                target=ServerTest._read_pipe_and_set_event,
+                args=(proc.stdout, stdout_event))
             stdout_thread.start()
 
             stderr_event = threading.Event()
             stderr_thread = threading.Thread(
-                    target=self._read_pipe_and_set_event,
-                    args=(p.stderr, stderr_event))
-            stderr_thread.daemon = True
+                target=ServerTest._read_pipe_and_set_event,
+                args=(proc.stderr, stderr_event))
             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 +264,8 @@
             # probably letting the adb server inherit stdin which would be
             # wrong.
             with self.assertRaises(IOError):
-                p.stdin.write('x')
+                proc.stdin.write(b"x")
+                proc.stdin.flush()
 
             # 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
@@ -138,15 +274,21 @@
             # inherit stdout/stderr which would be wrong.
             self.assertTrue(stdout_event.wait(5), "adb stdout not closed")
             self.assertTrue(stderr_event.wait(5), "adb stderr not closed")
+            stdout_thread.join()
+            stderr_thread.join()
         finally:
             # If we started a server, kill it.
-            subprocess.check_output(['adb', '-P', str(port), 'kill-server'],
+            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'
+        linger_format = "hh" if os.name == "nt" else "ii"
         l_onoff = 1
         l_linger = 0
 
@@ -162,35 +304,38 @@
 
         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(
-                ['adb', '-s', 'emulator-' + str(port), 'emu', 'kill'],
+            proc = subprocess.Popen(
+                ["adb", "-s", "emulator-" + str(port), "emu", "kill"],
                 stderr=subprocess.STDOUT)
 
             accepted_connection, addr = listener.accept()
             with contextlib.closing(accepted_connection) as conn:
                 # 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')
-                conn.sendall('OK\r\n')
+                conn.sendall(("Android Console: type 'help' for a list "
+                             "of commands\r\n").encode("utf8"))
+                conn.sendall(b"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')
+                conn.sendall(b"OK: killing emulator, bye bye\r\n")
 
                 # Use SO_LINGER to send TCP RST segment to test whether adb
                 # ignores WSAECONNRESET on Windows. This happens with the
@@ -201,47 +346,216 @@
                 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).encode("utf8"))
+
+                # 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).encode("utf8")
+                    sock.sendall(b"%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(), b"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)
+    def test_already_connected(self):
+        """Ensure that an already-connected device stays connected."""
+
+        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).encode("utf8"))
+
+    @unittest.skip("Currently failing b/123247844")
+    def test_reconnect(self):
+        """Ensure that a disconnected device reconnects."""
+
+        with fake_adbd() as (port, _):
+            serial = "localhost:{}".format(port)
+            with adb_connect(self, serial):
+                # Wait a bit to give adb some time to connect.
+                time.sleep(0.25)
+
+                output = subprocess.check_output(["adb", "-s", serial,
+                                                  "get-state"])
+                self.assertEqual(output.strip(), b"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(), b"error: closed")
+
+                subprocess.check_call(["adb", "-s", serial, "wait-for-device"])
+
+                output = subprocess.check_output(["adb", "-s", serial,
+                                                  "get-state"])
+                self.assertEqual(output.strip(), b"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).encode("utf8"))
+                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).encode("utf8"))
+
+
+class DisconnectionTest(unittest.TestCase):
+    """Tests for adb disconnect."""
+
+    def test_disconnect(self):
+        """Ensure that `adb disconnect` takes effect immediately."""
+
+        def _devices(port):
+            output = subprocess.check_output(["adb", "-P", str(port), "devices"])
+            return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
+
+        with adb_server() as server_port:
+            with fake_adbd() as (port, sock):
+                device_name = "localhost:{}".format(port)
+                output = subprocess.check_output(["adb", "-P", str(server_port),
+                                                  "connect", device_name])
+                self.assertEqual(output.strip(),
+                                  "connected to {}".format(device_name).encode("utf8"))
+
+
+                self.assertEqual(_devices(server_port), [[device_name, "device"]])
+
+                # Send a deliberately malformed packet to make the device go offline.
+                packet = struct.pack("IIIIII", 0, 0, 0, 0, 0, 0)
+                sock.sendall(packet)
+
+                # Wait a bit.
+                time.sleep(0.1)
+
+                self.assertEqual(_devices(server_port), [[device_name, "offline"]])
+
+                # Disconnect the device.
+                output = subprocess.check_output(["adb", "-P", str(server_port),
+                                                  "disconnect", device_name])
+
+                # Wait a bit.
+                time.sleep(0.1)
+
+                self.assertEqual(_devices(server_port), [])
+
+
+@unittest.skipUnless(sys.platform == "win32", "requires Windows")
+class PowerTest(unittest.TestCase):
+    def test_resume_usb_kick(self):
+        """Resuming from sleep/hibernate should kick USB devices."""
         try:
-            ipv6.bind(('::1', ipv4.getsockname()[1] + 1))
-            ipv6.listen(1)
-        except socket.error:
-            print("IPv6 not available, skipping")
-            return
+            usb_serial = subprocess.check_output(["adb", "-d", "get-serialno"]).strip()
+        except subprocess.CalledProcessError:
+            # If there are multiple USB devices, we don't have a way to check whether the selected
+            # device is USB.
+            raise unittest.SkipTest('requires single USB device')
 
-        for s in (ipv4, ipv6):
-            port = s.getsockname()[1]
-            output = subprocess.check_output(
-                ['adb', 'connect', 'localhost:{}'.format(port)])
+        try:
+            serial = subprocess.check_output(["adb", "get-serialno"]).strip()
+        except subprocess.CalledProcessError:
+            # Did you forget to select a device with $ANDROID_SERIAL?
+            raise unittest.SkipTest('requires $ANDROID_SERIAL set to a USB device')
 
-            self.assertEqual(
-                output.strip(), 'connected to localhost:{}'.format(port))
-            s.close()
+        # Test only works with USB devices because adb _power_notification_thread does not kick
+        # non-USB devices on resume event.
+        if serial != usb_serial:
+            raise unittest.SkipTest('requires USB device')
+
+        # Run an adb shell command in the background that takes a while to complete.
+        proc = subprocess.Popen(['adb', 'shell', 'sleep', '5'])
+
+        # Wait for startup of adb server's _power_notification_thread.
+        time.sleep(0.1)
+
+        # Simulate resuming from sleep/hibernation by sending Windows message.
+        import ctypes
+        from ctypes import wintypes
+        HWND_BROADCAST = 0xffff
+        WM_POWERBROADCAST = 0x218
+        PBT_APMRESUMEAUTOMATIC = 0x12
+
+        PostMessageW = ctypes.windll.user32.PostMessageW
+        PostMessageW.restype = wintypes.BOOL
+        PostMessageW.argtypes = (wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM)
+        result = PostMessageW(HWND_BROADCAST, WM_POWERBROADCAST, PBT_APMRESUMEAUTOMATIC, 0)
+        if not result:
+            raise ctypes.WinError()
+
+        # Wait for connection to adb shell to be broken by _power_notification_thread detecting the
+        # Windows message.
+        start = time.time()
+        proc.wait()
+        end = time.time()
+
+        # If the power event was detected, the adb shell command should be broken very quickly.
+        self.assertLess(end - start, 2)
 
 
 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__':
+if __name__ == "__main__":
     main()
diff --git a/adb/test_device.py b/adb/test_device.py
old mode 100644
new mode 100755
index 72e1c67..f95a5b3
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -35,6 +35,8 @@
 import time
 import unittest
 
+from datetime import datetime
+
 import adb
 
 def requires_root(func):
@@ -188,8 +190,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
@@ -752,9 +752,8 @@
             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."""
+    def disabled_test_push_empty(self):
+        """Push an empty directory to the device."""
         self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
         self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
 
@@ -765,13 +764,15 @@
             os.chmod(host_dir, 0o700)
 
             # Create an empty directory.
-            os.mkdir(os.path.join(host_dir, 'empty'))
+            empty_dir_path = os.path.join(host_dir, 'empty')
+            os.mkdir(empty_dir_path);
 
-            self.device.push(host_dir, self.DEVICE_TEMP_DIR)
+            self.device.push(empty_dir_path, self.DEVICE_TEMP_DIR)
 
-            test_empty_cmd = ['[', '-d',
-                              os.path.join(self.DEVICE_TEMP_DIR, 'empty')]
+            remote_path = os.path.join(self.DEVICE_TEMP_DIR, "empty")
+            test_empty_cmd = ["[", "-d", remote_path, "]"]
             rc, _, _ = self.device.shell_nocheck(test_empty_cmd)
+
             self.assertEqual(rc, 0)
             self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
         finally:
@@ -868,6 +869,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()
@@ -1020,7 +1036,8 @@
             if host_dir is not None:
                 shutil.rmtree(host_dir)
 
-    def test_pull_symlink_dir(self):
+    # selinux prevents adbd from accessing symlinks on /data/local/tmp.
+    def disabled_test_pull_symlink_dir(self):
         """Pull a symlink to a directory of symlinks to files."""
         try:
             host_dir = tempfile.mkdtemp()
@@ -1187,7 +1204,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)
@@ -1288,6 +1305,294 @@
                 self.assertEqual(length, len(stdout) - 4)
                 self.assertEqual(stdout, "\0" * length + "foo\n")
 
+    def test_zero_packet(self):
+        """Test for http://b/113070258
+
+        Make sure that we don't blow up when sending USB transfers that line up
+        exactly with the USB packet size.
+        """
+
+        local_port = int(self.device.forward("tcp:0", "tcp:12345"))
+        try:
+            for size in [512, 1024]:
+                def listener():
+                    cmd = ["echo foo | nc -l -p 12345; echo done"]
+                    rc, stdout, stderr = self.device.shell_nocheck(cmd)
+
+                thread = threading.Thread(target=listener)
+                thread.start()
+
+                # Wait a bit to let the shell command start.
+                time.sleep(0.25)
+
+                sock = socket.create_connection(("localhost", local_port))
+                with contextlib.closing(sock):
+                    bytesWritten = sock.send("a" * size)
+                    self.assertEqual(size, bytesWritten)
+                    readBytes = sock.recv(4096)
+                    self.assertEqual("foo\n", readBytes)
+
+                thread.join()
+        finally:
+            self.device.forward_remove("tcp:{}".format(local_port))
+
+
+class SocketTest(DeviceTest):
+    def test_socket_flush(self):
+        """Test that we handle socket closure properly.
+
+        If we're done writing to a socket, closing before the other end has
+        closed will send a TCP_RST if we have incoming data queued up, which
+        may result in data that we've written being discarded.
+
+        Bug: http://b/74616284
+        """
+        s = socket.create_connection(("localhost", 5037))
+
+        def adb_length_prefixed(string):
+            encoded = string.encode("utf8")
+            result = b"%04x%s" % (len(encoded), encoded)
+            return result
+
+        if "ANDROID_SERIAL" in os.environ:
+            transport_string = "host:transport:" + os.environ["ANDROID_SERIAL"]
+        else:
+            transport_string = "host:transport-any"
+
+        s.sendall(adb_length_prefixed(transport_string))
+        response = s.recv(4)
+        self.assertEquals(b"OKAY", response)
+
+        shell_string = "shell:sleep 0.5; dd if=/dev/zero bs=1m count=1 status=none; echo foo"
+        s.sendall(adb_length_prefixed(shell_string))
+
+        response = s.recv(4)
+        self.assertEquals(b"OKAY", response)
+
+        # Spawn a thread that dumps garbage into the socket until failure.
+        def spam():
+            buf = b"\0" * 16384
+            try:
+                while True:
+                    s.sendall(buf)
+            except Exception as ex:
+                print(ex)
+
+        thread = threading.Thread(target=spam)
+        thread.start()
+
+        time.sleep(1)
+
+        received = b""
+        while True:
+            read = s.recv(512)
+            if len(read) == 0:
+                break
+            received += read
+
+        self.assertEquals(1024 * 1024 + len("foo\n"), len(received))
+        thread.join()
+
+
+if sys.platform == "win32":
+    # From https://stackoverflow.com/a/38749458
+    import os
+    import contextlib
+    import msvcrt
+    import ctypes
+    from ctypes import wintypes
+
+    kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
+
+    GENERIC_READ  = 0x80000000
+    GENERIC_WRITE = 0x40000000
+    FILE_SHARE_READ  = 1
+    FILE_SHARE_WRITE = 2
+    CONSOLE_TEXTMODE_BUFFER = 1
+    INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value
+    STD_OUTPUT_HANDLE = wintypes.DWORD(-11)
+    STD_ERROR_HANDLE = wintypes.DWORD(-12)
+
+    def _check_zero(result, func, args):
+        if not result:
+            raise ctypes.WinError(ctypes.get_last_error())
+        return args
+
+    def _check_invalid(result, func, args):
+        if result == INVALID_HANDLE_VALUE:
+            raise ctypes.WinError(ctypes.get_last_error())
+        return args
+
+    if not hasattr(wintypes, 'LPDWORD'): # Python 2
+        wintypes.LPDWORD = ctypes.POINTER(wintypes.DWORD)
+        wintypes.PSMALL_RECT = ctypes.POINTER(wintypes.SMALL_RECT)
+
+    class COORD(ctypes.Structure):
+        _fields_ = (('X', wintypes.SHORT),
+                    ('Y', wintypes.SHORT))
+
+    class CONSOLE_SCREEN_BUFFER_INFOEX(ctypes.Structure):
+        _fields_ = (('cbSize',               wintypes.ULONG),
+                    ('dwSize',               COORD),
+                    ('dwCursorPosition',     COORD),
+                    ('wAttributes',          wintypes.WORD),
+                    ('srWindow',             wintypes.SMALL_RECT),
+                    ('dwMaximumWindowSize',  COORD),
+                    ('wPopupAttributes',     wintypes.WORD),
+                    ('bFullscreenSupported', wintypes.BOOL),
+                    ('ColorTable',           wintypes.DWORD * 16))
+        def __init__(self, *args, **kwds):
+            super(CONSOLE_SCREEN_BUFFER_INFOEX, self).__init__(
+                    *args, **kwds)
+            self.cbSize = ctypes.sizeof(self)
+
+    PCONSOLE_SCREEN_BUFFER_INFOEX = ctypes.POINTER(
+                                        CONSOLE_SCREEN_BUFFER_INFOEX)
+    LPSECURITY_ATTRIBUTES = wintypes.LPVOID
+
+    kernel32.GetStdHandle.errcheck = _check_invalid
+    kernel32.GetStdHandle.restype = wintypes.HANDLE
+    kernel32.GetStdHandle.argtypes = (
+        wintypes.DWORD,) # _In_ nStdHandle
+
+    kernel32.CreateConsoleScreenBuffer.errcheck = _check_invalid
+    kernel32.CreateConsoleScreenBuffer.restype = wintypes.HANDLE
+    kernel32.CreateConsoleScreenBuffer.argtypes = (
+        wintypes.DWORD,        # _In_       dwDesiredAccess
+        wintypes.DWORD,        # _In_       dwShareMode
+        LPSECURITY_ATTRIBUTES, # _In_opt_   lpSecurityAttributes
+        wintypes.DWORD,        # _In_       dwFlags
+        wintypes.LPVOID)       # _Reserved_ lpScreenBufferData
+
+    kernel32.GetConsoleScreenBufferInfoEx.errcheck = _check_zero
+    kernel32.GetConsoleScreenBufferInfoEx.argtypes = (
+        wintypes.HANDLE,               # _In_  hConsoleOutput
+        PCONSOLE_SCREEN_BUFFER_INFOEX) # _Out_ lpConsoleScreenBufferInfo
+
+    kernel32.SetConsoleScreenBufferInfoEx.errcheck = _check_zero
+    kernel32.SetConsoleScreenBufferInfoEx.argtypes = (
+        wintypes.HANDLE,               # _In_  hConsoleOutput
+        PCONSOLE_SCREEN_BUFFER_INFOEX) # _In_  lpConsoleScreenBufferInfo
+
+    kernel32.SetConsoleWindowInfo.errcheck = _check_zero
+    kernel32.SetConsoleWindowInfo.argtypes = (
+        wintypes.HANDLE,      # _In_ hConsoleOutput
+        wintypes.BOOL,        # _In_ bAbsolute
+        wintypes.PSMALL_RECT) # _In_ lpConsoleWindow
+
+    kernel32.FillConsoleOutputCharacterW.errcheck = _check_zero
+    kernel32.FillConsoleOutputCharacterW.argtypes = (
+        wintypes.HANDLE,  # _In_  hConsoleOutput
+        wintypes.WCHAR,   # _In_  cCharacter
+        wintypes.DWORD,   # _In_  nLength
+        COORD,            # _In_  dwWriteCoord
+        wintypes.LPDWORD) # _Out_ lpNumberOfCharsWritten
+
+    kernel32.ReadConsoleOutputCharacterW.errcheck = _check_zero
+    kernel32.ReadConsoleOutputCharacterW.argtypes = (
+        wintypes.HANDLE,  # _In_  hConsoleOutput
+        wintypes.LPWSTR,  # _Out_ lpCharacter
+        wintypes.DWORD,   # _In_  nLength
+        COORD,            # _In_  dwReadCoord
+        wintypes.LPDWORD) # _Out_ lpNumberOfCharsRead
+
+    @contextlib.contextmanager
+    def allocate_console():
+        allocated = kernel32.AllocConsole()
+        try:
+            yield allocated
+        finally:
+            if allocated:
+                kernel32.FreeConsole()
+
+    @contextlib.contextmanager
+    def console_screen(ncols=None, nrows=None):
+        info = CONSOLE_SCREEN_BUFFER_INFOEX()
+        new_info = CONSOLE_SCREEN_BUFFER_INFOEX()
+        nwritten = (wintypes.DWORD * 1)()
+        hStdOut = kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
+        kernel32.GetConsoleScreenBufferInfoEx(
+               hStdOut, ctypes.byref(info))
+        if ncols is None:
+            ncols = info.dwSize.X
+        if nrows is None:
+            nrows = info.dwSize.Y
+        elif nrows > 9999:
+            raise ValueError('nrows must be 9999 or less')
+        fd_screen = None
+        hScreen = kernel32.CreateConsoleScreenBuffer(
+                    GENERIC_READ | GENERIC_WRITE,
+                    FILE_SHARE_READ | FILE_SHARE_WRITE,
+                    None, CONSOLE_TEXTMODE_BUFFER, None)
+        try:
+            fd_screen = msvcrt.open_osfhandle(
+                            hScreen, os.O_RDWR | os.O_BINARY)
+            kernel32.GetConsoleScreenBufferInfoEx(
+                   hScreen, ctypes.byref(new_info))
+            new_info.dwSize = COORD(ncols, nrows)
+            new_info.srWindow = wintypes.SMALL_RECT(
+                    Left=0, Top=0, Right=(ncols - 1),
+                    Bottom=(info.srWindow.Bottom - info.srWindow.Top))
+            kernel32.SetConsoleScreenBufferInfoEx(
+                    hScreen, ctypes.byref(new_info))
+            kernel32.SetConsoleWindowInfo(hScreen, True,
+                    ctypes.byref(new_info.srWindow))
+            kernel32.FillConsoleOutputCharacterW(
+                    hScreen, u'\0', ncols * nrows, COORD(0,0), nwritten)
+            kernel32.SetConsoleActiveScreenBuffer(hScreen)
+            try:
+                yield fd_screen
+            finally:
+                kernel32.SetConsoleScreenBufferInfoEx(
+                    hStdOut, ctypes.byref(info))
+                kernel32.SetConsoleWindowInfo(hStdOut, True,
+                        ctypes.byref(info.srWindow))
+                kernel32.SetConsoleActiveScreenBuffer(hStdOut)
+        finally:
+            if fd_screen is not None:
+                os.close(fd_screen)
+            else:
+                kernel32.CloseHandle(hScreen)
+
+    def read_screen(fd):
+        hScreen = msvcrt.get_osfhandle(fd)
+        csbi = CONSOLE_SCREEN_BUFFER_INFOEX()
+        kernel32.GetConsoleScreenBufferInfoEx(
+            hScreen, ctypes.byref(csbi))
+        ncols = csbi.dwSize.X
+        pos = csbi.dwCursorPosition
+        length = ncols * pos.Y + pos.X + 1
+        buf = (ctypes.c_wchar * length)()
+        n = (wintypes.DWORD * 1)()
+        kernel32.ReadConsoleOutputCharacterW(
+            hScreen, buf, length, COORD(0,0), n)
+        lines = [buf[i:i+ncols].rstrip(u'\0')
+                    for i in range(0, n[0], ncols)]
+        return u'\n'.join(lines)
+
+@unittest.skipUnless(sys.platform == "win32", "requires Windows")
+class WindowsConsoleTest(DeviceTest):
+    def test_unicode_output(self):
+        """Test Unicode command line parameters and Unicode console window output.
+
+        Bug: https://issuetracker.google.com/issues/111972753
+        """
+        # If we don't have a console window, allocate one. This isn't necessary if we're already
+        # being run from a console window, which is typical.
+        with allocate_console() as allocated_console:
+            # Create a temporary console buffer and switch to it. We could also pass a parameter of
+            # ncols=len(unicode_string), but it causes the window to flash as it is resized and
+            # likely unnecessary given the typical console window size.
+            with console_screen(nrows=1000) as screen:
+                unicode_string = u'로보카 폴리'
+                # Run adb and allow it to detect that stdout is a console, not a pipe, by using
+                # device.shell_popen() which does not use a pipe, unlike device.shell().
+                process = self.device.shell_popen(['echo', '"' + unicode_string + '"'])
+                process.wait()
+                # Read what was written by adb to the temporary console buffer.
+                console_output = read_screen(screen)
+                self.assertEqual(unicode_string, console_output)
+
 
 def main():
     random.seed(0)
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 6b1a00b..841865a 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -17,6 +17,7 @@
 #define TRACE_TAG TRANSPORT
 
 #include "sysdeps.h"
+
 #include "transport.h"
 
 #include <ctype.h>
@@ -28,8 +29,11 @@
 #include <unistd.h>
 
 #include <algorithm>
+#include <deque>
 #include <list>
+#include <memory>
 #include <mutex>
+#include <set>
 #include <thread>
 
 #include <android-base/logging.h>
@@ -38,15 +42,20 @@
 #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);
+using android::base::ScopedLockAssertion;
+
+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*>();
@@ -59,12 +68,324 @@
 const char* const kFeatureStat2 = "stat_v2";
 const char* const kFeatureLibusb = "libusb";
 const char* const kFeaturePushSync = "push_sync";
+const char* const kFeatureApex = "apex";
+const char* const kFeatureFixedPushMkdir = "fixed_push_mkdir";
+const char* const kFeatureAbb = "abb";
+const char* const kFeatureFixedPushSymlinkTimestamp = "fixed_push_symlink_timestamp";
+const char* const kFeatureAbbExec = "abb_exec";
+
+namespace {
+
+#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);
+
+    // Wake up the ReconnectHandler thread to have it check for kicked transports.
+    void CheckForKicked();
+
+  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 {
+            if (reconnect_time == rhs.reconnect_time) {
+                return reinterpret_cast<uintptr_t>(transport) <
+                       reinterpret_cast<uintptr_t>(rhs.transport);
+            }
+            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::set<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_.begin();
+        reconnect_queue_.erase(reconnect_queue_.begin());
+        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::CheckForKicked() {
+    reconnect_cv_.notify_one();
+}
+
+void ReconnectHandler::Run() {
+    while (true) {
+        ReconnectAttempt attempt;
+        {
+            std::unique_lock<std::mutex> lock(reconnect_mutex_);
+            ScopedLockAssertion 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_.begin()->reconnect_time);
+            } else {
+                reconnect_cv_.wait(lock);
+            }
+
+            if (!running_) return;
+
+            // Scan the whole list for kicked transports, so that we immediately handle an explicit
+            // disconnect request.
+            bool kicked = false;
+            for (auto it = reconnect_queue_.begin(); it != reconnect_queue_.end();) {
+                if (it->transport->kicked()) {
+                    D("transport %s was kicked. giving up on it.", it->transport->serial.c_str());
+                    remove_transport(it->transport);
+                    it = reconnect_queue_.erase(it);
+                } else {
+                    ++it;
+                }
+                kicked = true;
+            }
+
+            if (reconnect_queue_.empty()) continue;
+
+            // Go back to sleep if we either woke up spuriously, or we were woken up to remove
+            // a kicked transport, and the first transport isn't ready for reconnection yet.
+            auto now = std::chrono::steady_clock::now();
+            if (reconnect_queue_.begin()->reconnect_time > now) {
+                continue;
+            }
+
+            attempt = *reconnect_queue_.begin();
+            reconnect_queue_.erase(reconnect_queue_.begin());
+        }
+        D("attempting to reconnect %s", attempt.transport->serial.c_str());
+
+        switch (attempt.transport->Reconnect()) {
+            case ReconnectResult::Retry: {
+                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;
+            }
+
+            case ReconnectResult::Success:
+                D("reconnection to %s succeeded.", attempt.transport->serial.c_str());
+                register_transport(attempt.transport);
+                continue;
+
+            case ReconnectResult::Abort:
+                D("cancelling reconnection attempt to %s.", attempt.transport->serial.c_str());
+                remove_transport(attempt.transport);
+                continue;
+        }
+    }
+}
+
+static auto& reconnect_handler = *new ReconnectHandler();
+
+#endif
+
+}  // namespace
 
 TransportId NextTransportId() {
     static std::atomic<TransportId> next(1);
     return next++;
 }
 
+void Connection::Reset() {
+    LOG(INFO) << "Connection::Reset(): stopping";
+    Stop();
+}
+
+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_);
+            ScopedLockAssertion 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::Reset() {
+    {
+        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;
+        }
+    }
+
+    LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): resetting";
+    this->underlying_->Reset();
+    Stop();
+}
+
+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)");
@@ -107,103 +428,6 @@
     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;
-    char cmd[9];
-    char arg0[12], arg1[12];
-    int n;
-
-    for (n = 0; n < 4; n++) {
-        int b = (command >> (n * 8)) & 255;
-        if (b < 32 || b >= 127) break;
-        cmd[n] = (char)b;
-    }
-    if (n == 4) {
-        cmd[4] = 0;
-    } else {
-        /* There is some non-ASCII name in the command, so dump
-            * the hexadecimal value instead */
-        snprintf(cmd, sizeof cmd, "%08x", command);
-    }
-
-    if (p->msg.arg0 < 256U)
-        snprintf(arg0, sizeof arg0, "%d", p->msg.arg0);
-    else
-        snprintf(arg0, sizeof arg0, "0x%x", p->msg.arg0);
-
-    if (p->msg.arg1 < 256U)
-        snprintf(arg1, sizeof arg1, "%d", p->msg.arg1);
-    else
-        snprintf(arg1, sizeof arg1, "0x%x", p->msg.arg1);
-
-    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->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);
-            return;
-        }
-
-        handle_packet(p, (atransport*)_t);
-    }
-}
-
 void send_packet(apacket* p, atransport* t) {
     p->msg.magic = p->msg.command ^ 0xffffffff;
     // compute a checksum for connection/auth packets for compatibility reasons
@@ -213,176 +437,40 @@
         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) {
-        fatal("Transport is null");
+    if (t == nullptr) {
+        LOG(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;
-    D("sending SYNC packet (len = %u, payload.size() = %zu)", p->msg.data_length, p->payload.size());
-    if (write_packet(t->fd, t->serial, &p)) {
-        put_apacket(p);
-        D("%s: failed to write SYNC packet", t->serial);
-        goto oops;
-    }
-
-    D("%s: data pump started", t->serial);
-    for (;;) {
-        ATRACE_NAME("read_transport loop");
-        p = get_apacket();
-
-        {
-            ATRACE_NAME("read_transport read_remote");
-            if (!t->connection->Read(p)) {
-                D("%s: remote read failed for transport", t->serial);
-                put_apacket(p);
-                break;
-            }
-
-            if (!check_header(p, t)) {
-                D("%s: remote read: bad header", t->serial);
-                put_apacket(p);
-                break;
-            }
-
-#if ADB_HOST
-            if (p->msg.command == 0) {
-                put_apacket(p);
-                continue;
-            }
-#endif
-        }
-
-        D("%s: received remote packet, sending to transport", t->serial);
-        if (write_packet(t->fd, t->serial, &p)) {
-            put_apacket(p);
-            D("%s: failed to write apacket to transport", t->serial);
-            goto oops;
-        }
-    }
-
-    D("%s: SYNC offline for transport", t->serial);
-    p = get_apacket();
-    p->msg.command = A_SYNC;
-    p->msg.arg0 = 0;
-    p->msg.arg1 = 0;
-    p->msg.magic = A_SYNC ^ 0xffffffff;
-    if (write_packet(t->fd, t->serial, &p)) {
-        put_apacket(p);
-        D("%s: failed to write SYNC apacket to transport", t->serial);
-    }
-
-oops:
-    D("%s: read_transport thread is exiting", t->serial);
-    kick_transport(t);
-    transport_unref(t);
-}
-
-// write_transport thread gets packets sent by the main thread (through send_packet()),
-// and writes to a transport (representing a usb/tcp connection).
-static void write_transport_thread(void* _t) {
-    atransport* t = reinterpret_cast<atransport*>(_t);
-    apacket* p;
-    int active = 0;
-
-    adb_thread_setname(
-        android::base::StringPrintf("->%s", (t->serial != nullptr ? t->serial : "transport")));
-    D("%s: starting write_transport thread, reading from fd %d", t->serial, t->fd);
-
-    for (;;) {
-        ATRACE_NAME("write_transport loop");
-        if (read_packet(t->fd, t->serial, &p)) {
-            D("%s: failed to read apacket from transport on fd %d", t->serial, t->fd);
-            break;
-        }
-
-        if (p->msg.command == A_SYNC) {
-            if (p->msg.arg0 == 0) {
-                D("%s: transport SYNC offline", t->serial);
-                put_apacket(p);
-                break;
-            } else {
-                if (p->msg.arg1 == t->sync_token) {
-                    D("%s: transport SYNC online", t->serial);
-                    active = 1;
-                } else {
-                    D("%s: transport ignoring SYNC %d != %d", t->serial, p->msg.arg1, t->sync_token);
-                }
-            }
-        } else {
-            if (active) {
-                D("%s: transport got packet, sending to remote", t->serial);
-                ATRACE_NAME("write_transport write_remote");
-
-                // Allow sending the payload's implicit null terminator.
-                if (p->msg.data_length != p->payload.size()) {
-                    LOG(FATAL) << "packet data length doesn't match payload: msg.data_length = "
-                               << p->msg.data_length << ", payload.size() = " << p->payload.size();
-                }
-
-                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) {
+void kick_transport(atransport* t, bool reset) {
     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();
+        if (reset) {
+            t->Reset();
+        } else {
+            t->Kick();
+        }
     }
+
+#if ADB_HOST
+    reconnect_handler.CheckForKicked();
+#endif
 }
 
 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
 
@@ -422,14 +510,14 @@
 
     D("device tracker %p removed", tracker);
     if (peer) {
-        peer->peer = NULL;
+        peer->peer = nullptr;
         peer->close(peer);
     }
     device_tracker_remove(tracker);
     delete tracker;
 }
 
-static int device_tracker_enqueue(asocket* socket, std::string) {
+static int device_tracker_enqueue(asocket* socket, apacket::payload_type) {
     /* you can't read from a device tracker, close immediately */
     device_tracker_close(socket);
     return -1;
@@ -438,7 +526,7 @@
 static int device_tracker_send(device_tracker* tracker, const std::string& string) {
     asocket* peer = tracker->socket.peer;
 
-    std::string data;
+    apacket::payload_type data;
     data.resize(4 + string.size());
     char buf[5];
     snprintf(buf, sizeof(buf), "%04x", static_cast<int>(string.size()));
@@ -462,7 +550,7 @@
 
 asocket* create_device_tracker(bool long_output) {
     device_tracker* tracker = new device_tracker();
-    if (tracker == nullptr) fatal("cannot allocate device tracker");
+    if (tracker == nullptr) LOG(FATAL) << "cannot allocate device tracker";
 
     D("device tracker %p created", tracker);
 
@@ -559,9 +647,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)) {
@@ -569,31 +656,19 @@
     }
 
     if (transport_read_action(_fd, &m)) {
-        fatal_errno("cannot read transport registration socket");
+        PLOG(FATAL) << "cannot read transport registration socket";
     }
 
     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::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();
@@ -602,53 +677,75 @@
 
     /* 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) {
+            LOG(INFO) << t->serial_name() << ": connection terminated: " << error;
+            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::recursive_mutex> lock(transport_lock);
-        pending_list.remove(t);
-        transport_list.push_front(t);
+        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];
 
     if (adb_socketpair(s)) {
-        fatal_errno("cannot open transport registration socketpair");
+        PLOG(FATAL) << "cannot open transport registration socketpair";
     }
     D("socketpair: (%d,%d)", s[0], s[1]);
 
     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::recursive_mutex> lock(transport_lock);
     for (auto t : transport_list) {
@@ -657,13 +754,13 @@
 }
 
 /* the fdevent select pump is single threaded */
-static void register_transport(atransport* transport) {
+void register_transport(atransport* transport) {
     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");
+        PLOG(FATAL) << "cannot write transport registration socket";
     }
 }
 
@@ -671,48 +768,65 @@
     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");
+        PLOG(FATAL) << "cannot write transport registration socket";
     }
 }
 
 static void transport_unref(atransport* t) {
+    check_main_thread();
     CHECK(t != nullptr);
 
     std::lock_guard<std::recursive_mutex> lock(transport_lock);
     CHECK_GT(t->ref_count, 0u);
     t->ref_count--;
     if (t->ref_count == 0) {
-        D("transport: %s unref (kicking and closing)", t->serial);
-        t->connection->Close();
+        LOG(INFO) << "destroying transport " << t->serial_name();
+        t->connection()->Stop();
+#if ADB_HOST
+        if (t->IsTcpDevice() && !t->kicked()) {
+            D("transport: %s unref (attempting reconnection)", t->serial.c_str());
+
+            // We need to clear the transport's keys, so that on the next connection, it tries
+            // again from the beginning.
+            t->ResetKeys();
+            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, t->ref_count);
+        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, TransportId transport_id,
@@ -736,9 +850,7 @@
     std::unique_lock<std::recursive_mutex> lock(transport_lock);
     for (const auto& t : transport_list) {
         if (t->GetConnectionState() == kCsNoPerm) {
-#if ADB_HOST
             *error_out = UsbNoPermissionsLongHelpText();
-#endif
             continue;
         }
 
@@ -787,22 +899,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) {
@@ -812,15 +943,45 @@
     return result;
 }
 
+bool ConnectionWaitable::WaitForConnection(std::chrono::milliseconds timeout) {
+    std::unique_lock<std::mutex> lock(mutex_);
+    ScopedLockAssertion 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) {
-    return this->connection->Write(p) ? 0 : -1;
+    return this->connection()->Write(std::unique_ptr<apacket>(p)) ? 0 : -1;
+}
+
+void atransport::Reset() {
+    if (!kicked_.exchange(true)) {
+        LOG(INFO) << "resetting transport " << this << " " << this->serial;
+        this->connection()->Reset();
+    }
 }
 
 void atransport::Kick() {
-    if (!kicked_) {
-        D("kicking transport %s", this->serial);
-        kicked_ = true;
-        this->connection->Close();
+    if (!kicked_.exchange(true)) {
+        LOG(INFO) << "kicking transport " << this << " " << this->serial;
+        this->connection()->Stop();
     }
 }
 
@@ -833,7 +994,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:
@@ -846,12 +1012,18 @@
             return "host";
         case kCsRecovery:
             return "recovery";
+        case kCsRescue:
+            return "rescue";
         case kCsNoPerm:
             return UsbNoPermissionsShortHelpText();
         case kCsSideload:
             return "sideload";
         case kCsUnauthorized:
             return "unauthorized";
+        case kCsAuthorizing:
+            return "authorizing";
+        case kCsConnecting:
+            return "connecting";
         default:
             return "unknown";
     }
@@ -870,26 +1042,28 @@
     return max_payload;
 }
 
-namespace {
-
-constexpr char kFeatureStringDelimiter = ',';
-
-}  // namespace
-
 const FeatureSet& supported_features() {
     // Local static allocation to avoid global non-POD variables.
     static const FeatureSet* features = new FeatureSet{
-        kFeatureShell2, kFeatureCmd, kFeatureStat2,
-        // Increment ADB_SERVER_VERSION whenever the feature list changes to
-        // make sure that the adb client and server features stay in sync
-        // (http://b/24370690).
+            kFeatureShell2,
+            kFeatureCmd,
+            kFeatureStat2,
+            kFeatureFixedPushMkdir,
+            kFeatureApex,
+            kFeatureAbb,
+            kFeatureFixedPushSymlinkTimestamp,
+            kFeatureAbbExec,
+            // Increment ADB_SERVER_VERSION when adding a feature that adbd needs
+            // to know about. Otherwise, the client can be stuck running an old
+            // version of the server even after upgrading their copy of adb.
+            // (http://b/24370690)
     };
 
     return *features;
 }
 
 std::string FeatureSetToString(const FeatureSet& features) {
-    return android::base::Join(features, kFeatureStringDelimiter);
+    return android::base::Join(features, ',');
 }
 
 FeatureSet StringToFeatureSet(const std::string& features_string) {
@@ -897,7 +1071,7 @@
         return FeatureSet();
     }
 
-    auto names = android::base::Split(features_string, {kFeatureStringDelimiter});
+    auto names = android::base::Split(features_string, ",");
     return FeatureSet(names.begin(), names.end());
 }
 
@@ -929,7 +1103,7 @@
 }
 
 bool atransport::MatchesTarget(const std::string& target) const {
-    if (serial) {
+    if (!serial.empty()) {
         if (target == serial) {
             return true;
         } else if (type == kTransportLocal) {
@@ -958,10 +1132,17 @@
         }
     }
 
-    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);
+}
+
+ReconnectResult atransport::Reconnect() {
+    return reconnect_(this);
 }
 
 #if ADB_HOST
@@ -974,9 +1155,9 @@
     return str;
 }
 
-static void append_transport_info(std::string* result, const char* key, const char* value,
+static void append_transport_info(std::string* result, const char* key, const std::string& value,
                                   bool alphanumeric) {
-    if (value == nullptr || *value == '\0') {
+    if (value.empty()) {
         return;
     }
 
@@ -986,8 +1167,8 @@
 }
 
 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)";
     }
 
@@ -996,7 +1177,8 @@
         *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);
@@ -1019,7 +1201,7 @@
         if (x->type != y->type) {
             return x->type < y->type;
         }
-        return strcmp(x->serial, y->serial) < 0;
+        return x->serial < y->serial;
     });
 
     std::string result;
@@ -1029,62 +1211,81 @@
     return result;
 }
 
-void close_usb_devices(std::function<bool(const atransport*)> predicate) {
+void close_usb_devices(std::function<bool(const atransport*)> predicate, bool reset) {
     std::lock_guard<std::recursive_mutex> lock(transport_lock);
     for (auto& t : transport_list) {
         if (predicate(t)) {
-            t->Kick();
+            if (reset) {
+                t->Reset();
+            } else {
+                t->Kick();
+            }
         }
     }
 }
 
 /* hack for osx */
-void close_usb_devices() {
-    close_usb_devices([](const atransport*) { return true; });
+void close_usb_devices(bool reset) {
+    close_usb_devices([](const atransport*) { return true; }, reset);
 }
 #endif  // ADB_HOST
 
-int register_socket_transport(int s, const char* serial, int port, int local) {
-    atransport* t = new atransport();
+bool register_socket_transport(unique_fd s, std::string serial, int port, int local,
+                               atransport::ReconnectCallback reconnect, int* error) {
+    atransport* t = new atransport(std::move(reconnect), kCsOffline);
 
-    if (!serial) {
-        char buf[32];
-        snprintf(buf, sizeof(buf), "T-%p", t);
-        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.c_str(), s.get(), port);
+    if (init_socket_transport(t, std::move(s), port, local) < 0) {
         delete t;
-        return -1;
+        if (error) *error = errno;
+        return false;
     }
 
     std::unique_lock<std::recursive_mutex> lock(transport_lock);
     for (const auto& transport : pending_list) {
-        if (transport->serial && strcmp(serial, transport->serial) == 0) {
+        if (serial == transport->serial) {
             VLOG(TRANSPORT) << "socket transport " << transport->serial
                             << " is already in pending_list and fails to register";
             delete t;
-            return -1;
+            if (error) *error = EALREADY;
+            return false;
         }
     }
 
     for (const auto& transport : transport_list) {
-        if (transport->serial && strcmp(serial, transport->serial) == 0) {
+        if (serial == transport->serial) {
             VLOG(TRANSPORT) << "socket transport " << transport->serial
                             << " is already in transport_list and fails to register";
             delete t;
-            return -1;
+            if (error) *error = EALREADY;
+            return false;
         }
     }
 
+    t->serial = std::move(serial);
     pending_list.push_front(t);
-    t->serial = strdup(serial);
 
     lock.unlock();
 
+    auto waitable = t->connection_waitable();
     register_transport(t);
-    return 0;
+
+    if (local == 1) {
+        // Do not wait for emulator transports.
+        return true;
+    }
+
+    if (!waitable->WaitForConnection(std::chrono::seconds(10))) {
+        if (error) *error = ETIMEDOUT;
+        return false;
+    }
+
+    if (t->GetConnectionState() == kCsUnauthorized) {
+        if (error) *error = EPERM;
+        return false;
+    }
+
+    return true;
 }
 
 #if ADB_HOST
@@ -1093,7 +1294,7 @@
 
     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;
         }
@@ -1113,22 +1314,25 @@
             t->Kick();
         }
     }
+#if ADB_HOST
+    reconnect_handler.CheckForKicked();
+#endif
 }
 
 #endif
 
 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;
     }
 
     {
@@ -1139,16 +1343,15 @@
     register_transport(t);
 }
 
+#if ADB_HOST
 // This should only be used for transports with connection_state == kCsNoPerm.
 void unregister_usb_transport(usb_handle* usb) {
     std::lock_guard<std::recursive_mutex> lock(transport_lock);
     transport_list.remove_if([usb](atransport* t) {
-        if (auto connection = dynamic_cast<UsbConnection*>(t->connection.get())) {
-            return connection->handle_ == usb && t->GetConnectionState() == kCsNoPerm;
-        }
-        return false;
+        return t->GetUsbHandle() == usb && t->GetConnectionState() == kCsNoPerm;
     });
 }
+#endif
 
 bool check_header(apacket* p, atransport* t) {
     if (p->msg.magic != (p->msg.command ^ 0xffffffff)) {
@@ -1168,10 +1371,20 @@
 
 #if ADB_HOST
 std::shared_ptr<RSA> atransport::NextKey() {
-    if (keys_.empty()) keys_ = adb_auth_get_private_keys();
+    if (keys_.empty()) {
+        LOG(INFO) << "fetching keys for transport " << this->serial_name();
+        keys_ = adb_auth_get_private_keys();
+
+        // We should have gotten at least one key: the one that's automatically generated.
+        CHECK(!keys_.empty());
+    }
 
     std::shared_ptr<RSA> result = keys_[0];
     keys_.pop_front();
     return result;
 }
+
+void atransport::ResetKeys() {
+    keys_.clear();
+}
 #endif
diff --git a/adb/transport.h b/adb/transport.h
index 9700f44..f4490ed 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -20,18 +20,25 @@
 #include <sys/types.h>
 
 #include <atomic>
+#include <chrono>
+#include <condition_variable>
 #include <deque>
 #include <functional>
 #include <list>
 #include <memory>
 #include <mutex>
 #include <string>
+#include <string_view>
+#include <thread>
 #include <unordered_set>
 
+#include <android-base/macros.h>
+#include <android-base/thread_annotations.h>
 #include <openssl/rsa.h>
 
 #include "adb.h"
 #include "adb_unique_fd.h"
+#include "usb.h"
 
 typedef std::unordered_set<std::string> FeatureSet;
 
@@ -52,20 +59,65 @@
 extern const char* const kFeatureStat2;
 // The server is running with libusb enabled.
 extern const char* const kFeatureLibusb;
-// The server supports `push --sync`.
+// adbd supports `push --sync`.
 extern const char* const kFeaturePushSync;
+// adbd supports installing .apex packages.
+extern const char* const kFeatureApex;
+// adbd has b/110953234 fixed.
+extern const char* const kFeatureFixedPushMkdir;
+// adbd supports android binder bridge (abb).
+extern const char* const kFeatureAbb;
+// adbd properly updates symlink timestamps on push.
+extern const char* const kFeatureFixedPushSymlinkTimestamp;
 
 TransportId NextTransportId();
 
-// Abstraction for a blocking packet transport.
+// Abstraction for a non-blocking packet transport.
 struct Connection {
     Connection() = default;
-    Connection(const Connection& copy) = delete;
-    Connection(Connection&& move) = delete;
-
-    // Destroy a Connection. Formerly known as 'Close' in atransport.
     virtual ~Connection() = default;
 
+    void SetTransportName(std::string transport_name) {
+        transport_name_ = std::move(transport_name);
+    }
+
+    using ReadCallback = std::function<bool(Connection*, std::unique_ptr<apacket>)>;
+    void SetReadCallback(ReadCallback callback) {
+        CHECK(!read_callback_);
+        read_callback_ = callback;
+    }
+
+    // Called after the Connection has terminated, either by an error or because Stop was called.
+    using ErrorCallback = std::function<void(Connection*, const std::string&)>;
+    void SetErrorCallback(ErrorCallback callback) {
+        CHECK(!error_callback_);
+        error_callback_ = callback;
+    }
+
+    virtual bool Write(std::unique_ptr<apacket> packet) = 0;
+
+    virtual void Start() = 0;
+    virtual void Stop() = 0;
+
+    // Stop, and reset the device if it's a USB connection.
+    virtual void Reset();
+
+    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;
@@ -75,21 +127,51 @@
     // This method must be thread-safe, and must cause concurrent Reads/Writes to terminate.
     // Formerly known as 'Kick' in atransport.
     virtual void Close() = 0;
+
+    // Terminate a connection, and reset it.
+    virtual void Reset() = 0;
 };
 
-struct FdConnection : public Connection {
+struct BlockingConnectionAdapter : public Connection {
+    explicit BlockingConnectionAdapter(std::unique_ptr<BlockingConnection> connection);
+
+    virtual ~BlockingConnectionAdapter();
+
+    virtual bool Write(std::unique_ptr<apacket> packet) override final;
+
+    virtual void Start() override final;
+    virtual void Stop() override final;
+
+    virtual void Reset() 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;
+    virtual void Reset() override final { Close(); }
 
   private:
     unique_fd fd_;
 };
 
-struct UsbConnection : public Connection {
+struct UsbConnection : public BlockingConnection {
     explicit UsbConnection(usb_handle* handle) : handle_(handle) {}
     ~UsbConnection();
 
@@ -97,10 +179,46 @@
     bool Write(apacket* packet) override final;
 
     void Close() override final;
+    virtual void Reset() 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);
+};
+
+enum class ReconnectResult {
+    Retry,
+    Success,
+    Abort,
+};
+
 class atransport {
   public:
     // TODO(danalbert): We expose waaaaaaay too much stuff because this was
@@ -108,52 +226,66 @@
     // 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)
-        : id(NextTransportId()), connection_state_(state) {
-        transport_fde = {};
+    using ReconnectCallback = std::function<ReconnectResult(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 ReconnectResult::Abort; }, state) {}
+    virtual ~atransport();
 
     int Write(apacket* p);
+    void Reset();
     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);
 
+    void SetConnection(std::unique_ptr<Connection> connection);
+    std::shared_ptr<Connection> connection() {
+        std::lock_guard<std::mutex> lock(mutex_);
+        return connection_;
+    }
+
+    void SetUsbHandle(usb_handle* h) { usb_handle_ = h; }
+    usb_handle* GetUsbHandle() { return usb_handle_; }
+
     const TransportId id;
-    int fd = -1;
-    int transport_socket = -1;
-    fdevent transport_fde;
     size_t ref_count = 0;
-    uint32_t sync_token = 0;
     bool online = false;
     TransportType type = kTransportAny;
 
-    std::unique_ptr<Connection> connection;
-
     // Used to identify transports for clients.
-    char* serial = nullptr;
-    char* product = nullptr;
-    char* model = nullptr;
-    char* device = nullptr;
-    char* devpath = nullptr;
+    std::string serial;
+    std::string product;
+    std::string model;
+    std::string device;
+    std::string devpath;
 
     bool IsTcpDevice() const { return type == kTransportLocal; }
 
 #if ADB_HOST
     std::shared_ptr<RSA> NextKey();
+    void ResetKeys();
 #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;
@@ -187,8 +319,18 @@
     // 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:
-    bool kicked_ = false;
+    // 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.
+    ReconnectResult 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'.
@@ -204,6 +346,21 @@
     std::deque<std::shared_ptr<RSA>> keys_;
 #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_);
+
+    // USB handle for the connection, if available.
+    usb_handle* usb_handle_ = nullptr;
+
+    // A callback that will be invoked when the atransport needs to reconnect.
+    ReconnectCallback reconnect_;
+
+    std::mutex mutex_;
+
     DISALLOW_COPY_AND_ASSIGN(atransport);
 };
 
@@ -218,13 +375,14 @@
 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 kick_transport(atransport* t, bool reset = false);
 void update_transports(void);
 
 // Iterates across all of the current and pending transports.
 // 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);
@@ -232,6 +390,7 @@
 void kick_all_tcp_devices();
 void kick_all_transports();
 
+void register_transport(atransport* transport);
 void register_usb_transport(usb_handle* h, const char* serial,
                             const char* devpath, unsigned writeable);
 
@@ -239,18 +398,30 @@
 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);
+bool register_socket_transport(unique_fd s, std::string serial, int port, int local,
+                               atransport::ReconnectCallback reconnect, int* error = nullptr);
 
 // 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);
 
-void close_usb_devices();
-void close_usb_devices(std::function<bool(const atransport*)> predicate);
+void close_usb_devices(bool reset = false);
+void close_usb_devices(std::function<bool(const atransport*)> predicate, bool reset = false);
 
 void send_packet(apacket* p, atransport* t);
 
 asocket* create_device_tracker(bool long_output);
 
+#if !ADB_HOST
+unique_fd tcp_listen_inaddr_any(int port, std::string* error);
+void server_socket_thread(std::function<unique_fd(int, std::string*)> listen_func, int port);
+
+#if defined(__ANDROID__)
+void qemu_socket_thread(int port);
+bool use_qemu_goldfish();
+#endif
+
+#endif
+
 #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..a93e68a
--- /dev/null
+++ b/adb/transport_fd.cpp
@@ -0,0 +1,237 @@
+/*
+ * 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 <memory>
+#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 "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_);
+                    if (DispatchWrites() == WriteResult::Error) {
+                        *error = "write failed";
+                        return;
+                    }
+                }
+
+                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());
+        auto iovs = write_buffer_.iovecs();
+        ssize_t rc = adb_writev(fd_.get(), iovs.data(), iovs.size());
+        if (rc == -1) {
+            if (errno == EAGAIN || errno == EWOULDBLOCK) {
+                writable_ = false;
+                return WriteResult::TryAgain;
+            }
+
+            return WriteResult::Error;
+        } else if (rc == 0) {
+            errno = 0;
+            return WriteResult::Error;
+        }
+
+        // TODO: Implement a more efficient drop_front?
+        write_buffer_.take_front(rc);
+        writable_ = write_buffer_.empty();
+        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)));
+        }
+
+        WriteResult result = DispatchWrites();
+        if (result == WriteResult::TryAgain) {
+            WakeThread();
+        }
+        return result != 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 560a031..b9f738d 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -26,6 +26,8 @@
 #include <sys/types.h>
 
 #include <condition_variable>
+#include <functional>
+#include <memory>
 #include <mutex>
 #include <thread>
 #include <unordered_map>
@@ -44,18 +46,34 @@
 #include "adb_io.h"
 #include "adb_unique_fd.h"
 #include "adb_utils.h"
+#include "socket_spec.h"
 #include "sysdeps/chrono.h"
 
 #if ADB_HOST
 
 // Android Wear has been using port 5601 in all of its documentation/tooling,
 // but we search for emulators on ports [5554, 5555 + ADB_LOCAL_TRANSPORT_MAX].
-// Avoid stomping on their port by limiting the number of emulators that can be
-// connected.
-#define ADB_LOCAL_TRANSPORT_MAX 16
+// Avoid stomping on their port by restricting the active scanning range.
+// Once emulators self-(re-)register, they'll have to avoid 5601 in their own way.
+static int adb_local_transport_max_port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT + 16 * 2 - 1;
 
 static std::mutex& local_transports_lock = *new std::mutex();
 
+static void adb_local_transport_max_port_env_override() {
+    const char* env_max_s = getenv("ADB_LOCAL_TRANSPORT_MAX_PORT");
+    if (env_max_s != nullptr) {
+        size_t env_max;
+        if (ParseUint(&env_max, env_max_s, nullptr) && env_max < 65536) {
+            // < DEFAULT_ADB_LOCAL_TRANSPORT_PORT harmlessly mimics ADB_EMU=0
+            adb_local_transport_max_port = env_max;
+            D("transport: ADB_LOCAL_TRANSPORT_MAX_PORT read as %d", adb_local_transport_max_port);
+        } else {
+            D("transport: ADB_LOCAL_TRANSPORT_MAX_PORT '%s' invalid or >= 65536, so ignored",
+              env_max_s);
+        }
+    }
+}
+
 // We keep a map from emulator port to transport.
 // TODO: weak_ptr?
 static auto& local_transports GUARDED_BY(local_transports_lock) =
@@ -67,40 +85,63 @@
     return local_connect_arbitrary_ports(port - 1, port, &dummy) == 0;
 }
 
+std::tuple<unique_fd, int, std::string> tcp_connect(const std::string& address,
+                                                    std::string* response) {
+    unique_fd fd;
+    int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+    std::string serial;
+    std::string prefix_addr = address.starts_with("vsock:") ? address : "tcp:" + address;
+    if (socket_spec_connect(&fd, prefix_addr, &port, &serial, response)) {
+        close_on_exec(fd);
+        if (!set_tcp_keepalive(fd, 1)) {
+            D("warning: failed to configure TCP keepalives (%s)", strerror(errno));
+        }
+        return std::make_tuple(std::move(fd), port, serial);
+    }
+    return std::make_tuple(unique_fd(), 0, serial);
+}
+
 void connect_device(const std::string& address, std::string* response) {
     if (address.empty()) {
         *response = "empty address";
         return;
     }
 
+    D("connection requested to '%s'", address.c_str());
+    unique_fd fd;
+    int port;
     std::string serial;
-    std::string host;
-    int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
-    if (!android::base::ParseNetAddress(address, &host, &port, &serial, response)) {
+    std::tie(fd, port, serial) = tcp_connect(address, response);
+    if (fd.get() == -1) {
         return;
     }
+    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 ReconnectResult::Retry;
+        }
+        // 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 ? ReconnectResult::Success
+                                                                     : ReconnectResult::Retry;
+    };
 
-    std::string error;
-    int 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;
-    }
-
-    D("client: connected %s remote on fd %d", serial.c_str(), fd);
-    close_on_exec(fd);
-    disable_tcp_nagle(fd);
-
-    // Send a TCP keepalive ping to the device every second so we can detect disconnects.
-    if (!set_tcp_keepalive(fd, 1)) {
-        D("warning: failed to configure TCP keepalives (%s)", strerror(errno));
-    }
-
-    int ret = register_socket_transport(fd, serial.c_str(), port, 0);
-    if (ret < 0) {
-        adb_close(fd);
-        *response = android::base::StringPrintf("already connected to %s", serial.c_str());
+    int error;
+    if (!register_socket_transport(std::move(fd), serial, port, 0, std::move(reconnect), &error)) {
+        if (error == EALREADY) {
+            *response = android::base::StringPrintf("already connected to %s", serial.c_str());
+        } else if (error == EPERM) {
+            *response = android::base::StringPrintf("failed to authenticate 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());
     }
@@ -108,7 +149,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 ||
@@ -118,22 +159,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), std::move(serial), adb_port, 1,
+                                      [](atransport*) { return ReconnectResult::Abort; })) {
             return 0;
         }
-        adb_close(fd);
     }
     return -1;
 }
@@ -141,18 +182,16 @@
 #if ADB_HOST
 
 static void PollAllLocalPortsForEmulator() {
-    int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
-    int count = ADB_LOCAL_TRANSPORT_MAX;
-
     // Try to connect to any number of running emulator instances.
-    for ( ; count > 0; count--, port += 2 ) {
-        local_connect(port);
+    for (int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; port <= adb_local_transport_max_port;
+         port += 2) {
+        local_connect(port);  // Note, uses port and port-1, so '=max_port' is OK.
     }
 }
 
 // Retry the disconnected local port for 60 times, and sleep 1 second between two retries.
-constexpr uint32_t LOCAL_PORT_RETRY_COUNT = 60;
-constexpr auto LOCAL_PORT_RETRY_INTERVAL = 1s;
+static constexpr uint32_t LOCAL_PORT_RETRY_COUNT = 60;
+static constexpr auto LOCAL_PORT_RETRY_INTERVAL = 1s;
 
 struct RetryPort {
     int port;
@@ -207,190 +246,78 @@
     }
 }
 
-#else // ADB_HOST
+#else  // !ADB_HOST
 
-static void server_socket_thread(int port) {
-    int serverfd, fd;
-
+void server_socket_thread(std::function<unique_fd(int, std::string*)> listen_func, int port) {
     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);
-        }
 
-        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 serverfd;
+    std::string error;
+
+    while (serverfd == -1) {
+        errno = 0;
+        serverfd = listen_func(port, &error);
+        if (errno == EAFNOSUPPORT || errno == EINVAL || errno == EPROTONOSUPPORT) {
+            D("unrecoverable error: '%s'", error.c_str());
+            return;
+        } else 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 fd %d", serverfd.get());
+        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), std::move(serial), port, 1,
+                                      [](atransport*) { return ReconnectResult::Abort; });
         }
     }
     D("transport: server_socket_thread() exiting");
 }
 
-/* This is relevant only for ADB daemon running inside the emulator. */
-/*
- * Redefine open and write for qemu_pipe.h that contains inlined references
- * to those routines. We will redefine them back after qemu_pipe.h inclusion.
- */
-#undef open
-#undef read
-#undef write
-#define open    adb_open
-#define read    adb_read
-#define write   adb_write
-#include <qemu_pipe.h>
-#undef open
-#undef read
-#undef write
-#define open    ___xxx_open
-#define read    ___xxx_read
-#define write   ___xxx_write
+#endif
 
-/* A worker thread that monitors host connections, and registers a transport for
- * every new host connection. This thread replaces server_socket_thread on
- * condition that adbd daemon runs inside the emulator, and emulator uses QEMUD
- * pipe to communicate with adbd daemon inside the guest. This is done in order
- * to provide more robust communication channel between ADB host and guest. The
- * main issue with server_socket_thread approach is that it runs on top of TCP,
- * and thus is sensitive to network disruptions. For instance, the
- * ConnectionManager may decide to reset all network connections, in which case
- * the connection between ADB host and guest will be lost. To make ADB traffic
- * independent from the network, we use here 'adb' QEMUD service to transfer data
- * between the host, and the guest. See external/qemu/android/adb-*.* that
- * implements the emulator's side of the protocol. Another advantage of using
- * QEMUD approach is that ADB will be up much sooner, since it doesn't depend
- * anymore on network being set up.
- * The guest side of the protocol contains the following phases:
- * - Connect with adb QEMUD service. In this phase a handle to 'adb' QEMUD service
- *   is opened, and it becomes clear whether or not emulator supports that
- *   protocol.
- * - Wait for the ADB host to create connection with the guest. This is done by
- *   sending an 'accept' request to the adb QEMUD service, and waiting on
- *   response.
- * - When new ADB host connection is accepted, the connection with adb QEMUD
- *   service is registered as the transport, and a 'start' request is sent to the
- *   adb QEMUD service, indicating that the guest is ready to receive messages.
- *   Note that the guest will ignore messages sent down from the emulator before
- *   the transport registration is completed. That's why we need to send the
- *   'start' request after the transport is registered.
- */
-static void qemu_socket_thread(int port) {
-    /* 'accept' request to the adb QEMUD service. */
-    static const char _accept_req[] = "accept";
-    /* 'start' request to the adb QEMUD service. */
-    static const char _start_req[] = "start";
-    /* 'ok' reply from the adb QEMUD service. */
-    static const char _ok_resp[] = "ok";
-
-    int fd;
-    char tmp[256];
-    char con_name[32];
-
-    adb_thread_setname("qemu socket");
-    D("transport: qemu_socket_thread() starting");
-
-    /* adb QEMUD service connection request. */
-    snprintf(con_name, sizeof(con_name), "pipe:qemud:adb:%d", port);
-
-    /* Connect to the adb QEMUD service. */
-    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. */
-        D("adb service is not available. Falling back to TCP socket.");
-        std::thread(server_socket_thread, port).detach();
-        return;
-    }
-
-    for(;;) {
-        /*
-         * Wait till the host creates a new connection.
-         */
-
-        /* Send the 'accept' request. */
-        if (WriteFdExactly(fd, _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)) {
-                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);
-                }
-            }
-
-            /* Prepare for accepting of the next ADB host connection. */
-            fd = qemu_pipe_open(con_name);
-            if (fd < 0) {
-                D("adb service become unavailable.");
-                return;
-            }
-        } else {
-            D("Unable to send the '%s' request to ADB service.", _accept_req);
-            return;
-        }
-    }
-    D("transport: qemu_socket_thread() exiting");
-    return;
+unique_fd tcp_listen_inaddr_any(int port, std::string* error) {
+    return unique_fd{network_inaddr_any_server(port, SOCK_STREAM, error)};
 }
 
-// If adbd is running inside the emulator, it will normally use QEMUD pipe (aka
-// goldfish) as the transport. This can either be explicitly set by the
-// service.adb.transport property, or be inferred from ro.kernel.qemu that is
-// set to "1" for ranchu/goldfish.
-static bool use_qemu_goldfish() {
-    // Legacy way to detect if adbd should use the goldfish pipe is to check for
-    // ro.kernel.qemu, keep that behaviour for backward compatibility.
-    if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
-        return true;
-    }
-    // If service.adb.transport is present and is set to "goldfish", use the
-    // QEMUD pipe.
-    if (android::base::GetProperty("service.adb.transport", "") == "goldfish") {
-        return true;
-    }
-    return false;
+#if !ADB_HOST
+static unique_fd vsock_listen(int port, std::string* error) {
+    return unique_fd{
+        socket_spec_listen(android::base::StringPrintf("vsock:%d", port), error, nullptr)
+    };
 }
+#endif
 
-#endif  // !ADB_HOST
-
-void local_init(int port)
-{
-    void (*func)(int);
-    const char* debug_name = "";
-
+void local_init(int port) {
 #if ADB_HOST
-    func = client_socket_thread;
-    debug_name = "client";
+    D("transport: local client init");
+    std::thread(client_socket_thread, port).detach();
+    adb_local_transport_max_port_env_override();
+#elif !defined(__ANDROID__)
+    // Host adbd.
+    D("transport: local server init");
+    std::thread(server_socket_thread, tcp_listen_inaddr_any, port).detach();
+    std::thread(server_socket_thread, vsock_listen, port).detach();
 #else
+    D("transport: local server init");
     // For the adbd daemon in the system image we need to distinguish
     // between the device, and the emulator.
-    func = use_qemu_goldfish() ? qemu_socket_thread : server_socket_thread;
-    debug_name = "server";
+    if (use_qemu_goldfish()) {
+        std::thread(qemu_socket_thread, port).detach();
+    } else {
+        std::thread(server_socket_thread, tcp_listen_inaddr_any, port).detach();
+    }
+    std::thread(server_socket_thread, vsock_listen, port).detach();
 #endif // !ADB_HOST
-
-    D("transport: local %s init", debug_name);
-    std::thread(func, port).detach();
 }
 
 #if ADB_HOST
@@ -427,10 +354,6 @@
     return it->second;
 }
 
-std::string getEmulatorSerialString(int console_port) {
-    return android::base::StringPrintf("emulator-%d", console_port);
-}
-
 atransport* find_emulator_transport_by_adb_port(int adb_port) {
     std::lock_guard<std::mutex> lock(local_transports_lock);
     return find_emulator_transport_by_adb_port_locked(adb_port);
@@ -441,26 +364,26 @@
 }
 #endif
 
-int init_socket_transport(atransport* t, int s, int adb_port, int local) {
+std::string getEmulatorSerialString(int console_port) {
+    return android::base::StringPrintf("emulator-%d", console_port);
+}
+
+int init_socket_transport(atransport* t, unique_fd fd, int adb_port, int local) {
     int fail = 0;
 
-    unique_fd fd(s);
-    t->sync_token = 1;
     t->type = kTransportLocal;
 
 #if ADB_HOST
     // Emulator connection.
     if (local) {
-        t->connection.reset(new EmulatorConnection(std::move(fd), adb_port));
+        auto emulator_connection = std::make_unique<EmulatorConnection>(std::move(fd), adb_port);
+        t->SetConnection(
+            std::make_unique<BlockingConnectionAdapter>(std::move(emulator_connection)));
         std::lock_guard<std::mutex> lock(local_transports_lock);
         atransport* existing_transport = find_emulator_transport_by_adb_port_locked(adb_port);
-        if (existing_transport != NULL) {
+        if (existing_transport != nullptr) {
             D("local transport for port %d already registered (%p)?", adb_port, existing_transport);
             fail = -1;
-        } 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[adb_port] = t;
         }
@@ -470,6 +393,7 @@
 #endif
 
     // Regular tcp connection.
-    t->connection.reset(new FdConnection(std::move(fd)));
+    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 d987d4f..b66f8fa 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -19,13 +19,16 @@
 #include <gtest/gtest.h>
 
 #include "adb.h"
+#include "fdevent_test.h"
+
+struct TransportTest : public FdeventTest {};
 
 static void DisconnectFunc(void* arg, atransport*) {
     int* count = reinterpret_cast<int*>(arg);
     ++*count;
 }
 
-TEST(transport, RunDisconnects) {
+TEST_F(TransportTest, RunDisconnects) {
     atransport t;
     // RunDisconnects() can be called with an empty atransport.
     t.RunDisconnects();
@@ -49,7 +52,7 @@
     ASSERT_EQ(0, count);
 }
 
-TEST(transport, SetFeatures) {
+TEST_F(TransportTest, SetFeatures) {
     atransport t;
     ASSERT_EQ(0U, t.features().size());
 
@@ -77,8 +80,7 @@
     ASSERT_EQ(0U, t.features().size());
 }
 
-TEST(transport, parse_banner_no_features) {
-    set_main_thread();
+TEST_F(TransportTest, parse_banner_no_features) {
     atransport t;
 
     parse_banner("host::", &t);
@@ -86,12 +88,12 @@
     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) {
+TEST_F(TransportTest, parse_banner_product_features) {
     atransport t;
 
     const char banner[] =
@@ -107,9 +109,8 @@
     ASSERT_EQ(std::string("baz"), t.device);
 }
 
-TEST(transport, parse_banner_features) {
+TEST_F(TransportTest, parse_banner_features) {
     atransport t;
-
     const char banner[] =
         "host::ro.product.name=foo;ro.product.model=bar;ro.product.device=baz;"
         "features=woodly,doodly";
@@ -126,7 +127,7 @@
     ASSERT_EQ(std::string("baz"), t.device);
 }
 
-TEST(transport, test_matches_target) {
+TEST_F(TransportTest, test_matches_target) {
     std::string serial = "foo";
     std::string devpath = "/path/to/bar";
     std::string product = "test_product";
@@ -157,7 +158,7 @@
     }
 }
 
-TEST(transport, test_matches_target_local) {
+TEST_F(TransportTest, test_matches_target_local) {
     std::string serial = "100.100.100.100:5555";
 
     atransport t;
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index d7565f6..3e87522 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -16,6 +16,8 @@
 
 #define TRACE_TAG TRANSPORT
 
+#include <memory>
+
 #include "sysdeps.h"
 #include "transport.h"
 
@@ -121,7 +123,7 @@
 // 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, usb_handle* usb) {
-    if (usb_read(usb, &p->msg, sizeof(amessage))) {
+    if (usb_read(usb, &p->msg, sizeof(amessage)) != sizeof(amessage)) {
         PLOG(ERROR) << "remote usb: read terminated (message)";
         return -1;
     }
@@ -133,7 +135,8 @@
         }
 
         p->payload.resize(p->msg.data_length);
-        if (usb_read(usb, &p->payload[0], p->payload.size())) {
+        if (usb_read(usb, &p->payload[0], p->payload.size())
+                != static_cast<int>(p->payload.size())) {
             PLOG(ERROR) << "remote usb: terminated (data)";
             return -1;
         }
@@ -153,14 +156,14 @@
 }
 
 bool UsbConnection::Write(apacket* packet) {
-    unsigned size = packet->msg.data_length;
+    int size = packet->msg.data_length;
 
-    if (usb_write(handle_, &packet->msg, sizeof(packet->msg)) != 0) {
+    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) != 0) {
+    if (packet->msg.data_length != 0 && usb_write(handle_, packet->payload.data(), size) != size) {
         PLOG(ERROR) << "remote usb: 2 - write terminated";
         return false;
     }
@@ -168,15 +171,21 @@
     return true;
 }
 
+void UsbConnection::Reset() {
+    usb_reset(handle_);
+    usb_kick(handle_);
+}
+
 void UsbConnection::Close() {
     usb_kick(handle_);
 }
 
 void init_usb_transport(atransport* t, usb_handle* h) {
     D("transport: usb");
-    t->connection.reset(new UsbConnection(h));
-    t->sync_token = 1;
+    auto connection = std::make_unique<UsbConnection>(h);
+    t->SetConnection(std::make_unique<BlockingConnectionAdapter>(std::move(connection)));
     t->type = kTransportUsb;
+    t->SetUsbHandle(h);
 }
 
 int is_adb_interface(int usb_class, int usb_subclass, int usb_protocol) {
diff --git a/adb/types.h b/adb/types.h
new file mode 100644
index 0000000..cd1366d
--- /dev/null
+++ b/adb/types.h
@@ -0,0 +1,342 @@
+/*
+ * 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/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) noexcept {
+        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) noexcept {
+        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) {
+            // This isn't std::make_unique because that's equivalent to `new char[size]()`, which
+            // value-initializes the array instead of leaving it uninitialized. As an optimization,
+            // call new without parentheses to avoid this costly initialization.
+            data_.reset(new 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) noexcept : IOVector() { *this = std::move(move); }
+
+    IOVector& operator=(const IOVector& copy) = delete;
+    IOVector& operator=(IOVector&& move) noexcept {
+        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) {
+        if (block->size() == 0) {
+            return;
+        }
+
+        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..1fbd2ca
--- /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 <memory>
+#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 cd83c42..eb8ca6c 100644
--- a/adb/usb.h
+++ b/adb/usb.h
@@ -26,6 +26,7 @@
     int usb_write(handle_ref_type h, const void* data, int len); \
     int usb_read(handle_ref_type h, void* data, int len);        \
     int usb_close(handle_ref_type h);                            \
+    void usb_reset(handle_ref_type h);                           \
     void usb_kick(handle_ref_type h);                            \
     size_t usb_get_max_packet_size(handle_ref_type)
 
diff --git a/adf/libadf/Android.bp b/adf/libadf/Android.bp
deleted file mode 100644
index 8eef2ea..0000000
--- a/adf/libadf/Android.bp
+++ /dev/null
@@ -1,27 +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.
-
-cc_library {
-    name: "libadf",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
-    srcs: ["adf.cpp"],
-    cflags: ["-Werror"],
-    local_include_dirs: ["include"],
-    export_include_dirs: ["include"],
-}
-
-subdirs = ["tests"]
diff --git a/base/Android.bp b/base/Android.bp
index 5d70d47..357ce01 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -20,13 +20,16 @@
         "-Wall",
         "-Werror",
         "-Wextra",
+        "-D_FILE_OFFSET_BITS=64",
     ],
 }
 
 cc_library_headers {
     name: "libbase_headers",
     vendor_available: true,
+    recovery_available: true,
     host_supported: true,
+    native_bridge_supported: true,
     export_include_dirs: ["include"],
 
     target: {
@@ -43,22 +46,26 @@
     name: "libbase_defaults",
     defaults: ["libbase_cflags_defaults"],
     srcs: [
+        "abi_compatibility.cpp",
         "chrono_utils.cpp",
+        "cmsg.cpp",
         "file.cpp",
         "logging.cpp",
+        "mapped_file.cpp",
         "parsenetaddress.cpp",
+        "process.cpp",
+        "properties.cpp",
         "quick_exit.cpp",
         "stringprintf.cpp",
         "strings.cpp",
+        "threads.cpp",
         "test_utils.cpp",
     ],
 
+    cppflags: ["-Wexit-time-destructors"],
     shared_libs: ["liblog"],
     target: {
         android: {
-            srcs: [
-                "properties.cpp",
-            ],
             sanitize: {
                 misc_undefined: ["integer"],
             },
@@ -68,13 +75,11 @@
             srcs: [
                 "errors_unix.cpp",
             ],
-            cppflags: ["-Wexit-time-destructors"],
         },
         darwin: {
             srcs: [
                 "errors_unix.cpp",
             ],
-            cppflags: ["-Wexit-time-destructors"],
         },
         linux_bionic: {
             enabled: true,
@@ -84,6 +89,9 @@
                 "errors_windows.cpp",
                 "utf8.cpp",
             ],
+            exclude_srcs: [
+                "cmsg.cpp",
+            ],
             enabled: true,
         },
     },
@@ -93,7 +101,9 @@
     name: "libbase",
     defaults: ["libbase_defaults"],
     vendor_available: true,
+    recovery_available: true,
     host_supported: true,
+    native_bridge_supported: true,
     vndk: {
         enabled: true,
         support_system_process: true,
@@ -102,6 +112,9 @@
         "libbase_headers",
     ],
     export_header_lib_headers: ["libbase_headers"],
+    static_libs: ["fmtlib"],
+    whole_static_libs: ["fmtlib"],
+    export_static_lib_headers: ["fmtlib"],
 }
 
 cc_library_static {
@@ -110,6 +123,9 @@
     sdk_version: "current",
     stl: "c++_static",
     export_include_dirs: ["include"],
+    static_libs: ["fmtlib_ndk"],
+    whole_static_libs: ["fmtlib_ndk"],
+    export_static_lib_headers: ["fmtlib_ndk"],
 }
 
 // Tests
@@ -119,14 +135,21 @@
     defaults: ["libbase_cflags_defaults"],
     host_supported: true,
     srcs: [
+        "cmsg_test.cpp",
         "endian_test.cpp",
         "errors_test.cpp",
+        "expected_test.cpp",
         "file_test.cpp",
         "logging_test.cpp",
+        "macros_test.cpp",
+        "mapped_file_test.cpp",
         "parsedouble_test.cpp",
         "parseint_test.cpp",
         "parsenetaddress_test.cpp",
+        "process_test.cpp",
+        "properties_test.cpp",
         "quick_exit_test.cpp",
+        "result_test.cpp",
         "scopeguard_test.cpp",
         "stringprintf_test.cpp",
         "strings_test.cpp",
@@ -135,7 +158,6 @@
     ],
     target: {
         android: {
-            srcs: ["properties_test.cpp"],
             sanitize: {
                 misc_undefined: ["integer"],
             },
@@ -160,4 +182,23 @@
             suffix: "64",
         },
     },
+    test_suites: ["device-tests"],
+}
+
+cc_benchmark {
+    name: "libbase_benchmark",
+    defaults: ["libbase_cflags_defaults"],
+
+    srcs: ["format_benchmark.cpp"],
+    shared_libs: ["libbase"],
+
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
 }
diff --git a/base/abi_compatibility.cpp b/base/abi_compatibility.cpp
new file mode 100644
index 0000000..06a7801
--- /dev/null
+++ b/base/abi_compatibility.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "android-base/cmsg.h"
+#include "android-base/file.h"
+#include "android-base/mapped_file.h"
+#include "android-base/unique_fd.h"
+
+namespace android {
+namespace base {
+
+// These ABI-compatibility shims are in a separate file for two reasons:
+//   1. If they were in the file with the actual functions, it prevents calls to
+//      those functions by other functions in the file, due to ambiguity.
+//   2. We will hopefully be able to delete these quickly.
+
+#if !defined(_WIN32)
+ssize_t SendFileDescriptorVector(int sockfd, const void* data, size_t len,
+                                 const std::vector<int>& fds) {
+  return SendFileDescriptorVector(borrowed_fd(sockfd), data, len, fds);
+}
+
+ssize_t ReceiveFileDescriptorVector(int sockfd, void* data, size_t len, size_t max_fds,
+                                    std::vector<unique_fd>* fds) {
+  return ReceiveFileDescriptorVector(borrowed_fd(sockfd), data, len, max_fds, fds);
+}
+#endif
+
+bool ReadFdToString(int fd, std::string* content) {
+  return ReadFdToString(borrowed_fd(fd), content);
+}
+
+bool WriteStringToFd(const std::string& content, int fd) {
+  return WriteStringToFd(content, borrowed_fd(fd));
+}
+
+bool ReadFully(int fd, void* data, size_t byte_count) {
+  return ReadFully(borrowed_fd(fd), data, byte_count);
+}
+
+bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset) {
+  return ReadFullyAtOffset(borrowed_fd(fd), data, byte_count, offset);
+}
+
+bool WriteFully(int fd, const void* data, size_t byte_count) {
+  return WriteFully(borrowed_fd(fd), data, byte_count);
+}
+
+#if defined(__LP64__)
+#define MAPPEDFILE_FROMFD _ZN10MappedFile6FromFdEilmi
+#else
+#define MAPPEDFILE_FROMFD _ZN10MappedFile6FromFdEixmi
+#endif
+
+#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
+extern "C" std::unique_ptr<MappedFile> MAPPEDFILE_FROMFD(int fd, off64_t offset, size_t length,
+                                                         int prot) {
+  return MappedFile::FromFd(fd, offset, length, prot);
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/cmsg.cpp b/base/cmsg.cpp
new file mode 100644
index 0000000..1fa873c
--- /dev/null
+++ b/base/cmsg.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/cmsg.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/user.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace base {
+
+ssize_t SendFileDescriptorVector(borrowed_fd sockfd, const void* data, size_t len,
+                                 const std::vector<int>& fds) {
+  size_t cmsg_space = CMSG_SPACE(sizeof(int) * fds.size());
+  size_t cmsg_len = CMSG_LEN(sizeof(int) * fds.size());
+  if (cmsg_space >= PAGE_SIZE) {
+    errno = ENOMEM;
+    return -1;
+  }
+
+  alignas(struct cmsghdr) char cmsg_buf[cmsg_space];
+  iovec iov = {.iov_base = const_cast<void*>(data), .iov_len = len};
+  msghdr msg = {
+      .msg_name = nullptr,
+      .msg_namelen = 0,
+      .msg_iov = &iov,
+      .msg_iovlen = 1,
+      .msg_control = cmsg_buf,
+      // We can't cast to the actual type of the field, because it's different across platforms.
+      .msg_controllen = static_cast<unsigned int>(cmsg_space),
+      .msg_flags = 0,
+  };
+
+  struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  cmsg->cmsg_len = cmsg_len;
+
+  int* cmsg_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+  for (size_t i = 0; i < fds.size(); ++i) {
+    cmsg_fds[i] = fds[i];
+  }
+
+#if defined(__linux__)
+  int flags = MSG_NOSIGNAL;
+#else
+  int flags = 0;
+#endif
+
+  return TEMP_FAILURE_RETRY(sendmsg(sockfd.get(), &msg, flags));
+}
+
+ssize_t ReceiveFileDescriptorVector(borrowed_fd sockfd, void* data, size_t len, size_t max_fds,
+                                    std::vector<unique_fd>* fds) {
+  fds->clear();
+
+  size_t cmsg_space = CMSG_SPACE(sizeof(int) * max_fds);
+  if (cmsg_space >= PAGE_SIZE) {
+    errno = ENOMEM;
+    return -1;
+  }
+
+  alignas(struct cmsghdr) char cmsg_buf[cmsg_space];
+  iovec iov = {.iov_base = const_cast<void*>(data), .iov_len = len};
+  msghdr msg = {
+      .msg_name = nullptr,
+      .msg_namelen = 0,
+      .msg_iov = &iov,
+      .msg_iovlen = 1,
+      .msg_control = cmsg_buf,
+      // We can't cast to the actual type of the field, because it's different across platforms.
+      .msg_controllen = static_cast<unsigned int>(cmsg_space),
+      .msg_flags = 0,
+  };
+
+  int flags = MSG_TRUNC | MSG_CTRUNC;
+#if defined(__linux__)
+  flags |= MSG_CMSG_CLOEXEC | MSG_NOSIGNAL;
+#endif
+
+  ssize_t rc = TEMP_FAILURE_RETRY(recvmsg(sockfd.get(), &msg, flags));
+
+  if (rc == -1) {
+    return -1;
+  }
+
+  int error = 0;
+  if ((msg.msg_flags & MSG_TRUNC)) {
+    LOG(ERROR) << "message was truncated when receiving file descriptors";
+    error = EMSGSIZE;
+  } else if ((msg.msg_flags & MSG_CTRUNC)) {
+    LOG(ERROR) << "control message was truncated when receiving file descriptors";
+    error = EMSGSIZE;
+  }
+
+  std::vector<unique_fd> received_fds;
+  struct cmsghdr* cmsg;
+  for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+    if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
+      LOG(ERROR) << "received unexpected cmsg: [" << cmsg->cmsg_level << ", " << cmsg->cmsg_type
+                 << "]";
+      error = EBADMSG;
+      continue;
+    }
+
+    // There isn't a macro that does the inverse of CMSG_LEN, so hack around it ourselves, with
+    // some asserts to ensure that CMSG_LEN behaves as we expect.
+#if defined(__linux__)
+#define CMSG_ASSERT static_assert
+#else
+// CMSG_LEN is somehow not constexpr on darwin.
+#define CMSG_ASSERT CHECK
+#endif
+    CMSG_ASSERT(CMSG_LEN(0) + 1 * sizeof(int) == CMSG_LEN(1 * sizeof(int)));
+    CMSG_ASSERT(CMSG_LEN(0) + 2 * sizeof(int) == CMSG_LEN(2 * sizeof(int)));
+    CMSG_ASSERT(CMSG_LEN(0) + 3 * sizeof(int) == CMSG_LEN(3 * sizeof(int)));
+    CMSG_ASSERT(CMSG_LEN(0) + 4 * sizeof(int) == CMSG_LEN(4 * sizeof(int)));
+
+    if (cmsg->cmsg_len % sizeof(int) != 0) {
+      LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not aligned to sizeof(int)";
+    } else if (cmsg->cmsg_len <= CMSG_LEN(0)) {
+      LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not long enough to hold any data";
+    }
+
+    int* cmsg_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+    size_t cmsg_fdcount = static_cast<size_t>(cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+    for (size_t i = 0; i < cmsg_fdcount; ++i) {
+#if !defined(__linux__)
+      // Linux uses MSG_CMSG_CLOEXEC instead of doing this manually.
+      fcntl(cmsg_fds[i], F_SETFD, FD_CLOEXEC);
+#endif
+      received_fds.emplace_back(cmsg_fds[i]);
+    }
+  }
+
+  if (error != 0) {
+    errno = error;
+    return -1;
+  }
+
+  if (received_fds.size() > max_fds) {
+    LOG(ERROR) << "received too many file descriptors, expected " << fds->size() << ", received "
+               << received_fds.size();
+    errno = EMSGSIZE;
+    return -1;
+  }
+
+  *fds = std::move(received_fds);
+  return rc;
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/cmsg_test.cpp b/base/cmsg_test.cpp
new file mode 100644
index 0000000..9ee5c82
--- /dev/null
+++ b/base/cmsg_test.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/cmsg.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+#if !defined(_WIN32)
+
+using android::base::ReceiveFileDescriptors;
+using android::base::SendFileDescriptors;
+using android::base::unique_fd;
+
+static ino_t GetInode(int fd) {
+  struct stat st;
+  if (fstat(fd, &st) != 0) {
+    PLOG(FATAL) << "fstat failed";
+  }
+
+  return st.st_ino;
+}
+
+struct CmsgTest : ::testing::TestWithParam<bool> {
+  bool Seqpacket() { return GetParam(); }
+
+  void SetUp() override {
+    ASSERT_TRUE(
+        android::base::Socketpair(Seqpacket() ? SOCK_SEQPACKET : SOCK_STREAM, &send, &recv));
+    int dup1 = dup(tmp1.fd);
+    ASSERT_NE(-1, dup1);
+    int dup2 = dup(tmp2.fd);
+    ASSERT_NE(-1, dup2);
+
+    fd1.reset(dup1);
+    fd2.reset(dup2);
+
+    ino1 = GetInode(dup1);
+    ino2 = GetInode(dup2);
+  }
+
+  unique_fd send;
+  unique_fd recv;
+
+  TemporaryFile tmp1;
+  TemporaryFile tmp2;
+
+  unique_fd fd1;
+  unique_fd fd2;
+
+  ino_t ino1;
+  ino_t ino2;
+};
+
+TEST_P(CmsgTest, smoke) {
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "x", 1, fd1.get()));
+
+  char buf[2];
+  unique_fd received;
+  ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 2, &received));
+  ASSERT_EQ('x', buf[0]);
+  ASSERT_NE(-1, received.get());
+
+  ASSERT_EQ(ino1, GetInode(received.get()));
+}
+
+TEST_P(CmsgTest, msg_trunc) {
+  ASSERT_EQ(2, SendFileDescriptors(send.get(), "ab", 2, fd1.get(), fd2.get()));
+
+  char buf[2];
+  unique_fd received1, received2;
+
+  ssize_t rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2);
+  if (Seqpacket()) {
+    ASSERT_EQ(-1, rc);
+    ASSERT_EQ(EMSGSIZE, errno);
+    ASSERT_EQ(-1, received1.get());
+    ASSERT_EQ(-1, received2.get());
+  } else {
+    ASSERT_EQ(1, rc);
+    ASSERT_NE(-1, received1.get());
+    ASSERT_NE(-1, received2.get());
+    ASSERT_EQ(ino1, GetInode(received1.get()));
+    ASSERT_EQ(ino2, GetInode(received2.get()));
+    ASSERT_EQ(1, read(recv.get(), buf, 2));
+  }
+}
+
+TEST_P(CmsgTest, msg_ctrunc) {
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
+
+  char buf[2];
+  unique_fd received;
+  ASSERT_EQ(-1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
+  ASSERT_EQ(EMSGSIZE, errno);
+  ASSERT_EQ(-1, received.get());
+}
+
+TEST_P(CmsgTest, peek) {
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
+
+  char buf[2];
+  ASSERT_EQ(1, ::recv(recv.get(), buf, sizeof(buf), MSG_PEEK));
+  ASSERT_EQ('a', buf[0]);
+
+  unique_fd received;
+  ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
+  ASSERT_EQ(ino1, GetInode(received.get()));
+}
+
+TEST_P(CmsgTest, stream_fd_association) {
+  if (Seqpacket()) {
+    return;
+  }
+
+  // fds are associated with the first byte of the write.
+  ASSERT_EQ(1, TEMP_FAILURE_RETRY(write(send.get(), "a", 1)));
+  ASSERT_EQ(2, SendFileDescriptors(send.get(), "bc", 2, fd1.get()));
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "d", 1, fd2.get()));
+  char buf[2];
+  ASSERT_EQ(2, TEMP_FAILURE_RETRY(read(recv.get(), buf, 2)));
+  ASSERT_EQ(0, memcmp(buf, "ab", 2));
+
+  std::vector<unique_fd> received1;
+  ssize_t rc = ReceiveFileDescriptorVector(recv.get(), buf, 1, 1, &received1);
+  ASSERT_EQ(1, rc);
+  ASSERT_EQ('c', buf[0]);
+  ASSERT_TRUE(received1.empty());
+
+  unique_fd received2;
+  rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received2);
+  ASSERT_EQ(1, rc);
+  ASSERT_EQ('d', buf[0]);
+  ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, multiple_fd_ordering) {
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
+
+  char buf[2];
+  unique_fd received1, received2;
+  ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2));
+
+  ASSERT_NE(-1, received1.get());
+  ASSERT_NE(-1, received2.get());
+
+  ASSERT_EQ(ino1, GetInode(received1.get()));
+  ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, separate_fd_ordering) {
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "b", 1, fd2.get()));
+
+  char buf[2];
+  unique_fd received1, received2;
+  ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1));
+  ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received2));
+
+  ASSERT_NE(-1, received1.get());
+  ASSERT_NE(-1, received2.get());
+
+  ASSERT_EQ(ino1, GetInode(received1.get()));
+  ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, separate_fds_no_coalescing) {
+  unique_fd sent1(dup(tmp1.fd));
+  unique_fd sent2(dup(tmp2.fd));
+
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd1.get()));
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd2.get()));
+
+  char buf[2];
+  std::vector<unique_fd> received;
+  ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
+  ASSERT_EQ(1U, received.size());
+  ASSERT_EQ(ino1, GetInode(received[0].get()));
+
+  ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
+  ASSERT_EQ(1U, received.size());
+  ASSERT_EQ(ino2, GetInode(received[0].get()));
+}
+
+INSTANTIATE_TEST_CASE_P(CmsgTest, CmsgTest, testing::Bool());
+
+#endif
diff --git a/base/expected_test.cpp b/base/expected_test.cpp
new file mode 100644
index 0000000..a74bc1d
--- /dev/null
+++ b/base/expected_test.cpp
@@ -0,0 +1,894 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/expected.h"
+
+#include <cstdio>
+#include <memory>
+#include <string>
+
+#include <gtest/gtest.h>
+
+using android::base::expected;
+using android::base::unexpected;
+
+typedef expected<int, int> exp_int;
+typedef expected<double, double> exp_double;
+typedef expected<std::string, std::string> exp_string;
+typedef expected<std::pair<std::string, int>, int> exp_pair;
+typedef expected<void, int> exp_void;
+
+struct T {
+  int a;
+  int b;
+  T() = default;
+  T(int a, int b) noexcept : a(a), b(b) {}
+};
+bool operator==(const T& x, const T& y) {
+  return x.a == y.a && x.b == y.b;
+}
+bool operator!=(const T& x, const T& y) {
+  return x.a != y.a || x.b != y.b;
+}
+
+struct E {
+    std::string message;
+    int cause;
+    E(const std::string& message, int cause) : message(message), cause(cause) {}
+};
+
+typedef expected<T,E> exp_complex;
+
+TEST(Expected, testDefaultConstructible) {
+  exp_int e;
+  EXPECT_TRUE(e.has_value());
+  EXPECT_EQ(0, e.value());
+
+  exp_complex e2;
+  EXPECT_TRUE(e2.has_value());
+  EXPECT_EQ(T(0,0), e2.value());
+
+  exp_void e3;
+  EXPECT_TRUE(e3.has_value());
+}
+
+TEST(Expected, testCopyConstructible) {
+  exp_int e;
+  exp_int e2 = e;
+
+  EXPECT_TRUE(e.has_value());
+  EXPECT_TRUE(e2.has_value());
+  EXPECT_EQ(0, e.value());
+  EXPECT_EQ(0, e2.value());
+
+  exp_void e3;
+  exp_void e4 = e3;
+  EXPECT_TRUE(e3.has_value());
+  EXPECT_TRUE(e4.has_value());
+}
+
+TEST(Expected, testMoveConstructible) {
+  exp_int e;
+  exp_int e2 = std::move(e);
+
+  EXPECT_TRUE(e.has_value());
+  EXPECT_TRUE(e2.has_value());
+  EXPECT_EQ(0, e.value());
+  EXPECT_EQ(0, e2.value());
+
+  exp_string e3(std::string("hello"));
+  exp_string e4 = std::move(e3);
+
+  EXPECT_TRUE(e3.has_value());
+  EXPECT_TRUE(e4.has_value());
+  EXPECT_EQ("", e3.value()); // e3 is moved
+  EXPECT_EQ("hello", e4.value());
+
+  exp_void e5;
+  exp_void e6 = std::move(e5);
+  EXPECT_TRUE(e5.has_value());
+  EXPECT_TRUE(e6.has_value());
+}
+
+TEST(Expected, testCopyConstructibleFromConvertibleType) {
+  exp_double e = 3.3f;
+  exp_int e2 = e;
+
+  EXPECT_TRUE(e.has_value());
+  EXPECT_TRUE(e2.has_value());
+  EXPECT_EQ(3.3f, e.value());
+  EXPECT_EQ(3, e2.value());
+}
+
+TEST(Expected, testMoveConstructibleFromConvertibleType) {
+  exp_double e = 3.3f;
+  exp_int e2 = std::move(e);
+
+  EXPECT_TRUE(e.has_value());
+  EXPECT_TRUE(e2.has_value());
+  EXPECT_EQ(3.3f, e.value());
+  EXPECT_EQ(3, e2.value());
+}
+
+TEST(Expected, testConstructibleFromValue) {
+  exp_int e = 3;
+  exp_double e2 = 5.5f;
+  exp_string e3 = std::string("hello");
+  exp_complex e4 = T(10, 20);
+  exp_void e5 = {};
+
+  EXPECT_TRUE(e.has_value());
+  EXPECT_TRUE(e2.has_value());
+  EXPECT_TRUE(e3.has_value());
+  EXPECT_TRUE(e4.has_value());
+  EXPECT_TRUE(e5.has_value());
+  EXPECT_EQ(3, e.value());
+  EXPECT_EQ(5.5f, e2.value());
+  EXPECT_EQ("hello", e3.value());
+  EXPECT_EQ(T(10,20), e4.value());
+}
+
+TEST(Expected, testConstructibleFromMovedValue) {
+  std::string hello = "hello";
+  exp_string e = std::move(hello);
+
+  EXPECT_TRUE(e.has_value());
+  EXPECT_EQ("hello", e.value());
+  EXPECT_EQ("", hello);
+}
+
+TEST(Expected, testConstructibleFromConvertibleValue) {
+  exp_int e = 3.3f; // double to int
+  exp_string e2 = "hello"; // char* to std::string
+  EXPECT_TRUE(e.has_value());
+  EXPECT_EQ(3, e.value());
+
+  EXPECT_TRUE(e2.has_value());
+  EXPECT_EQ("hello", e2.value());
+}
+
+TEST(Expected, testConstructibleFromUnexpected) {
+  exp_int::unexpected_type unexp = unexpected(10);
+  exp_int e = unexp;
+
+  exp_double::unexpected_type unexp2 = unexpected(10.5f);
+  exp_double e2 = unexp2;
+
+  exp_string::unexpected_type unexp3 = unexpected(std::string("error"));
+  exp_string e3 = unexp3;
+
+  exp_void::unexpected_type unexp4 = unexpected(10);
+  exp_void e4 = unexp4;
+
+  EXPECT_FALSE(e.has_value());
+  EXPECT_FALSE(e2.has_value());
+  EXPECT_FALSE(e3.has_value());
+  EXPECT_FALSE(e4.has_value());
+  EXPECT_EQ(10, e.error());
+  EXPECT_EQ(10.5f, e2.error());
+  EXPECT_EQ("error", e3.error());
+  EXPECT_EQ(10, e4.error());
+}
+
+TEST(Expected, testMoveConstructibleFromUnexpected) {
+  exp_int e = unexpected(10);
+  exp_double e2 = unexpected(10.5f);
+  exp_string e3 = unexpected(std::string("error"));
+  exp_void e4 = unexpected(10);
+
+  EXPECT_FALSE(e.has_value());
+  EXPECT_FALSE(e2.has_value());
+  EXPECT_FALSE(e3.has_value());
+  EXPECT_FALSE(e4.has_value());
+  EXPECT_EQ(10, e.error());
+  EXPECT_EQ(10.5f, e2.error());
+  EXPECT_EQ("error", e3.error());
+  EXPECT_EQ(10, e4.error());
+}
+
+TEST(Expected, testConstructibleByForwarding) {
+  exp_string e(std::in_place, 5, 'a');
+  EXPECT_TRUE(e.has_value());
+  EXPECT_EQ("aaaaa", e.value());
+
+  exp_string e2({'a', 'b', 'c'});
+  EXPECT_TRUE(e2.has_value());
+  EXPECT_EQ("abc", e2.value());
+
+  exp_pair e3({"hello", 30});
+  EXPECT_TRUE(e3.has_value());
+  EXPECT_EQ("hello",e3->first);
+  EXPECT_EQ(30,e3->second);
+
+  exp_void e4({});
+  EXPECT_TRUE(e4.has_value());
+}
+
+TEST(Expected, testDestructible) {
+  bool destroyed = false;
+  struct T {
+    bool* flag_;
+    T(bool* flag) : flag_(flag) {}
+    ~T() { *flag_ = true; }
+  };
+  {
+    expected<T, int> exp = T(&destroyed);
+  }
+  EXPECT_TRUE(destroyed);
+}
+
+TEST(Expected, testAssignable) {
+  exp_int e = 10;
+  exp_int e2 = 20;
+  e = e2;
+
+  EXPECT_EQ(20, e.value());
+  EXPECT_EQ(20, e2.value());
+
+  exp_int e3 = 10;
+  exp_int e4 = 20;
+  e3 = std::move(e4);
+
+  EXPECT_EQ(20, e3.value());
+  EXPECT_EQ(20, e4.value());
+
+  exp_void e5 = unexpected(10);
+  ASSERT_FALSE(e5.has_value());
+  exp_void e6;
+  e5 = e6;
+
+  EXPECT_TRUE(e5.has_value());
+  EXPECT_TRUE(e6.has_value());
+}
+
+TEST(Expected, testAssignableFromValue) {
+  exp_int e = 10;
+  e = 20;
+  EXPECT_EQ(20, e.value());
+
+  exp_double e2 = 3.5f;
+  e2 = 10.5f;
+  EXPECT_EQ(10.5f, e2.value());
+
+  exp_string e3 = "hello";
+  e3 = "world";
+  EXPECT_EQ("world", e3.value());
+
+  exp_void e4 = unexpected(10);
+  ASSERT_FALSE(e4.has_value());
+  e4 = {};
+  EXPECT_TRUE(e4.has_value());
+}
+
+TEST(Expected, testAssignableFromUnexpected) {
+  exp_int e = 10;
+  e = unexpected(30);
+  EXPECT_FALSE(e.has_value());
+  EXPECT_EQ(30, e.error());
+
+  exp_double e2 = 3.5f;
+  e2 = unexpected(10.5f);
+  EXPECT_FALSE(e2.has_value());
+  EXPECT_EQ(10.5f, e2.error());
+
+  exp_string e3 = "hello";
+  e3 = unexpected("world");
+  EXPECT_FALSE(e3.has_value());
+  EXPECT_EQ("world", e3.error());
+
+  exp_void e4 = {};
+  e4 = unexpected(10);
+  EXPECT_FALSE(e4.has_value());
+  EXPECT_EQ(10, e4.error());
+}
+
+TEST(Expected, testAssignableFromMovedValue) {
+  std::string world = "world";
+  exp_string e = "hello";
+  e = std::move(world);
+
+  EXPECT_TRUE(e.has_value());
+  EXPECT_EQ("world", e.value());
+  EXPECT_EQ("", world);
+}
+
+TEST(Expected, testAssignableFromMovedUnexpected) {
+  std::string world = "world";
+  exp_string e = "hello";
+  e = unexpected(std::move(world));
+
+  EXPECT_FALSE(e.has_value());
+  EXPECT_EQ("world", e.error());
+  EXPECT_EQ("", world);
+}
+
+TEST(Expected, testEmplace) {
+  struct T {
+    int a;
+    double b;
+    T() {}
+    T(int a, double b) noexcept : a(a), b(b) {}
+  };
+  expected<T, int> exp;
+  T& t = exp.emplace(3, 10.5f);
+
+  EXPECT_TRUE(exp.has_value());
+  EXPECT_EQ(3, t.a);
+  EXPECT_EQ(10.5f, t.b);
+  EXPECT_EQ(3, exp.value().a);
+  EXPECT_EQ(10.5, exp.value().b);
+
+  exp_void e = unexpected(10);
+  ASSERT_FALSE(e.has_value());
+  e.emplace();
+  EXPECT_TRUE(e.has_value());
+}
+
+TEST(Expected, testSwapExpectedExpected) {
+  exp_int e = 10;
+  exp_int e2 = 20;
+  e.swap(e2);
+
+  EXPECT_TRUE(e.has_value());
+  EXPECT_TRUE(e2.has_value());
+  EXPECT_EQ(20, e.value());
+  EXPECT_EQ(10, e2.value());
+
+  exp_void e3;
+  exp_void e4;
+  e3.swap(e4);
+
+  EXPECT_TRUE(e3.has_value());
+  EXPECT_TRUE(e4.has_value());
+}
+
+TEST(Expected, testSwapUnexpectedUnexpected) {
+  exp_int e = unexpected(10);
+  exp_int e2 = unexpected(20);
+  e.swap(e2);
+  EXPECT_FALSE(e.has_value());
+  EXPECT_FALSE(e2.has_value());
+  EXPECT_EQ(20, e.error());
+  EXPECT_EQ(10, e2.error());
+
+  exp_void e3 = unexpected(10);
+  exp_void e4 = unexpected(20);
+  e3.swap(e4);
+  EXPECT_FALSE(e3.has_value());
+  EXPECT_FALSE(e4.has_value());
+  EXPECT_EQ(20, e3.error());
+  EXPECT_EQ(10, e4.error());
+}
+
+TEST(Expected, testSwapExpectedUnepected) {
+  exp_int e = 10;
+  exp_int e2 = unexpected(30);
+  e.swap(e2);
+  EXPECT_FALSE(e.has_value());
+  EXPECT_TRUE(e2.has_value());
+  EXPECT_EQ(30, e.error());
+  EXPECT_EQ(10, e2.value());
+
+  exp_void e3;
+  exp_void e4 = unexpected(10);
+  e3.swap(e4);
+  EXPECT_FALSE(e3.has_value());
+  EXPECT_TRUE(e4.has_value());
+  EXPECT_EQ(10, e3.error());
+}
+
+TEST(Expected, testDereference) {
+  struct T {
+    int a;
+    double b;
+    T() {}
+    T(int a, double b) : a(a), b(b) {}
+  };
+  expected<T, int> exp = T(3, 10.5f);
+
+  EXPECT_EQ(3, exp->a);
+  EXPECT_EQ(10.5f, exp->b);
+
+  EXPECT_EQ(3, (*exp).a);
+  EXPECT_EQ(10.5f, (*exp).b);
+}
+
+TEST(Expected, testTest) {
+  exp_int e = 10;
+  EXPECT_TRUE(e);
+  EXPECT_TRUE(e.has_value());
+
+  exp_int e2 = unexpected(10);
+  EXPECT_FALSE(e2);
+  EXPECT_FALSE(e2.has_value());
+}
+
+TEST(Expected, testGetValue) {
+  exp_int e = 10;
+  EXPECT_EQ(10, e.value());
+  EXPECT_EQ(10, e.value_or(20));
+
+  exp_int e2 = unexpected(10);
+  EXPECT_EQ(10, e2.error());
+  EXPECT_EQ(20, e2.value_or(20));
+}
+
+TEST(Expected, testSameValues) {
+  exp_int e = 10;
+  exp_int e2 = 10;
+  EXPECT_TRUE(e == e2);
+  EXPECT_TRUE(e2 == e);
+  EXPECT_FALSE(e != e2);
+  EXPECT_FALSE(e2 != e);
+
+  exp_void e3;
+  exp_void e4;
+  EXPECT_TRUE(e3 == e4);
+  EXPECT_TRUE(e4 == e3);
+  EXPECT_FALSE(e3 != e4);
+  EXPECT_FALSE(e4 != e3);
+}
+
+TEST(Expected, testDifferentValues) {
+  exp_int e = 10;
+  exp_int e2 = 20;
+  EXPECT_FALSE(e == e2);
+  EXPECT_FALSE(e2 == e);
+  EXPECT_TRUE(e != e2);
+  EXPECT_TRUE(e2 != e);
+}
+
+TEST(Expected, testValueWithError) {
+  exp_int e = 10;
+  exp_int e2 = unexpected(10);
+  EXPECT_FALSE(e == e2);
+  EXPECT_FALSE(e2 == e);
+  EXPECT_TRUE(e != e2);
+  EXPECT_TRUE(e2 != e);
+
+  exp_void e3;
+  exp_void e4 = unexpected(10);
+  EXPECT_FALSE(e3 == e4);
+  EXPECT_FALSE(e4 == e3);
+  EXPECT_TRUE(e3 != e4);
+  EXPECT_TRUE(e4 != e3);
+}
+
+TEST(Expected, testSameErrors) {
+  exp_int e = unexpected(10);
+  exp_int e2 = unexpected(10);
+  EXPECT_TRUE(e == e2);
+  EXPECT_TRUE(e2 == e);
+  EXPECT_FALSE(e != e2);
+  EXPECT_FALSE(e2 != e);
+
+  exp_void e3 = unexpected(10);
+  exp_void e4 = unexpected(10);
+  EXPECT_TRUE(e3 == e4);
+  EXPECT_TRUE(e4 == e3);
+  EXPECT_FALSE(e3 != e4);
+  EXPECT_FALSE(e4 != e3);
+}
+
+TEST(Expected, testDifferentErrors) {
+  exp_int e = unexpected(10);
+  exp_int e2 = unexpected(20);
+  EXPECT_FALSE(e == e2);
+  EXPECT_FALSE(e2 == e);
+  EXPECT_TRUE(e != e2);
+  EXPECT_TRUE(e2 != e);
+
+  exp_void e3 = unexpected(10);
+  exp_void e4 = unexpected(20);
+  EXPECT_FALSE(e3 == e4);
+  EXPECT_FALSE(e4 == e3);
+  EXPECT_TRUE(e3 != e4);
+  EXPECT_TRUE(e4 != e3);
+}
+
+TEST(Expected, testCompareWithSameValue) {
+  exp_int e = 10;
+  int value = 10;
+  EXPECT_TRUE(e == value);
+  EXPECT_TRUE(value == e);
+  EXPECT_FALSE(e != value);
+  EXPECT_FALSE(value != e);
+}
+
+TEST(Expected, testCompareWithDifferentValue) {
+  exp_int e = 10;
+  int value = 20;
+  EXPECT_FALSE(e == value);
+  EXPECT_FALSE(value == e);
+  EXPECT_TRUE(e != value);
+  EXPECT_TRUE(value != e);
+}
+
+TEST(Expected, testCompareWithSameError) {
+  exp_int e = unexpected(10);
+  exp_int::unexpected_type error = 10;
+  EXPECT_TRUE(e == error);
+  EXPECT_TRUE(error == e);
+  EXPECT_FALSE(e != error);
+  EXPECT_FALSE(error != e);
+
+  exp_void e2 = unexpected(10);
+  exp_void::unexpected_type error2 = 10;
+  EXPECT_TRUE(e2 == error2);
+  EXPECT_TRUE(error2 == e2);
+  EXPECT_FALSE(e2 != error2);
+  EXPECT_FALSE(error2 != e2);
+}
+
+TEST(Expected, testCompareWithDifferentError) {
+  exp_int e = unexpected(10);
+  exp_int::unexpected_type error = 20;
+  EXPECT_FALSE(e == error);
+  EXPECT_FALSE(error == e);
+  EXPECT_TRUE(e != error);
+  EXPECT_TRUE(error != e);
+
+  exp_void e2 = unexpected(10);
+  exp_void::unexpected_type error2 = 20;
+  EXPECT_FALSE(e2 == error2);
+  EXPECT_FALSE(error2 == e2);
+  EXPECT_TRUE(e2 != error2);
+  EXPECT_TRUE(error2 != e2);
+}
+
+TEST(Expected, testCompareDifferentType) {
+  expected<int,int> e = 10;
+  expected<int32_t, int> e2 = 10;
+  EXPECT_TRUE(e == e2);
+  e2 = 20;
+  EXPECT_FALSE(e == e2);
+
+  expected<std::string_view,int> e3 = "hello";
+  expected<std::string,int> e4 = "hello";
+  EXPECT_TRUE(e3 == e4);
+  e4 = "world";
+  EXPECT_FALSE(e3 == e4);
+
+  expected<void,int> e5;
+  expected<int,int> e6 = 10;
+  EXPECT_FALSE(e5 == e6);
+  EXPECT_FALSE(e6 == e5);
+}
+
+TEST(Expected, testDivideExample) {
+  struct QR {
+    int quotient;
+    int remainder;
+    QR(int q, int r) noexcept : quotient(q), remainder(r) {}
+    bool operator==(const QR& rhs) const {
+      return quotient == rhs.quotient && remainder == rhs.remainder;
+    }
+    bool operator!=(const QR& rhs) const {
+      return quotient != rhs.quotient || remainder == rhs.remainder;
+    }
+  };
+
+  auto divide = [](int x, int y) -> expected<QR,E> {
+    if (y == 0) {
+      return unexpected(E("divide by zero", -1));
+    } else {
+      return QR(x / y, x % y);
+    }
+  };
+
+  EXPECT_FALSE(divide(10, 0));
+  EXPECT_EQ("divide by zero", divide(10, 0).error().message);
+  EXPECT_EQ(-1, divide(10, 0).error().cause);
+
+  EXPECT_TRUE(divide(10, 3));
+  EXPECT_EQ(QR(3, 1), divide(10, 3));
+}
+
+TEST(Expected, testPair) {
+  auto test = [](bool yes) -> exp_pair {
+    if (yes) {
+      return exp_pair({"yes", 42});
+    } else {
+      return unexpected(42);
+    }
+  };
+
+  auto r = test(true);
+  EXPECT_TRUE(r);
+  EXPECT_EQ("yes", r->first);
+}
+
+TEST(Expected, testVoid) {
+  auto test = [](bool ok) -> exp_void {
+    if (ok) {
+      return {};
+    } else {
+      return unexpected(10);
+    }
+  };
+
+  auto r = test(true);
+  EXPECT_TRUE(r);
+  r = test(false);
+  EXPECT_FALSE(r);
+  EXPECT_EQ(10, r.error());
+}
+
+// copied from result_test.cpp
+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,
+    typename std::enable_if_t<std::is_convertible_v<T, std::string>>* = nullptr>
+  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;
+  }
+  static void Reset() {
+    constructor_called = 0;
+    copy_constructor_called = 0;
+    move_constructor_called = 0;
+    copy_assignment_called = 0;
+    move_assignment_called = 0;
+  }
+  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;
+
+typedef expected<ConstructorTracker, int> exp_track;
+
+TEST(Expected, testNumberOfCopies) {
+  // default constructor
+  ConstructorTracker::Reset();
+  exp_track e("hello");
+  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);
+
+  // copy constructor
+  ConstructorTracker::Reset();
+  exp_track e2 = e;
+  EXPECT_EQ(0U, ConstructorTracker::constructor_called);
+  EXPECT_EQ(1U, 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);
+
+  // move constructor
+  ConstructorTracker::Reset();
+  exp_track e3 = std::move(e);
+  EXPECT_EQ(0U, 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);
+
+  // construct from lvalue
+  ConstructorTracker::Reset();
+  ConstructorTracker ct = "hello";
+  exp_track e4(ct);
+  EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+  EXPECT_EQ(1U, 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);
+
+  // construct from rvalue
+  ConstructorTracker::Reset();
+  ConstructorTracker ct2 = "hello";
+  exp_track e5(std::move(ct2));
+  EXPECT_EQ(1U, 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);
+
+  // copy assignment
+  ConstructorTracker::Reset();
+  exp_track e6 = "hello";
+  exp_track e7 = "world";
+  e7 = e6;
+  EXPECT_EQ(2U, ConstructorTracker::constructor_called);
+  EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+  EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+  EXPECT_EQ(1U, ConstructorTracker::copy_assignment_called);
+  EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+  // move assignment
+  ConstructorTracker::Reset();
+  exp_track e8 = "hello";
+  exp_track e9 = "world";
+  e9 = std::move(e8);
+  EXPECT_EQ(2U, 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(1U, ConstructorTracker::move_assignment_called);
+
+  // swap
+  ConstructorTracker::Reset();
+  exp_track e10 = "hello";
+  exp_track e11 = "world";
+  std::swap(e10, e11);
+  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(2U, ConstructorTracker::move_assignment_called);
+}
+
+TEST(Expected, testNoCopyOnReturn) {
+  auto test = [](const std::string& in) -> exp_track {
+    if (in.empty()) {
+      return "literal string";
+    }
+    if (in == "test2") {
+      return ConstructorTracker(in + in + "2");
+    }
+    ConstructorTracker result(in + " " + in);
+    return result;
+  };
+
+  ConstructorTracker::Reset();
+  auto result1 = test("");
+  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);
+
+  ConstructorTracker::Reset();
+  auto result2 = test("test2");
+  ASSERT_TRUE(result2);
+  EXPECT_EQ("test2test22", result2->string);
+  EXPECT_EQ(1U, 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);
+
+  ConstructorTracker::Reset();
+  auto result3 = test("test3");
+  ASSERT_TRUE(result3);
+  EXPECT_EQ("test3 test3", result3->string);
+  EXPECT_EQ(1U, 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);
+}
+
+TEST(Expected, testNested) {
+  expected<exp_string, std::string> e = "hello";
+
+  EXPECT_TRUE(e.has_value());
+  EXPECT_TRUE(e.value().has_value());
+  EXPECT_TRUE(e);
+  EXPECT_TRUE(*e);
+  EXPECT_EQ("hello", e.value().value());
+
+  expected<exp_string, std::string> e2 = unexpected("world");
+  EXPECT_FALSE(e2.has_value());
+  EXPECT_FALSE(e2);
+  EXPECT_EQ("world", e2.error());
+
+  expected<exp_string, std::string> e3 = exp_string(unexpected("world"));
+  EXPECT_TRUE(e3.has_value());
+  EXPECT_FALSE(e3.value().has_value());
+  EXPECT_TRUE(e3);
+  EXPECT_FALSE(*e3);
+  EXPECT_EQ("world", e3.value().error());
+}
+
+constexpr bool equals(const char* a, const char* b) {
+  return (a == nullptr && b == nullptr) ||
+      (a != nullptr && b != nullptr && *a == *b &&
+       (*a == '\0' || equals(a + 1, b + 1)));
+}
+
+TEST(Expected, testConstexpr) {
+  // Compliation error will occur if these expressions can't be
+  // evaluated at compile time
+  constexpr exp_int e(3);
+  constexpr exp_int::unexpected_type err(3);
+  constexpr int i = 4;
+
+  // default constructor
+  static_assert(exp_int().value() == 0);
+  // copy constructor
+  static_assert(exp_int(e).value() == 3);
+  // move constructor
+  static_assert(exp_int(exp_int(4)).value() == 4);
+  // copy construct from value
+  static_assert(exp_int(i).value() == 4);
+  // copy construct from unexpected
+  static_assert(exp_int(err).error() == 3);
+  // move costruct from unexpected
+  static_assert(exp_int(unexpected(3)).error() == 3);
+  // observers
+  static_assert(*exp_int(3) == 3);
+  static_assert(exp_int(3).has_value() == true);
+  static_assert(exp_int(3).value_or(4) == 3);
+
+  typedef expected<const char*, int> exp_s;
+  constexpr exp_s s("hello");
+  constexpr const char* c = "hello";
+  static_assert(equals(exp_s().value(), nullptr));
+  static_assert(equals(exp_s(s).value(), "hello"));
+  static_assert(equals(exp_s(exp_s("hello")).value(), "hello"));
+  static_assert(equals(exp_s("hello").value(), "hello"));
+  static_assert(equals(exp_s(c).value(), "hello"));
+}
+
+TEST(Expected, testWithNonConstructible) {
+   struct AssertNotConstructed {
+     AssertNotConstructed() = delete;
+   };
+
+   expected<int, AssertNotConstructed> v(42);
+   EXPECT_TRUE(v.has_value());
+   EXPECT_EQ(42, v.value());
+
+   expected<AssertNotConstructed, int> e(unexpected(42));
+   EXPECT_FALSE(e.has_value());
+   EXPECT_EQ(42, e.error());
+}
+
+TEST(Expected, testWithMoveOnlyType) {
+  typedef expected<std::unique_ptr<int>,std::unique_ptr<int>> exp_ptr;
+  exp_ptr e(std::make_unique<int>(3));
+  exp_ptr e2(unexpected(std::make_unique<int>(4)));
+
+  EXPECT_TRUE(e.has_value());
+  EXPECT_FALSE(e2.has_value());
+  EXPECT_EQ(3, *(e.value()));
+  EXPECT_EQ(4, *(e2.error()));
+
+  e2 = std::move(e);
+  EXPECT_TRUE(e.has_value());
+  EXPECT_TRUE(e2.has_value());
+  EXPECT_EQ(3, *(e2.value()));
+}
diff --git a/base/file.cpp b/base/file.cpp
index 2f697a1..3dfcfbb 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -18,7 +18,11 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <ftw.h>
 #include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -28,40 +32,164 @@
 #include <string>
 #include <vector>
 
-#include "android-base/logging.h"
-#include "android-base/macros.h"  // For TEMP_FAILURE_RETRY on Darwin.
-#include "android-base/unique_fd.h"
-#include "android-base/utf8.h"
-
 #if defined(__APPLE__)
 #include <mach-o/dyld.h>
 #endif
 #if defined(_WIN32)
+#include <direct.h>
 #include <windows.h>
-#define O_CLOEXEC O_NOINHERIT
 #define O_NOFOLLOW 0
+#define OS_PATH_SEPARATOR '\\'
+#else
+#define OS_PATH_SEPARATOR '/'
 #endif
 
+#include "android-base/logging.h"  // and must be after windows.h for ERROR
+#include "android-base/macros.h"   // For TEMP_FAILURE_RETRY on Darwin.
+#include "android-base/unique_fd.h"
+#include "android-base/utf8.h"
+
+#ifdef _WIN32
+int mkstemp(char* template_name) {
+  if (_mktemp(template_name) == nullptr) {
+    return -1;
+  }
+  // Use open() to match the close() that TemporaryFile's destructor does.
+  // Use O_BINARY to match base file APIs.
+  return open(template_name, O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
+}
+
+char* mkdtemp(char* template_name) {
+  if (_mktemp(template_name) == nullptr) {
+    return nullptr;
+  }
+  if (_mkdir(template_name) == -1) {
+    return nullptr;
+  }
+  return template_name;
+}
+#endif
+
+namespace {
+
+std::string GetSystemTempDir() {
+#if defined(__ANDROID__)
+  const auto* tmpdir = getenv("TMPDIR");
+  if (tmpdir == nullptr) tmpdir = "/data/local/tmp";
+  if (access(tmpdir, R_OK | W_OK | X_OK) == 0) {
+    return tmpdir;
+  }
+  // Tests running in app context can't access /data/local/tmp,
+  // so try current directory if /data/local/tmp is not accessible.
+  return ".";
+#elif defined(_WIN32)
+  char tmp_dir[MAX_PATH];
+  DWORD result = GetTempPathA(sizeof(tmp_dir), tmp_dir);  // checks TMP env
+  CHECK_NE(result, 0ul) << "GetTempPathA failed, error: " << GetLastError();
+  CHECK_LT(result, sizeof(tmp_dir)) << "path truncated to: " << result;
+
+  // GetTempPath() returns a path with a trailing slash, but init()
+  // does not expect that, so remove it.
+  CHECK_EQ(tmp_dir[result - 1], '\\');
+  tmp_dir[result - 1] = '\0';
+  return tmp_dir;
+#else
+  const auto* tmpdir = getenv("TMPDIR");
+  if (tmpdir == nullptr) tmpdir = "/tmp";
+  return tmpdir;
+#endif
+}
+
+}  // namespace
+
+TemporaryFile::TemporaryFile() {
+  init(GetSystemTempDir());
+}
+
+TemporaryFile::TemporaryFile(const std::string& tmp_dir) {
+  init(tmp_dir);
+}
+
+TemporaryFile::~TemporaryFile() {
+  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) {
+  snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR);
+  fd = mkstemp(path);
+}
+
+TemporaryDir::TemporaryDir() {
+  init(GetSystemTempDir());
+}
+
+TemporaryDir::~TemporaryDir() {
+  if (!remove_dir_and_contents_) return;
+
+  auto callback = [](const char* child, const struct stat*, int file_type, struct FTW*) -> int {
+    switch (file_type) {
+      case FTW_D:
+      case FTW_DP:
+      case FTW_DNR:
+        if (rmdir(child) == -1) {
+          PLOG(ERROR) << "rmdir " << child;
+        }
+        break;
+      case FTW_NS:
+      default:
+        if (rmdir(child) != -1) break;
+        // FALLTHRU (for gcc, lint, pcc, etc; and following for clang)
+        FALLTHROUGH_INTENDED;
+      case FTW_F:
+      case FTW_SL:
+      case FTW_SLN:
+        if (unlink(child) == -1) {
+          PLOG(ERROR) << "unlink " << child;
+        }
+        break;
+    }
+    return 0;
+  };
+
+  nftw(path, callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
+}
+
+bool TemporaryDir::init(const std::string& tmp_dir) {
+  snprintf(path, sizeof(path), "%s%cTemporaryDir-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR);
+  return (mkdtemp(path) != nullptr);
+}
+
 namespace android {
 namespace base {
 
 // Versions of standard library APIs that support UTF-8 strings.
 using namespace android::base::utf8;
 
-bool ReadFdToString(int fd, std::string* content) {
+bool ReadFdToString(borrowed_fd fd, std::string* content) {
   content->clear();
 
   // Although original we had small files in mind, this code gets used for
   // very large files too, where the std::string growth heuristics might not
   // be suitable. https://code.google.com/p/android/issues/detail?id=258500.
   struct stat sb;
-  if (fstat(fd, &sb) != -1 && sb.st_size > 0) {
+  if (fstat(fd.get(), &sb) != -1 && sb.st_size > 0) {
     content->reserve(sb.st_size);
   }
 
   char buf[BUFSIZ];
   ssize_t n;
-  while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) {
+  while ((n = TEMP_FAILURE_RETRY(read(fd.get(), &buf[0], sizeof(buf)))) > 0) {
     content->append(buf, n);
   }
   return (n == 0) ? true : false;
@@ -78,11 +206,11 @@
   return ReadFdToString(fd, content);
 }
 
-bool WriteStringToFd(const std::string& content, int fd) {
+bool WriteStringToFd(const std::string& content, borrowed_fd fd) {
   const char* p = content.data();
   size_t left = content.size();
   while (left > 0) {
-    ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, left));
+    ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, left));
     if (n == -1) {
       return false;
     }
@@ -141,11 +269,11 @@
   return WriteStringToFd(content, fd) || CleanUpAfterFailedWrite(path);
 }
 
-bool ReadFully(int fd, void* data, size_t byte_count) {
+bool ReadFully(borrowed_fd fd, void* data, size_t byte_count) {
   uint8_t* p = reinterpret_cast<uint8_t*>(data);
   size_t remaining = byte_count;
   while (remaining > 0) {
-    ssize_t n = TEMP_FAILURE_RETRY(read(fd, p, remaining));
+    ssize_t n = TEMP_FAILURE_RETRY(read(fd.get(), p, remaining));
     if (n <= 0) return false;
     p += n;
     remaining -= n;
@@ -156,14 +284,14 @@
 #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) {
+static ssize_t pread(borrowed_fd 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)) {
+  if (!ReadFile(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), 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;
@@ -172,10 +300,10 @@
 }
 #endif
 
-bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset) {
+bool ReadFullyAtOffset(borrowed_fd 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));
+    ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), p, byte_count, offset));
     if (n <= 0) return false;
     p += n;
     byte_count -= n;
@@ -184,11 +312,11 @@
   return true;
 }
 
-bool WriteFully(int fd, const void* data, size_t byte_count) {
+bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count) {
   const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
   size_t remaining = byte_count;
   while (remaining > 0) {
-    ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, remaining));
+    ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, remaining));
     if (n == -1) return false;
     p += n;
     remaining -= n;
@@ -199,17 +327,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;
     }
@@ -251,7 +385,12 @@
 bool Realpath(const std::string& path, std::string* result) {
   result->clear();
 
-  char* realpath_buf = realpath(path.c_str(), nullptr);
+  // realpath may exit with EINTR. Retry if so.
+  char* realpath_buf = nullptr;
+  do {
+    realpath_buf = realpath(path.c_str(), nullptr);
+  } while (realpath_buf == nullptr && errno == EINTR);
+
   if (realpath_buf == nullptr) {
     return false;
   }
diff --git a/base/file_test.cpp b/base/file_test.cpp
index 02b431d..f64e81c 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -24,7 +24,9 @@
 
 #include <string>
 
-#include "android-base/test_utils.h"
+#if !defined(_WIN32)
+#include <pwd.h>
+#endif
 
 TEST(file, ReadFileToString_ENOENT) {
   std::string s("hello");
@@ -115,7 +117,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 +128,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/format_benchmark.cpp b/base/format_benchmark.cpp
new file mode 100644
index 0000000..9590b23
--- /dev/null
+++ b/base/format_benchmark.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/format.h"
+
+#include <limits>
+
+#include <benchmark/benchmark.h>
+
+#include "android-base/stringprintf.h"
+
+using android::base::StringPrintf;
+
+static void BenchmarkFormatInt(benchmark::State& state) {
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(fmt::format("{} {} {}", 42, std::numeric_limits<int>::min(),
+                                         std::numeric_limits<int>::max()));
+  }
+}
+
+BENCHMARK(BenchmarkFormatInt);
+
+static void BenchmarkStringPrintfInt(benchmark::State& state) {
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(StringPrintf("%d %d %d", 42, std::numeric_limits<int>::min(),
+                                          std::numeric_limits<int>::max()));
+  }
+}
+
+BENCHMARK(BenchmarkStringPrintfInt);
+
+static void BenchmarkFormatFloat(benchmark::State& state) {
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(fmt::format("{} {} {}", 42.42, std::numeric_limits<float>::min(),
+                                         std::numeric_limits<float>::max()));
+  }
+}
+
+BENCHMARK(BenchmarkFormatFloat);
+
+static void BenchmarkStringPrintfFloat(benchmark::State& state) {
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(StringPrintf("%f %f %f", 42.42, std::numeric_limits<float>::min(),
+                                          std::numeric_limits<float>::max()));
+  }
+}
+
+BENCHMARK(BenchmarkStringPrintfFloat);
+
+static void BenchmarkFormatStrings(benchmark::State& state) {
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(fmt::format("{} hello there {}", "hi,", "!!"));
+  }
+}
+
+BENCHMARK(BenchmarkFormatStrings);
+
+static void BenchmarkStringPrintfStrings(benchmark::State& state) {
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(StringPrintf("%s hello there %s", "hi,", "!!"));
+  }
+}
+
+BENCHMARK(BenchmarkStringPrintfStrings);
+
+// Run the benchmark
+BENCHMARK_MAIN();
diff --git a/base/include/android-base/chrono_utils.h b/base/include/android-base/chrono_utils.h
index c3396ee..11fcf71 100644
--- a/base/include/android-base/chrono_utils.h
+++ b/base/include/android-base/chrono_utils.h
@@ -14,13 +14,12 @@
  * 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  // C++14
+#if __cplusplus > 201103L && !defined(__WIN32)  // C++14
 using namespace std::chrono_literals;
 #endif
 
@@ -52,5 +51,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_CHRONO_UTILS_H
diff --git a/base/include/android-base/cmsg.h b/base/include/android-base/cmsg.h
new file mode 100644
index 0000000..e4197b1
--- /dev/null
+++ b/base/include/android-base/cmsg.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/stat.h>
+#include <sys/types.h>
+
+#include <type_traits>
+#include <vector>
+
+#include <android-base/collections.h>
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace base {
+
+#if !defined(_WIN32)
+
+// Helpers for sending and receiving file descriptors across Unix domain sockets.
+//
+// The cmsg(3) API is very hard to get right, with multiple landmines that can
+// lead to death. Almost all of the uses of cmsg in Android make at least one of
+// the following mistakes:
+//
+//   - not aligning the cmsg buffer
+//   - leaking fds if more fds are received than expected
+//   - blindly dereferencing CMSG_DATA without checking the header
+//   - using CMSG_SPACE instead of CMSG_LEN for .cmsg_len
+//   - using CMSG_LEN instead of CMSG_SPACE for .msg_controllen
+//   - using a length specified in number of fds instead of bytes
+//
+// These functions wrap the hard-to-use cmsg API with an easier to use abstraction.
+
+// Send file descriptors across a Unix domain socket.
+//
+// Note that the write can return short if the socket type is SOCK_STREAM. When
+// this happens, file descriptors are still sent to the other end, but with
+// truncated data. For this reason, using SOCK_SEQPACKET or SOCK_DGRAM is recommended.
+ssize_t SendFileDescriptorVector(borrowed_fd sock, const void* data, size_t len,
+                                 const std::vector<int>& fds);
+
+// Receive file descriptors from a Unix domain socket.
+//
+// If more FDs (or bytes, for datagram sockets) are received than expected,
+// -1 is returned with errno set to EMSGSIZE, and all received FDs are thrown away.
+ssize_t ReceiveFileDescriptorVector(borrowed_fd sock, void* data, size_t len, size_t max_fds,
+                                    std::vector<android::base::unique_fd>* fds);
+
+// Helper for SendFileDescriptorVector that constructs a std::vector for you, e.g.:
+//   SendFileDescriptors(sock, "foo", 3, std::move(fd1), std::move(fd2))
+template <typename... Args>
+ssize_t SendFileDescriptors(borrowed_fd sock, const void* data, size_t len, Args&&... sent_fds) {
+  // Do not allow implicit conversion to int: people might try to do something along the lines of:
+  //   SendFileDescriptors(..., std::move(a_unique_fd))
+  // and be surprised when the unique_fd isn't closed afterwards.
+  AssertType<int>(std::forward<Args>(sent_fds)...);
+  std::vector<int> fds;
+  Append(fds, std::forward<Args>(sent_fds)...);
+  return SendFileDescriptorVector(sock, data, len, fds);
+}
+
+// Helper for ReceiveFileDescriptorVector that receives an exact number of file descriptors.
+// If more file descriptors are received than requested, -1 is returned with errno set to EMSGSIZE.
+// If fewer file descriptors are received than requested, -1 is returned with errno set to ENOMSG.
+// In both cases, all arguments are cleared and any received FDs are thrown away.
+template <typename... Args>
+ssize_t ReceiveFileDescriptors(borrowed_fd sock, void* data, size_t len, Args&&... received_fds) {
+  std::vector<unique_fd*> fds;
+  Append(fds, std::forward<Args>(received_fds)...);
+
+  std::vector<unique_fd> result;
+  ssize_t rc = ReceiveFileDescriptorVector(sock, data, len, fds.size(), &result);
+  if (rc == -1 || result.size() != fds.size()) {
+    int err = rc == -1 ? errno : ENOMSG;
+    for (unique_fd* fd : fds) {
+      fd->reset();
+    }
+    errno = err;
+    return -1;
+  }
+
+  for (size_t i = 0; i < fds.size(); ++i) {
+    *fds[i] = std::move(result[i]);
+  }
+  return rc;
+}
+
+#endif
+
+}  // namespace base
+}  // namespace android
diff --git a/base/include/android-base/collections.h b/base/include/android-base/collections.h
new file mode 100644
index 0000000..be0683a
--- /dev/null
+++ b/base/include/android-base/collections.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <utility>
+
+namespace android {
+namespace base {
+
+// Helpers for converting a variadic template parameter pack to a homogeneous collection.
+// Parameters must be implictly convertible to the contained type (including via move/copy ctors).
+//
+// Use as follows:
+//
+//   template <typename... Args>
+//   std::vector<int> CreateVector(Args&&... args) {
+//     std::vector<int> result;
+//     Append(result, std::forward<Args>(args)...);
+//     return result;
+//   }
+template <typename CollectionType, typename T>
+void Append(CollectionType& collection, T&& arg) {
+  collection.push_back(std::forward<T>(arg));
+}
+
+template <typename CollectionType, typename T, typename... Args>
+void Append(CollectionType& collection, T&& arg, Args&&... args) {
+  collection.push_back(std::forward<T>(arg));
+  return Append(collection, std::forward<Args>(args)...);
+}
+
+// Assert that all of the arguments in a variadic template parameter pack are of a given type
+// after std::decay.
+template <typename T, typename Arg, typename... Args>
+void AssertType(Arg&&) {
+  static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
+}
+
+template <typename T, typename Arg, typename... Args>
+void AssertType(Arg&&, Args&&... args) {
+  static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
+  AssertType<T>(std::forward<Args>(args)...);
+}
+
+}  // namespace base
+}  // namespace android
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/expected.h b/base/include/android-base/expected.h
new file mode 100644
index 0000000..957a8a0
--- /dev/null
+++ b/base/include/android-base/expected.h
@@ -0,0 +1,786 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <initializer_list>
+#include <type_traits>
+#include <utility>
+#include <variant>
+
+// android::base::expected is an Android implementation of the std::expected
+// proposal.
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0323r7.html
+//
+// Usage:
+// using android::base::expected;
+// using android::base::unexpected;
+//
+// expected<double,std::string> safe_divide(double i, double j) {
+//   if (j == 0) return unexpected("divide by zero");
+//   else return i / j;
+// }
+//
+// void test() {
+//   auto q = safe_divide(10, 0);
+//   if (q) { printf("%f\n", q.value()); }
+//   else { printf("%s\n", q.error().c_str()); }
+// }
+//
+// When the proposal becomes part of the standard and is implemented by
+// libcxx, this will be removed and android::base::expected will be
+// type alias to std::expected.
+//
+
+namespace android {
+namespace base {
+
+// Synopsis
+template<class T, class E>
+    class expected;
+
+template<class E>
+    class unexpected;
+template<class E>
+  unexpected(E) -> unexpected<E>;
+
+template<class E>
+   class bad_expected_access;
+
+template<>
+   class bad_expected_access<void>;
+
+struct unexpect_t {
+   explicit unexpect_t() = default;
+};
+inline constexpr unexpect_t unexpect{};
+
+// macros for SFINAE
+#define _ENABLE_IF(...) \
+  , std::enable_if_t<(__VA_ARGS__)>* = nullptr
+
+// Define NODISCARD_EXPECTED to prevent expected<T,E> from being
+// ignored when used as a return value. This is off by default.
+#ifdef NODISCARD_EXPECTED
+#define _NODISCARD_ [[nodiscard]]
+#else
+#define _NODISCARD_
+#endif
+
+namespace {
+template< class T >
+struct remove_cvref {
+  typedef std::remove_cv_t<std::remove_reference_t<T>> type;
+};
+template< class T >
+using remove_cvref_t = typename remove_cvref<T>::type;
+} // namespace
+
+// Class expected
+template<class T, class E>
+class _NODISCARD_ expected {
+ public:
+  using value_type = T;
+  using error_type = E;
+  using unexpected_type = unexpected<E>;
+
+  template<class U>
+  using rebind = expected<U, error_type>;
+
+  // constructors
+  constexpr expected() = default;
+  constexpr expected(const expected& rhs) = default;
+  constexpr expected(expected&& rhs) noexcept = default;
+
+  template<class U, class G _ENABLE_IF(
+    std::is_constructible_v<T, const U&> &&
+    std::is_constructible_v<E, const G&> &&
+    !std::is_constructible_v<T, expected<U, G>&> &&
+    !std::is_constructible_v<T, expected<U, G>&&> &&
+    !std::is_constructible_v<T, const expected<U, G>&> &&
+    !std::is_constructible_v<T, const expected<U, G>&&> &&
+    !std::is_convertible_v<expected<U, G>&, T> &&
+    !std::is_convertible_v<expected<U, G>&&, T> &&
+    !std::is_convertible_v<const expected<U, G>&, T> &&
+    !std::is_convertible_v<const expected<U, G>&&, T> &&
+    !(!std::is_convertible_v<const U&, T> ||
+     !std::is_convertible_v<const G&, E>) /* non-explicit */
+  )>
+  constexpr expected(const expected<U, G>& rhs) {
+    if (rhs.has_value()) var_ = rhs.value();
+    else var_ = unexpected(rhs.error());
+  }
+
+  template<class U, class G _ENABLE_IF(
+    std::is_constructible_v<T, const U&> &&
+    std::is_constructible_v<E, const G&> &&
+    !std::is_constructible_v<T, expected<U, G>&> &&
+    !std::is_constructible_v<T, expected<U, G>&&> &&
+    !std::is_constructible_v<T, const expected<U, G>&> &&
+    !std::is_constructible_v<T, const expected<U, G>&&> &&
+    !std::is_convertible_v<expected<U, G>&, T> &&
+    !std::is_convertible_v<expected<U, G>&&, T> &&
+    !std::is_convertible_v<const expected<U, G>&, T> &&
+    !std::is_convertible_v<const expected<U, G>&&, T> &&
+    (!std::is_convertible_v<const U&, T> ||
+     !std::is_convertible_v<const G&, E>) /* explicit */
+  )>
+  constexpr explicit expected(const expected<U, G>& rhs) {
+    if (rhs.has_value()) var_ = rhs.value();
+    else var_ = unexpected(rhs.error());
+  }
+
+  template<class U, class G _ENABLE_IF(
+    std::is_constructible_v<T, const U&> &&
+    std::is_constructible_v<E, const G&> &&
+    !std::is_constructible_v<T, expected<U, G>&> &&
+    !std::is_constructible_v<T, expected<U, G>&&> &&
+    !std::is_constructible_v<T, const expected<U, G>&> &&
+    !std::is_constructible_v<T, const expected<U, G>&&> &&
+    !std::is_convertible_v<expected<U, G>&, T> &&
+    !std::is_convertible_v<expected<U, G>&&, T> &&
+    !std::is_convertible_v<const expected<U, G>&, T> &&
+    !std::is_convertible_v<const expected<U, G>&&, T> &&
+    !(!std::is_convertible_v<const U&, T> ||
+     !std::is_convertible_v<const G&, E>) /* non-explicit */
+  )>
+  constexpr expected(expected<U, G>&& rhs) {
+    if (rhs.has_value()) var_ = std::move(rhs.value());
+    else var_ = unexpected(std::move(rhs.error()));
+  }
+
+  template<class U, class G _ENABLE_IF(
+    std::is_constructible_v<T, const U&> &&
+    std::is_constructible_v<E, const G&> &&
+    !std::is_constructible_v<T, expected<U, G>&> &&
+    !std::is_constructible_v<T, expected<U, G>&&> &&
+    !std::is_constructible_v<T, const expected<U, G>&> &&
+    !std::is_constructible_v<T, const expected<U, G>&&> &&
+    !std::is_convertible_v<expected<U, G>&, T> &&
+    !std::is_convertible_v<expected<U, G>&&, T> &&
+    !std::is_convertible_v<const expected<U, G>&, T> &&
+    !std::is_convertible_v<const expected<U, G>&&, T> &&
+    (!std::is_convertible_v<const U&, T> ||
+     !std::is_convertible_v<const G&, E>) /* explicit */
+  )>
+  constexpr explicit expected(expected<U, G>&& rhs) {
+    if (rhs.has_value()) var_ = std::move(rhs.value());
+    else var_ = unexpected(std::move(rhs.error()));
+  }
+
+  template<class U = T _ENABLE_IF(
+    std::is_constructible_v<T, U&&> &&
+    !std::is_same_v<remove_cvref_t<U>, std::in_place_t> &&
+    !std::is_same_v<expected<T, E>, remove_cvref_t<U>> &&
+    !std::is_same_v<unexpected<E>, remove_cvref_t<U>> &&
+    std::is_convertible_v<U&&,T> /* non-explicit */
+  )>
+  constexpr expected(U&& v)
+  : var_(std::in_place_index<0>, std::forward<U>(v)) {}
+
+  template<class U = T _ENABLE_IF(
+    std::is_constructible_v<T, U&&> &&
+    !std::is_same_v<remove_cvref_t<U>, std::in_place_t> &&
+    !std::is_same_v<expected<T, E>, remove_cvref_t<U>> &&
+    !std::is_same_v<unexpected<E>, remove_cvref_t<U>> &&
+    !std::is_convertible_v<U&&,T> /* explicit */
+  )>
+  constexpr explicit expected(U&& v)
+  : var_(std::in_place_index<0>, T(std::forward<U>(v))) {}
+
+  template<class G = E _ENABLE_IF(
+    std::is_constructible_v<E, const G&> &&
+    std::is_convertible_v<const G&, E> /* non-explicit */
+  )>
+  constexpr expected(const unexpected<G>& e)
+  : var_(std::in_place_index<1>, e.value()) {}
+
+  template<class G = E _ENABLE_IF(
+    std::is_constructible_v<E, const G&> &&
+    !std::is_convertible_v<const G&, E> /* explicit */
+  )>
+  constexpr explicit expected(const unexpected<G>& e)
+  : var_(std::in_place_index<1>, E(e.value())) {}
+
+  template<class G = E _ENABLE_IF(
+    std::is_constructible_v<E, G&&> &&
+    std::is_convertible_v<G&&, E> /* non-explicit */
+  )>
+  constexpr expected(unexpected<G>&& e)
+  : var_(std::in_place_index<1>, std::move(e.value())) {}
+
+  template<class G = E _ENABLE_IF(
+    std::is_constructible_v<E, G&&> &&
+    !std::is_convertible_v<G&&, E> /* explicit */
+  )>
+  constexpr explicit expected(unexpected<G>&& e)
+  : var_(std::in_place_index<1>, E(std::move(e.value()))) {}
+
+  template<class... Args _ENABLE_IF(
+    std::is_constructible_v<T, Args&&...>
+  )>
+  constexpr explicit expected(std::in_place_t, Args&&... args)
+  : var_(std::in_place_index<0>, std::forward<Args>(args)...) {}
+
+  template<class U, class... Args _ENABLE_IF(
+    std::is_constructible_v<T, std::initializer_list<U>&, Args...>
+  )>
+  constexpr explicit expected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
+  : var_(std::in_place_index<0>, il, std::forward<Args>(args)...) {}
+
+  template<class... Args _ENABLE_IF(
+    std::is_constructible_v<E, Args...>
+  )>
+  constexpr explicit expected(unexpect_t, Args&&... args)
+  : var_(unexpected_type(std::forward<Args>(args)...)) {}
+
+  template<class U, class... Args _ENABLE_IF(
+    std::is_constructible_v<E, std::initializer_list<U>&, Args...>
+  )>
+  constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
+  : var_(unexpected_type(il, std::forward<Args>(args)...)) {}
+
+  // destructor
+  ~expected() = default;
+
+  // assignment
+  // Note: SFNAIE doesn't work here because assignment operator should be
+  // non-template. We could workaround this by defining a templated parent class
+  // having the assignment operator. This incomplete implementation however
+  // doesn't allow us to copy assign expected<T,E> even when T is non-copy
+  // assignable. The copy assignment will fail by the underlying std::variant
+  // anyway though the error message won't be clear.
+  expected& operator=(const expected& rhs) = default;
+
+  // Note for SFNAIE above applies to here as well
+  expected& operator=(expected&& rhs) = default;
+
+  template<class U = T _ENABLE_IF(
+    !std::is_void_v<T> &&
+    !std::is_same_v<expected<T,E>, remove_cvref_t<U>> &&
+    !std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::decay_t<U>>> &&
+    std::is_constructible_v<T,U> &&
+    std::is_assignable_v<T&,U> &&
+    std::is_nothrow_move_constructible_v<E>
+  )>
+  expected& operator=(U&& rhs) {
+    var_ = T(std::forward<U>(rhs));
+    return *this;
+  }
+
+  template<class G = E>
+  expected& operator=(const unexpected<G>& rhs) {
+    var_ = rhs;
+    return *this;
+  }
+
+  template<class G = E _ENABLE_IF(
+    std::is_nothrow_move_constructible_v<G> &&
+    std::is_move_assignable_v<G>
+  )>
+  expected& operator=(unexpected<G>&& rhs) {
+    var_ = std::move(rhs);
+    return *this;
+  }
+
+  // modifiers
+  template<class... Args _ENABLE_IF(
+    std::is_nothrow_constructible_v<T, Args...>
+  )>
+  T& emplace(Args&&... args) {
+    expected(std::in_place, std::forward<Args>(args)...).swap(*this);
+    return value();
+  }
+
+  template<class U, class... Args _ENABLE_IF(
+    std::is_nothrow_constructible_v<T, std::initializer_list<U>&, Args...>
+  )>
+  T& emplace(std::initializer_list<U> il, Args&&... args) {
+    expected(std::in_place, il, std::forward<Args>(args)...).swap(*this);
+    return value();
+  }
+
+  // swap
+  template<typename U = T, typename = std::enable_if_t<(
+    std::is_swappable_v<U> &&
+    std::is_swappable_v<E> &&
+    (std::is_move_constructible_v<U> ||
+     std::is_move_constructible_v<E>))>>
+  void swap(expected& rhs) noexcept(
+    std::is_nothrow_move_constructible_v<T> &&
+    std::is_nothrow_swappable_v<T> &&
+    std::is_nothrow_move_constructible_v<E> &&
+    std::is_nothrow_swappable_v<E>) {
+    var_.swap(rhs.var_);
+  }
+
+  // observers
+  constexpr const T* operator->() const { return std::addressof(value()); }
+  constexpr T* operator->() { return std::addressof(value()); }
+  constexpr const T& operator*() const& { return value(); }
+  constexpr T& operator*() & { return value(); }
+  constexpr const T&& operator*() const&& { return std::move(std::get<T>(var_)); }
+  constexpr T&& operator*() && { return std::move(std::get<T>(var_)); }
+
+  constexpr explicit operator bool() const noexcept { return has_value(); }
+  constexpr bool has_value() const noexcept { return var_.index() == 0; }
+
+  constexpr const T& value() const& { return std::get<T>(var_); }
+  constexpr T& value() & { return std::get<T>(var_); }
+  constexpr const T&& value() const&& { return std::move(std::get<T>(var_)); }
+  constexpr T&& value() && { return std::move(std::get<T>(var_)); }
+
+  constexpr const E& error() const& { return std::get<unexpected_type>(var_).value(); }
+  constexpr E& error() & { return std::get<unexpected_type>(var_).value(); }
+  constexpr const E&& error() const&& { return std::move(std::get<unexpected_type>(var_)).value(); }
+  constexpr E&& error() && { return std::move(std::get<unexpected_type>(var_)).value(); }
+
+  template<class U _ENABLE_IF(
+    std::is_copy_constructible_v<T> &&
+    std::is_convertible_v<U, T>
+  )>
+  constexpr T value_or(U&& v) const& {
+    if (has_value()) return value();
+    else return static_cast<T>(std::forward<U>(v));
+  }
+
+  template<class U _ENABLE_IF(
+    std::is_move_constructible_v<T> &&
+    std::is_convertible_v<U, T>
+  )>
+  constexpr T value_or(U&& v) && {
+    if (has_value()) return std::move(value());
+    else return static_cast<T>(std::forward<U>(v));
+  }
+
+  // expected equality operators
+  template<class T1, class E1, class T2, class E2>
+  friend constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y);
+  template<class T1, class E1, class T2, class E2>
+  friend constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y);
+
+  // comparison with T
+  template<class T1, class E1, class T2>
+  friend constexpr bool operator==(const expected<T1, E1>&, const T2&);
+  template<class T1, class E1, class T2>
+  friend constexpr bool operator==(const T2&, const expected<T1, E1>&);
+  template<class T1, class E1, class T2>
+  friend constexpr bool operator!=(const expected<T1, E1>&, const T2&);
+  template<class T1, class E1, class T2>
+  friend constexpr bool operator!=(const T2&, const expected<T1, E1>&);
+
+  // Comparison with unexpected<E>
+  template<class T1, class E1, class E2>
+  friend constexpr bool operator==(const expected<T1, E1>&, const unexpected<E2>&);
+  template<class T1, class E1, class E2>
+  friend constexpr bool operator==(const unexpected<E2>&, const expected<T1, E1>&);
+  template<class T1, class E1, class E2>
+  friend constexpr bool operator!=(const expected<T1, E1>&, const unexpected<E2>&);
+  template<class T1, class E1, class E2>
+  friend constexpr bool operator!=(const unexpected<E2>&, const expected<T1, E1>&);
+
+  // Specialized algorithms
+  template<class T1, class E1>
+  friend void swap(expected<T1, E1>&, expected<T1, E1>&) noexcept;
+
+ private:
+  std::variant<value_type, unexpected_type> var_;
+};
+
+template<class T1, class E1, class T2, class E2>
+constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y) {
+  if (x.has_value() != y.has_value()) {
+    return false;
+  } else if (!x.has_value()) {
+    return x.error() == y.error();
+  } else {
+    return *x == *y;
+  }
+}
+
+template<class T1, class E1, class T2, class E2>
+constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y) {
+  return !(x == y);
+}
+
+// comparison with T
+template<class T1, class E1, class T2>
+constexpr bool operator==(const expected<T1, E1>& x, const T2& y) {
+  return x.has_value() && (*x == y);
+}
+template<class T1, class E1, class T2>
+constexpr bool operator==(const T2& x, const expected<T1, E1>& y) {
+  return y.has_value() && (x == *y);
+}
+template<class T1, class E1, class T2>
+constexpr bool operator!=(const expected<T1, E1>& x, const T2& y) {
+  return !x.has_value() || (*x != y);
+}
+template<class T1, class E1, class T2>
+constexpr bool operator!=(const T2& x, const expected<T1, E1>& y) {
+  return !y.has_value() || (x != *y);
+}
+
+// Comparison with unexpected<E>
+template<class T1, class E1, class E2>
+constexpr bool operator==(const expected<T1, E1>& x, const unexpected<E2>& y) {
+  return !x.has_value() && (x.error() == y.value());
+}
+template<class T1, class E1, class E2>
+constexpr bool operator==(const unexpected<E2>& x, const expected<T1, E1>& y) {
+  return !y.has_value() && (x.value() == y.error());
+}
+template<class T1, class E1, class E2>
+constexpr bool operator!=(const expected<T1, E1>& x, const unexpected<E2>& y) {
+  return x.has_value() || (x.error() != y.value());
+}
+template<class T1, class E1, class E2>
+constexpr bool operator!=(const unexpected<E2>& x, const expected<T1, E1>& y) {
+  return y.has_value() || (x.value() != y.error());
+}
+
+template<class E>
+class _NODISCARD_ expected<void, E> {
+ public:
+  using value_type = void;
+  using error_type = E;
+  using unexpected_type = unexpected<E>;
+
+  // constructors
+  constexpr expected() = default;
+  constexpr expected(const expected& rhs) = default;
+  constexpr expected(expected&& rhs) noexcept = default;
+
+  template<class U, class G _ENABLE_IF(
+    std::is_void_v<U> &&
+    std::is_convertible_v<const G&, E> /* non-explicit */
+  )>
+  constexpr expected(const expected<U, G>& rhs) {
+    if (!rhs.has_value()) var_ = unexpected(rhs.error());
+  }
+
+  template<class U, class G _ENABLE_IF(
+    std::is_void_v<U> &&
+    !std::is_convertible_v<const G&, E> /* explicit */
+  )>
+  constexpr explicit expected(const expected<U, G>& rhs) {
+    if (!rhs.has_value()) var_ = unexpected(rhs.error());
+  }
+
+  template<class U, class G _ENABLE_IF(
+    std::is_void_v<U> &&
+    std::is_convertible_v<const G&&, E> /* non-explicit */
+  )>
+  constexpr expected(expected<U, G>&& rhs) {
+    if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error()));
+  }
+
+  template<class U, class G _ENABLE_IF(
+    std::is_void_v<U> &&
+    !std::is_convertible_v<const G&&, E> /* explicit */
+  )>
+  constexpr explicit expected(expected<U, G>&& rhs) {
+    if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error()));
+  }
+
+  template<class G = E _ENABLE_IF(
+    std::is_constructible_v<E, const G&> &&
+    std::is_convertible_v<const G&, E> /* non-explicit */
+  )>
+  constexpr expected(const unexpected<G>& e)
+  : var_(std::in_place_index<1>, e.value()) {}
+
+  template<class G = E _ENABLE_IF(
+    std::is_constructible_v<E, const G&> &&
+    !std::is_convertible_v<const G&, E> /* explicit */
+  )>
+  constexpr explicit expected(const unexpected<G>& e)
+  : var_(std::in_place_index<1>, E(e.value())) {}
+
+  template<class G = E _ENABLE_IF(
+    std::is_constructible_v<E, G&&> &&
+    std::is_convertible_v<G&&, E> /* non-explicit */
+  )>
+  constexpr expected(unexpected<G>&& e)
+  : var_(std::in_place_index<1>, std::move(e.value())) {}
+
+  template<class G = E _ENABLE_IF(
+    std::is_constructible_v<E, G&&> &&
+    !std::is_convertible_v<G&&, E> /* explicit */
+  )>
+  constexpr explicit expected(unexpected<G>&& e)
+  : var_(std::in_place_index<1>, E(std::move(e.value()))) {}
+
+  template<class... Args _ENABLE_IF(
+    sizeof...(Args) == 0
+  )>
+  constexpr explicit expected(std::in_place_t, Args&&...) {}
+
+  template<class... Args _ENABLE_IF(
+    std::is_constructible_v<E, Args...>
+  )>
+  constexpr explicit expected(unexpect_t, Args&&... args)
+  : var_(unexpected_type(std::forward<Args>(args)...)) {}
+
+  template<class U, class... Args _ENABLE_IF(
+    std::is_constructible_v<E, std::initializer_list<U>&, Args...>
+  )>
+  constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
+  : var_(unexpected_type(il, std::forward<Args>(args)...)) {}
+
+  // destructor
+  ~expected() = default;
+
+  // assignment
+  // Note: SFNAIE doesn't work here because assignment operator should be
+  // non-template. We could workaround this by defining a templated parent class
+  // having the assignment operator. This incomplete implementation however
+  // doesn't allow us to copy assign expected<T,E> even when T is non-copy
+  // assignable. The copy assignment will fail by the underlying std::variant
+  // anyway though the error message won't be clear.
+  expected& operator=(const expected& rhs) = default;
+
+  // Note for SFNAIE above applies to here as well
+  expected& operator=(expected&& rhs) = default;
+
+  template<class G = E>
+  expected& operator=(const unexpected<G>& rhs) {
+    var_ = rhs;
+    return *this;
+  }
+
+  template<class G = E _ENABLE_IF(
+    std::is_nothrow_move_constructible_v<G> &&
+    std::is_move_assignable_v<G>
+  )>
+  expected& operator=(unexpected<G>&& rhs) {
+    var_ = std::move(rhs);
+    return *this;
+  }
+
+  // modifiers
+  void emplace() {
+    var_ = std::monostate();
+  }
+
+  // swap
+  template<typename = std::enable_if_t<
+    std::is_swappable_v<E>>
+  >
+  void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v<E>) {
+    var_.swap(rhs.var_);
+  }
+
+  // observers
+  constexpr explicit operator bool() const noexcept { return has_value(); }
+  constexpr bool has_value() const noexcept { return var_.index() == 0; }
+
+  constexpr void value() const& { if (!has_value()) std::get<0>(var_); }
+
+  constexpr const E& error() const& { return std::get<unexpected_type>(var_).value(); }
+  constexpr E& error() & { return std::get<unexpected_type>(var_).value(); }
+  constexpr const E&& error() const&& { return std::move(std::get<unexpected_type>(var_)).value(); }
+  constexpr E&& error() && { return std::move(std::get<unexpected_type>(var_)).value(); }
+
+  // expected equality operators
+  template<class E1, class E2>
+  friend constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y);
+
+  // Specialized algorithms
+  template<class T1, class E1>
+  friend void swap(expected<T1, E1>&, expected<T1, E1>&) noexcept;
+
+ private:
+  std::variant<std::monostate, unexpected_type> var_;
+};
+
+template<class E1, class E2>
+constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y) {
+  if (x.has_value() != y.has_value()) {
+    return false;
+  } else if (!x.has_value()) {
+    return x.error() == y.error();
+  } else {
+    return true;
+  }
+}
+
+template<class T1, class E1, class E2>
+constexpr bool operator==(const expected<T1, E1>& x, const expected<void, E2>& y) {
+  if (x.has_value() != y.has_value()) {
+    return false;
+  } else if (!x.has_value()) {
+    return x.error() == y.error();
+  } else {
+    return false;
+  }
+}
+
+template<class E1, class T2, class E2>
+constexpr bool operator==(const expected<void, E1>& x, const expected<T2, E2>& y) {
+  if (x.has_value() != y.has_value()) {
+    return false;
+  } else if (!x.has_value()) {
+    return x.error() == y.error();
+  } else {
+    return false;
+  }
+}
+
+template<class E>
+class unexpected {
+ public:
+  // constructors
+  constexpr unexpected(const unexpected&) = default;
+  constexpr unexpected(unexpected&&) = default;
+
+  template<class Err = E _ENABLE_IF(
+    std::is_constructible_v<E, Err> &&
+    !std::is_same_v<remove_cvref_t<E>, std::in_place_t> &&
+    !std::is_same_v<remove_cvref_t<E>, unexpected>
+  )>
+  constexpr unexpected(Err&& e)
+  : val_(std::forward<Err>(e)) {}
+
+  template<class U, class... Args _ENABLE_IF(
+    std::is_constructible_v<E, std::initializer_list<U>&, Args...>
+  )>
+  constexpr explicit unexpected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
+  : val_(il, std::forward<Args>(args)...) {}
+
+  template<class Err _ENABLE_IF(
+    std::is_constructible_v<E, Err> &&
+    !std::is_constructible_v<E, unexpected<Err>&> &&
+    !std::is_constructible_v<E, unexpected<Err>> &&
+    !std::is_constructible_v<E, const unexpected<Err>&> &&
+    !std::is_constructible_v<E, const unexpected<Err>> &&
+    !std::is_convertible_v<unexpected<Err>&, E> &&
+    !std::is_convertible_v<unexpected<Err>, E> &&
+    !std::is_convertible_v<const unexpected<Err>&, E> &&
+    !std::is_convertible_v<const unexpected<Err>, E> &&
+    std::is_convertible_v<Err, E> /* non-explicit */
+  )>
+  constexpr unexpected(const unexpected<Err>& rhs)
+  : val_(rhs.value()) {}
+
+  template<class Err _ENABLE_IF(
+    std::is_constructible_v<E, Err> &&
+    !std::is_constructible_v<E, unexpected<Err>&> &&
+    !std::is_constructible_v<E, unexpected<Err>> &&
+    !std::is_constructible_v<E, const unexpected<Err>&> &&
+    !std::is_constructible_v<E, const unexpected<Err>> &&
+    !std::is_convertible_v<unexpected<Err>&, E> &&
+    !std::is_convertible_v<unexpected<Err>, E> &&
+    !std::is_convertible_v<const unexpected<Err>&, E> &&
+    !std::is_convertible_v<const unexpected<Err>, E> &&
+    !std::is_convertible_v<Err, E> /* explicit */
+  )>
+  constexpr explicit unexpected(const unexpected<Err>& rhs)
+  : val_(E(rhs.value())) {}
+
+  template<class Err _ENABLE_IF(
+    std::is_constructible_v<E, Err> &&
+    !std::is_constructible_v<E, unexpected<Err>&> &&
+    !std::is_constructible_v<E, unexpected<Err>> &&
+    !std::is_constructible_v<E, const unexpected<Err>&> &&
+    !std::is_constructible_v<E, const unexpected<Err>> &&
+    !std::is_convertible_v<unexpected<Err>&, E> &&
+    !std::is_convertible_v<unexpected<Err>, E> &&
+    !std::is_convertible_v<const unexpected<Err>&, E> &&
+    !std::is_convertible_v<const unexpected<Err>, E> &&
+    std::is_convertible_v<Err, E> /* non-explicit */
+  )>
+  constexpr unexpected(unexpected<Err>&& rhs)
+  : val_(std::move(rhs.value())) {}
+
+  template<class Err _ENABLE_IF(
+    std::is_constructible_v<E, Err> &&
+    !std::is_constructible_v<E, unexpected<Err>&> &&
+    !std::is_constructible_v<E, unexpected<Err>> &&
+    !std::is_constructible_v<E, const unexpected<Err>&> &&
+    !std::is_constructible_v<E, const unexpected<Err>> &&
+    !std::is_convertible_v<unexpected<Err>&, E> &&
+    !std::is_convertible_v<unexpected<Err>, E> &&
+    !std::is_convertible_v<const unexpected<Err>&, E> &&
+    !std::is_convertible_v<const unexpected<Err>, E> &&
+    !std::is_convertible_v<Err, E> /* explicit */
+  )>
+  constexpr explicit unexpected(unexpected<Err>&& rhs)
+  : val_(E(std::move(rhs.value()))) {}
+
+  // assignment
+  constexpr unexpected& operator=(const unexpected&) = default;
+  constexpr unexpected& operator=(unexpected&&) = default;
+  template<class Err = E>
+  constexpr unexpected& operator=(const unexpected<Err>& rhs) {
+    val_ = rhs.value();
+    return *this;
+  }
+  template<class Err = E>
+  constexpr unexpected& operator=(unexpected<Err>&& rhs) {
+    val_ = std::forward<E>(rhs.value());
+    return *this;
+  }
+
+  // observer
+  constexpr const E& value() const& noexcept { return val_; }
+  constexpr E& value() & noexcept { return val_; }
+  constexpr const E&& value() const&& noexcept { return std::move(val_); }
+  constexpr E&& value() && noexcept { return std::move(val_); }
+
+  void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
+    std::swap(val_, other.val_);
+  }
+
+  template<class E1, class E2>
+  friend constexpr bool
+  operator==(const unexpected<E1>& e1, const unexpected<E2>& e2);
+  template<class E1, class E2>
+  friend constexpr bool
+  operator!=(const unexpected<E1>& e1, const unexpected<E2>& e2);
+
+  template<class E1>
+  friend void swap(unexpected<E1>& x, unexpected<E1>& y) noexcept(noexcept(x.swap(y)));
+
+ private:
+  E val_;
+};
+
+template<class E1, class E2>
+constexpr bool
+operator==(const unexpected<E1>& e1, const unexpected<E2>& e2) {
+  return e1.value() == e2.value();
+}
+
+template<class E1, class E2>
+constexpr bool
+operator!=(const unexpected<E1>& e1, const unexpected<E2>& e2) {
+  return e1.value() != e2.value();
+}
+
+template<class E1>
+void swap(unexpected<E1>& x, unexpected<E1>& y) noexcept(noexcept(x.swap(y))) {
+  x.swap(y);
+}
+
+// TODO: bad_expected_access class
+
+#undef _ENABLE_IF
+#undef _NODISCARD_
+
+}  // namespace base
+}  // namespace android
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index 667d6fb..c622562 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -14,32 +14,77 @@
  * 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>
 
+#include "android-base/macros.h"
+#include "android-base/off64_t.h"
+#include "android-base/unique_fd.h"
+
 #if !defined(_WIN32) && !defined(O_BINARY)
+/** Windows needs O_BINARY, but Unix never mangles line endings. */
 #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;
+#if defined(_WIN32) && !defined(O_CLOEXEC)
+/** Windows has O_CLOEXEC but calls it O_NOINHERIT for some reason. */
+#define O_CLOEXEC O_NOINHERIT
 #endif
 
+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);
+};
+
+class TemporaryDir {
+ public:
+  TemporaryDir();
+  ~TemporaryDir();
+  // Don't remove the temporary dir in the destructor.
+  void DoNotRemove() { remove_dir_and_contents_ = false; }
+
+  char path[1024];
+
+ private:
+  bool init(const std::string& tmp_dir);
+
+  bool remove_dir_and_contents_ = true;
+
+  DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
+};
+
 namespace android {
 namespace base {
 
-bool ReadFdToString(int fd, std::string* content);
+bool ReadFdToString(borrowed_fd fd, std::string* content);
 bool ReadFileToString(const std::string& path, std::string* content,
                       bool follow_symlinks = false);
 
 bool WriteStringToFile(const std::string& content, const std::string& path,
                        bool follow_symlinks = false);
-bool WriteStringToFd(const std::string& content, int fd);
+bool WriteStringToFd(const std::string& content, borrowed_fd fd);
 
 #if !defined(_WIN32)
 bool WriteStringToFile(const std::string& content, const std::string& path,
@@ -47,7 +92,7 @@
                        bool follow_symlinks = false);
 #endif
 
-bool ReadFully(int fd, void* data, size_t byte_count);
+bool ReadFully(borrowed_fd 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.
@@ -57,9 +102,9 @@
 // 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 ReadFullyAtOffset(borrowed_fd fd, void* data, size_t byte_count, off64_t offset);
 
-bool WriteFully(int fd, const void* data, size_t byte_count);
+bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count);
 
 bool RemoveFileIfExists(const std::string& path, std::string* err = nullptr);
 
@@ -78,5 +123,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif // ANDROID_BASE_FILE_H
diff --git a/base/include/android-base/format.h b/base/include/android-base/format.h
new file mode 100644
index 0000000..6799c1f
--- /dev/null
+++ b/base/include/android-base/format.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
+
+// We include fmtlib here as an alias, since libbase will have fmtlib statically linked already.
+// It is accessed through its normal fmt:: namespace.
+#include <fmt/core.h>
+#include <fmt/format.h>
+#include <fmt/ostream.h>
+#include <fmt/printf.h>
+#include <fmt/time.h>
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index cc7aaf6..ab6476c 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.
@@ -100,8 +99,17 @@
                                        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);
 
@@ -439,11 +447,6 @@
  private:
   const std::unique_ptr<LogMessageData> data_;
 
-  // TODO(b/35361699): remove these symbols once all prebuilds stop using it.
-  LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity, int error);
-  static void LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity,
-                      const char* msg);
-
   DISALLOW_COPY_AND_ASSIGN(LogMessage);
 };
 
@@ -466,7 +469,7 @@
 }  // namespace base
 }  // namespace android
 
-namespace std {
+namespace std {  // NOLINT(cert-dcl58-cpp)
 
 // Emit a warning of ostream<< with std::string*. The intention was most likely to print *string.
 //
@@ -479,23 +482,14 @@
 // 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.
-#ifdef __clang__
 #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")))
-#else
-#define OSTREAM_STRING_POINTER_USAGE_WARNING /* empty */
-#endif
 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);
 }
-#ifdef __clang__
 #pragma clang diagnostic pop
-#endif
-#undef OSTREAM_STRING_POINTER_USAGE_WARNING
 
 }  // namespace std
-
-#endif  // ANDROID_BASE_LOGGING_H
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
index 25f2ff4..5abf514 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)            \
@@ -74,45 +75,7 @@
 
 #define arraysize(array) (sizeof(ArraySizeHelper(array)))
 
-// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize,
-// but can be used on anonymous types or types defined inside
-// functions.  It's less safe than arraysize as it accepts some
-// (although not all) pointers.  Therefore, you should use arraysize
-// whenever possible.
-//
-// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type
-// size_t.
-//
-// ARRAYSIZE_UNSAFE catches a few type errors.  If you see a compiler error
-//
-//   "warning: division by zero in ..."
-//
-// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer.
-// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays.
-//
-// The following comments are on the implementation details, and can
-// be ignored by the users.
-//
-// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in
-// the array) and sizeof(*(arr)) (the # of bytes in one array
-// element).  If the former is divisible by the latter, perhaps arr is
-// indeed an array, in which case the division result is the # of
-// elements in the array.  Otherwise, arr cannot possibly be an array,
-// and we generate a compiler error to prevent the code from
-// compiling.
-//
-// Since the size of bool is implementation-defined, we need to cast
-// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
-// result has type size_t.
-//
-// This macro is not perfect as it wrongfully accepts certain
-// pointers, namely where the pointer size is divisible by the pointee
-// size.  Since all our code has to go through a 32-bit compiler,
-// where a pointer is 4 bytes, this means all pointers to a type whose
-// size is 3 or greater than 4 will be (righteously) rejected.
-#define ARRAYSIZE_UNSAFE(a)     \
-  ((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
@@ -150,33 +113,25 @@
 //    case 42:
 //      ...
 //
-//  As shown in the example above, the FALLTHROUGH_INTENDED macro should be
-//  followed by a semicolon. It is designed to mimic control-flow statements
-//  like 'break;', so it can be placed in most places where 'break;' can, but
-//  only if there are no statements on the execution path between it and the
-//  next switch label.
+// As shown in the example above, the FALLTHROUGH_INTENDED macro should be
+// followed by a semicolon. It is designed to mimic control-flow statements
+// like 'break;', so it can be placed in most places where 'break;' can, but
+// only if there are no statements on the execution path between it and the
+// next switch label.
 //
-//  When compiled with clang, the FALLTHROUGH_INTENDED macro is expanded to
-//  [[clang::fallthrough]] attribute, which is analysed when performing switch
-//  labels fall-through diagnostic ('-Wimplicit-fallthrough'). See clang
-//  documentation on language extensions for details:
-//  http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough
+// When compiled with clang, the FALLTHROUGH_INTENDED macro is expanded to
+// [[clang::fallthrough]] attribute, which is analysed when performing switch
+// labels fall-through diagnostic ('-Wimplicit-fallthrough'). See clang
+// documentation on language extensions for details:
+// http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough
 //
-//  When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no
-//  effect on diagnostics.
+// When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no
+// effect on diagnostics.
 //
-//  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
-
+// In either case this macro has no effect on runtime behavior and performance
+// of code.
 #ifndef FALLTHROUGH_INTENDED
-#define FALLTHROUGH_INTENDED \
-  do {                       \
-  } while (0)
+#define FALLTHROUGH_INTENDED [[clang::fallthrough]]  // NOLINT
 #endif
 
 // Current ABI string
@@ -193,5 +148,3 @@
 #elif defined(__mips__) && defined(__LP64__)
 #define ABI_STRING "mips64"
 #endif
-
-#endif  // ANDROID_BASE_MACROS_H
diff --git a/base/include/android-base/mapped_file.h b/base/include/android-base/mapped_file.h
new file mode 100644
index 0000000..2ab49ab
--- /dev/null
+++ b/base/include/android-base/mapped_file.h
@@ -0,0 +1,77 @@
+/*
+ * 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>
+
+#include <memory>
+
+#include "android-base/macros.h"
+#include "android-base/off64_t.h"
+#include "android-base/unique_fd.h"
+
+#if defined(_WIN32)
+#include <windows.h>
+#define PROT_READ 1
+#define PROT_WRITE 2
+#else
+#include <sys/mman.h>
+#endif
+
+namespace android {
+namespace base {
+
+/**
+ * A region of a file mapped into memory, also known as MmapFile.
+ */
+class MappedFile {
+ public:
+  /**
+   * Creates a new mapping of the file pointed to by `fd`. Unlike the underlying OS primitives,
+   * `offset` does not need to be page-aligned. If `PROT_WRITE` is set in `prot`, the mapping
+   * will be writable, otherwise it will be read-only. Mappings are always `MAP_SHARED`.
+   */
+  static std::unique_ptr<MappedFile> FromFd(borrowed_fd fd, off64_t offset, size_t length,
+                                            int prot);
+
+  /**
+   * Removes the mapping.
+   */
+  ~MappedFile();
+
+  char* data() { return base_ + offset_; }
+  size_t size() { return size_; }
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(MappedFile);
+
+  char* base_;
+  size_t size_;
+
+  size_t offset_;
+
+#if defined(_WIN32)
+  MappedFile(char* base, size_t size, size_t offset, HANDLE handle)
+      : base_(base), size_(size), offset_(offset), handle_(handle) {}
+  HANDLE handle_;
+#else
+  MappedFile(char* base, size_t size, size_t offset) : base_(base), size_(size), offset_(offset) {}
+#endif
+};
+
+}  // namespace base
+}  // namespace android
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/off64_t.h b/base/include/android-base/off64_t.h
new file mode 100644
index 0000000..e6b71b8
--- /dev/null
+++ b/base/include/android-base/off64_t.h
@@ -0,0 +1,22 @@
+/*
+ * 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
+
+#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
diff --git a/base/include/android-base/parsedouble.h b/base/include/android-base/parsedouble.h
index daa6902..ccffba2 100644
--- a/base/include/android-base/parsedouble.h
+++ b/base/include/android-base/parsedouble.h
@@ -14,37 +14,64 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_PARSEDOUBLE_H
-#define ANDROID_BASE_PARSEDOUBLE_H
+#pragma once
 
 #include <errno.h>
 #include <stdlib.h>
 
 #include <limits>
+#include <string>
 
 namespace android {
 namespace base {
 
-// Parse double value in the string 's' and sets 'out' to that value.
+// Parse floating value in the string 's' and sets 'out' to that value if it exists.
 // Optionally allows the caller to define a 'min' and 'max' beyond which
 // otherwise valid values will be rejected. Returns boolean success.
-static inline bool ParseDouble(const char* s, double* out,
-                               double min = std::numeric_limits<double>::lowest(),
-                               double max = std::numeric_limits<double>::max()) {
+template <typename T, T (*strtox)(const char* str, char** endptr)>
+static inline bool ParseFloatingPoint(const char* s, T* out, T min, T max) {
   errno = 0;
   char* end;
-  double result = strtod(s, &end);
+  T result = strtox(s, &end);
   if (errno != 0 || s == end || *end != '\0') {
     return false;
   }
   if (result < min || max < result) {
     return false;
   }
-  *out = result;
+  if (out != nullptr) {
+    *out = result;
+  }
   return true;
 }
 
+// Parse double value in the string 's' and sets 'out' to that value if it exists.
+// Optionally allows the caller to define a 'min' and 'max' beyond which
+// otherwise valid values will be rejected. Returns boolean success.
+static inline bool ParseDouble(const char* s, double* out,
+                               double min = std::numeric_limits<double>::lowest(),
+                               double max = std::numeric_limits<double>::max()) {
+  return ParseFloatingPoint<double, strtod>(s, out, min, max);
+}
+static inline bool ParseDouble(const std::string& s, double* out,
+                               double min = std::numeric_limits<double>::lowest(),
+                               double max = std::numeric_limits<double>::max()) {
+  return ParseFloatingPoint<double, strtod>(s.c_str(), out, min, max);
+}
+
+// Parse float value in the string 's' and sets 'out' to that value if it exists.
+// Optionally allows the caller to define a 'min' and 'max' beyond which
+// otherwise valid values will be rejected. Returns boolean success.
+static inline bool ParseFloat(const char* s, float* out,
+                              float min = std::numeric_limits<float>::lowest(),
+                              float max = std::numeric_limits<float>::max()) {
+  return ParseFloatingPoint<float, strtof>(s, out, min, max);
+}
+static inline bool ParseFloat(const std::string& s, float* out,
+                              float min = std::numeric_limits<float>::lowest(),
+                              float max = std::numeric_limits<float>::max()) {
+  return ParseFloatingPoint<float, strtof>(s.c_str(), out, min, max);
+}
+
 }  // 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..be8b97b 100644
--- a/base/include/android-base/parseint.h
+++ b/base/include/android-base/parseint.h
@@ -14,65 +14,113 @@
  * 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>
+#include <type_traits>
 
 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'
-// is untouched if parsing fails.
+// Parses the unsigned decimal or hexadecimal integer in the string 's' and sets
+// 'out' to that value if it is specified. 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) {
+  static_assert(std::is_unsigned<T>::value, "ParseUint can only be used with unsigned types");
+  while (isspace(*s)) {
+    s++;
+  }
+
+  if (s[0] == '-') {
+    errno = EINVAL;
+    return 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') {
+  if (errno != 0) return false;
+  if (end == s) {
+    errno = EINVAL;
     return false;
   }
+  if (*end != '\0') {
+    const char* suffixes = "bkmgtpe";
+    const char* suffix;
+    if ((!allow_suffixes || (suffix = strchr(suffixes, tolower(*end))) == nullptr) ||
+        __builtin_mul_overflow(result, 1ULL << (10 * (suffix - suffixes)), &result)) {
+      errno = EINVAL;
+      return false;
+    }
+  }
   if (max < result) {
+    errno = ERANGE;
     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
-// beyond which otherwise valid values will be rejected. Returns boolean
-// success; 'out' is untouched if parsing fails.
+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 if it is specified. 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>
 bool ParseInt(const char* s, T* out,
               T min = std::numeric_limits<T>::min(),
               T max = std::numeric_limits<T>::max()) {
+  static_assert(std::is_signed<T>::value, "ParseInt can only be used with signed types");
+  while (isspace(*s)) {
+    s++;
+  }
+
   int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10;
   errno = 0;
   char* end;
   long long int result = strtoll(s, &end, base);
-  if (errno != 0 || s == end || *end != '\0') {
+  if (errno != 0) {
+    return false;
+  }
+  if (s == end || *end != '\0') {
+    errno = EINVAL;
     return false;
   }
   if (result < min || max < result) {
+    errno = ERANGE;
     return false;
   }
-  *out = static_cast<T>(result);
+  if (out != nullptr) {
+    *out = static_cast<T>(result);
+  }
   return true;
 }
 
@@ -86,5 +134,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/process.h b/base/include/android-base/process.h
new file mode 100644
index 0000000..69ed3fb
--- /dev/null
+++ b/base/include/android-base/process.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <dirent.h>
+#include <sys/types.h>
+
+#include <iterator>
+#include <memory>
+#include <vector>
+
+namespace android {
+namespace base {
+
+class AllPids {
+  class PidIterator {
+   public:
+    PidIterator(DIR* dir) : dir_(dir, closedir) { Increment(); }
+    PidIterator& operator++() {
+      Increment();
+      return *this;
+    }
+    bool operator==(const PidIterator& other) const { return pid_ == other.pid_; }
+    bool operator!=(const PidIterator& other) const { return !(*this == other); }
+    long operator*() const { return pid_; }
+    // iterator traits
+    using difference_type = pid_t;
+    using value_type = pid_t;
+    using pointer = const pid_t*;
+    using reference = const pid_t&;
+    using iterator_category = std::input_iterator_tag;
+
+   private:
+    void Increment();
+
+    pid_t pid_ = -1;
+    std::unique_ptr<DIR, decltype(&closedir)> dir_;
+  };
+
+ public:
+  PidIterator begin() { return opendir("/proc"); }
+  PidIterator end() { return nullptr; }
+};
+
+}  // namespace base
+}  // namespace android
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index 041586c..31823df 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>
@@ -54,24 +49,23 @@
                                         T max = std::numeric_limits<T>::max());
 
 // Sets the system property `key` to `value`.
-// Note that system property setting is inherently asynchronous so a return value of `true`
-// isn't particularly meaningful, and immediately reading back the value won't necessarily
-// tell you whether or not your call succeeded. A `false` return value definitely means failure.
 bool SetProperty(const std::string& key, const std::string& value);
 
 // 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/result.h b/base/include/android-base/result.h
new file mode 100644
index 0000000..1b763af
--- /dev/null
+++ b/base/include/android-base/result.h
@@ -0,0 +1,205 @@
+/*
+ * 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.
+//
+// Result<void> is the correct return type for a function that either returns successfully or
+// returns an error value.  Returning {} from a function that returns Result<void> 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.
+//
+// Errorf and ErrnoErrorf accept the format string syntax of the fmblib (https://fmt.dev).
+// Errorf("{} errors", num) is equivalent to Error() << num << " errors".
+//
+// ResultError can be used in the ostream and when using Error/Errorf 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. Note that when the outer Result<T> is created with ErrnoError/ErrnoErrorf then
+// the errno from the inner ResultError is not passed. Also when multiple ResultError objects are
+// used, the errno of the last one is respected.
+//
+// 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 Errorf("SomeOtherCppFunction {} failed", input);
+//   }
+//   if (!c_api_function(output)) {
+//     return ErrnoErrorf("c_api_function {} failed", output);
+//   }
+//   return output;
+// }
+//
+// auto output = CalculateResult(input);
+// if (!output) return Error() << "CalculateResult failed: " << output.error();
+// UseOutput(*output);
+
+#pragma once
+
+#include <errno.h>
+
+#include <sstream>
+#include <string>
+
+#include "android-base/expected.h"
+#include "android-base/format.h"
+
+namespace android {
+namespace base {
+
+struct ResultError {
+  template <typename T>
+  ResultError(T&& message, int code) : message_(std::forward<T>(message)), code_(code) {}
+
+  template <typename T>
+  operator android::base::expected<T, ResultError>() {
+    return android::base::unexpected(ResultError(message_, code_));
+  }
+
+  std::string message() const { return message_; }
+  int code() const { return code_; }
+
+ private:
+  std::string message_;
+  int code_;
+};
+
+inline bool operator==(const ResultError& lhs, const ResultError& rhs) {
+  return lhs.message() == rhs.message() && lhs.code() == rhs.code();
+}
+
+inline bool operator!=(const ResultError& lhs, const ResultError& rhs) {
+  return !(lhs == rhs);
+}
+
+inline std::ostream& operator<<(std::ostream& os, const ResultError& t) {
+  os << t.message();
+  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>
+  operator android::base::expected<T, ResultError>() {
+    return android::base::unexpected(ResultError(str(), errno_));
+  }
+
+  template <typename T>
+  Error& operator<<(T&& t) {
+    if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
+      errno_ = t.code();
+      return (*this) << t.message();
+    }
+    int saved = errno;
+    ss_ << t;
+    errno = saved;
+    return *this;
+  }
+
+  const std::string str() const {
+    std::string str = ss_.str();
+    if (append_errno_) {
+      if (str.empty()) {
+        return strerror(errno_);
+      }
+      return std::move(str) + ": " + strerror(errno_);
+    }
+    return str;
+  }
+
+  Error(const Error&) = delete;
+  Error(Error&&) = delete;
+  Error& operator=(const Error&) = delete;
+  Error& operator=(Error&&) = delete;
+
+  template <typename... Args>
+  friend Error Errorf(const char* fmt, const Args&... args);
+
+  template <typename... Args>
+  friend Error ErrnoErrorf(const char* fmt, const Args&... args);
+
+ private:
+  Error(bool append_errno, int errno_to_append, const std::string& message)
+      : errno_(errno_to_append), append_errno_(append_errno) {
+    (*this) << message;
+  }
+
+  std::stringstream ss_;
+  int errno_;
+  const bool append_errno_;
+};
+
+inline Error ErrnoError() {
+  return Error(errno);
+}
+
+inline int ErrorCode(int code) {
+  return code;
+}
+
+// Return the error code of the last ResultError object, if any.
+// Otherwise, return `code` as it is.
+template <typename T, typename... Args>
+inline int ErrorCode(int code, T&& t, const Args&... args) {
+  if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
+    return ErrorCode(t.code(), args...);
+  }
+  return ErrorCode(code, args...);
+}
+
+template <typename... Args>
+inline Error Errorf(const char* fmt, const Args&... args) {
+  return Error(false, ErrorCode(0, args...), fmt::format(fmt, args...));
+}
+
+template <typename... Args>
+inline Error ErrnoErrorf(const char* fmt, const Args&... args) {
+  return Error(true, errno, fmt::format(fmt, args...));
+}
+
+template <typename T>
+using Result = android::base::expected<T, ResultError>;
+
+}  // namespace base
+}  // namespace android
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
index c314e02..5a224d6 100644
--- a/base/include/android-base/scopeguard.h
+++ b/base/include/android-base/scopeguard.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_SCOPEGUARD_H
-#define ANDROID_BASE_SCOPEGUARD_H
+#pragma once
 
 #include <utility>  // for std::move, std::forward
 
@@ -29,7 +28,7 @@
  public:
   ScopeGuard(F&& f) : f_(std::forward<F>(f)), active_(true) {}
 
-  ScopeGuard(ScopeGuard&& that) : f_(std::move(that.f_)), active_(that.active_) {
+  ScopeGuard(ScopeGuard&& that) noexcept : f_(std::move(that.f_)), active_(that.active_) {
     that.active_ = false;
   }
 
@@ -66,5 +65,3 @@
 
 }  // 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 1fd6297..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 ANDROID_BASE_FORMAT_ARCHETYPE __printf__
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-#undef ANDROID_BASE_FORMAT_ARCHETYPE
-#define ANDROID_BASE_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__(ANDROID_BASE_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__(ANDROID_BASE_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__(ANDROID_BASE_FORMAT_ARCHETYPE, 2, 0)));
-
-#undef ANDROID_BASE_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 4d9fa34..b1c22c9 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_STRINGS_H
-#define ANDROID_BASE_STRINGS_H
+#pragma once
 
 #include <sstream>
 #include <string>
+#include <string_view>
 #include <vector>
 
 namespace android {
@@ -57,23 +57,33 @@
 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);
+bool StartsWith(std::string_view s, std::string_view prefix);
+bool StartsWith(std::string_view s, char prefix);
+bool StartsWithIgnoreCase(std::string_view s, std::string_view 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);
+bool EndsWith(std::string_view s, std::string_view suffix);
+bool EndsWith(std::string_view s, char suffix);
+bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix);
 
 // Tests whether 'lhs' equals 'rhs', ignoring case.
-bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs);
+bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs);
+
+// Removes `prefix` from the start of the given string and returns true (if
+// it was present), false otherwise.
+inline bool ConsumePrefix(std::string_view* s, std::string_view prefix) {
+  if (!StartsWith(*s, prefix)) return false;
+  s->remove_prefix(prefix.size());
+  return true;
+}
+
+// Removes `suffix` from the end of the given string and returns true (if
+// it was present), false otherwise.
+inline bool ConsumeSuffix(std::string_view* s, std::string_view suffix) {
+  if (!EndsWith(*s, suffix)) return false;
+  s->remove_suffix(suffix.size());
+  return true;
+}
 
 }  // 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 2edafe3..b20f278 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -14,61 +14,43 @@
  * 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/file.h>
 #include <android-base/macros.h>
 
-class TemporaryFile {
+class CapturedStdFd {
  public:
-  TemporaryFile();
-  explicit TemporaryFile(const std::string& tmp_dir);
-  ~TemporaryFile();
+  CapturedStdFd(int std_fd);
+  ~CapturedStdFd();
 
-  // Release the ownership of fd, caller is reponsible for closing the
-  // fd or stream properly.
-  int release();
+  std::string str();
 
-  int fd;
-  char path[1024];
+  void Start();
+  void Stop();
+  void Reset();
 
  private:
-  void init(const std::string& tmp_dir);
-
-  DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
-};
-
-class TemporaryDir {
- public:
-  TemporaryDir();
-  ~TemporaryDir();
-
-  char path[1024];
-
- private:
-  bool init(const std::string& tmp_dir);
-
-  DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
-};
-
-class CapturedStderr {
- public:
-  CapturedStderr();
-  ~CapturedStderr();
-
   int fd() const;
 
- private:
-  void init();
-  void reset();
-
   TemporaryFile temp_file_;
-  int old_stderr_;
+  int std_fd_;
+  int old_fd_ = -1;
 
-  DISALLOW_COPY_AND_ASSIGN(CapturedStderr);
+  DISALLOW_COPY_AND_ASSIGN(CapturedStdFd);
+};
+
+class CapturedStderr : public CapturedStdFd {
+ public:
+  CapturedStderr() : CapturedStdFd(STDERR_FILENO) {}
+};
+
+class CapturedStdout : public CapturedStdFd {
+ public:
+  CapturedStdout() : CapturedStdFd(STDOUT_FILENO) {}
 };
 
 #define ASSERT_MATCH(str, pattern)                                             \
@@ -98,5 +80,3 @@
       ADD_FAILURE() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << (str); \
     }                                                                                             \
   } while (0)
-
-#endif  // ANDROID_BASE_TEST_UTILS_H
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
index 1307f0e..53fe6da 100644
--- a/base/include/android-base/thread_annotations.h
+++ b/base/include/android-base/thread_annotations.h
@@ -14,14 +14,11 @@
  * 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
+#include <mutex>
+
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
 
 #define CAPABILITY(x) \
       THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
@@ -110,4 +107,38 @@
 #define NO_THREAD_SAFETY_ANALYSIS \
       THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
 
-#endif  // ANDROID_BASE_THREAD_ANNOTATIONS_H
+namespace android {
+namespace base {
+
+// A class to help thread safety analysis deal with std::unique_lock and condition_variable.
+//
+// Clang's thread safety analysis currently doesn't perform alias analysis, so movable types
+// like std::unique_lock can't be marked with thread safety annotations. This helper allows
+// for manual assertion of lock state in a scope.
+//
+// For example:
+//
+//   std::mutex mutex;
+//   std::condition_variable cv;
+//   std::vector<int> vec GUARDED_BY(mutex);
+//
+//   int pop() {
+//     std::unique_lock lock(mutex);
+//     ScopedLockAssertion lock_assertion(mutex);
+//     cv.wait(lock, []() {
+//       ScopedLockAssertion lock_assertion(mutex);
+//       return !vec.empty();
+//     });
+//
+//     int result = vec.back();
+//     vec.pop_back();
+//     return result;
+//   }
+class SCOPED_CAPABILITY ScopedLockAssertion {
+ public:
+  ScopedLockAssertion(std::mutex& mutex) ACQUIRE(mutex) {}
+  ~ScopedLockAssertion() RELEASE() {}
+};
+
+}  // namespace base
+}  // namespace android
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 5d89271..3a02cff 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -14,15 +14,17 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_UNIQUE_FD_H
-#define ANDROID_BASE_UNIQUE_FD_H
+#pragma once
 
+#include <dirent.h>
+#include <errno.h>
 #include <fcntl.h>
 
 #if !defined(_WIN32)
 #include <sys/socket.h>
 #endif
 
+#include <stdio.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -43,10 +45,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
@@ -54,40 +81,92 @@
     // 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& operator=(unique_fd_impl&& s) {
-    reset(s.release());
+  unique_fd_impl(unique_fd_impl&& other) noexcept { reset(other.release()); }
+  unique_fd_impl& operator=(unique_fd_impl&& s) noexcept {
+    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_; }
-  operator int() const { return get(); }
+  int get() const { return fd_; }
+
+#if !defined(ANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION)
+  // unique_fd's operator int is dangerous, but we have way too much code that
+  // depends on it, so make this opt-in at first.
+  operator int() const { return get(); }  // NOLINT
+#endif
+
+  bool operator>=(int rhs) const { return get() >= rhs; }
+  bool operator<(int rhs) const { return get() < rhs; }
+  bool operator==(int rhs) const { return get() == rhs; }
+  bool operator!=(int rhs) const { return get() != rhs; }
+
+  // Catch bogus error checks (i.e.: "!fd" instead of "fd != -1").
+  bool operator!() const = delete;
 
   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) {
+    int previous_errno = errno;
+
+    if (fd_ != -1) {
+      close(fd_, this);
+    }
+
+    fd_ = new_value;
+    if (new_value != -1) {
+      tag(new_value, previous_tag, this);
+    }
+
+    errno = previous_errno;
+  }
+
+  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&);
@@ -98,22 +177,36 @@
 #if !defined(_WIN32)
 
 // Inline functions, so that they can be used header-only.
-inline bool Pipe(unique_fd* read, unique_fd* write) {
+template <typename Closer>
+inline bool Pipe(unique_fd_impl<Closer>* read, unique_fd_impl<Closer>* write,
+                 int flags = O_CLOEXEC) {
   int pipefd[2];
 
 #if defined(__linux__)
-  if (pipe2(pipefd, O_CLOEXEC) != 0) {
+  if (pipe2(pipefd, flags) != 0) {
     return false;
   }
 #else  // defined(__APPLE__)
+  if (flags & ~(O_CLOEXEC | O_NONBLOCK)) {
+    return false;
+  }
   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;
+  if (flags & O_CLOEXEC) {
+    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;
+    }
+  }
+  if (flags & O_NONBLOCK) {
+    if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 || fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
+      close(pipefd[0]);
+      close(pipefd[1]);
+      return false;
+    }
   }
 #endif
 
@@ -122,7 +215,9 @@
   return true;
 }
 
-inline bool Socketpair(int domain, int type, int protocol, unique_fd* left, unique_fd* right) {
+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;
@@ -132,23 +227,64 @@
   return true;
 }
 
-inline bool Socketpair(int type, unique_fd* left, unique_fd* right) {
+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);
 }
 
+// Using fdopen with unique_fd correctly is more annoying than it should be,
+// because fdopen doesn't close the file descriptor received upon failure.
+inline FILE* Fdopen(unique_fd&& ufd, const char* mode) {
+  int fd = ufd.release();
+  FILE* file = fdopen(fd, mode);
+  if (!file) {
+    close(fd);
+  }
+  return file;
+}
+
+// Using fdopendir with unique_fd correctly is more annoying than it should be,
+// because fdopen doesn't close the file descriptor received upon failure.
+inline DIR* Fdopendir(unique_fd&& ufd) {
+  int fd = ufd.release();
+  DIR* dir = fdopendir(fd);
+  if (dir == nullptr) {
+    close(fd);
+  }
+  return dir;
+}
+
 #endif  // !defined(_WIN32)
 
+// A wrapper type that can be implicitly constructed from either int or unique_fd.
+struct borrowed_fd {
+  /* implicit */ borrowed_fd(int fd) : fd_(fd) {}
+  template <typename T>
+  /* implicit */ borrowed_fd(const unique_fd_impl<T>& ufd) : fd_(ufd.get()) {}
+
+  int get() const { return fd_; }
+
+  bool operator>=(int rhs) const { return get() >= rhs; }
+  bool operator<(int rhs) const { return get() < rhs; }
+  bool operator==(int rhs) const { return get() == rhs; }
+  bool operator!=(int rhs) const { return get() != rhs; }
+
+ private:
+  int fd_ = -1;
+};
 }  // 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"
-  )));
+    __attribute__((__unavailable__("close called on unique_fd")));
 
-#endif  // ANDROID_BASE_UNIQUE_FD_H
+template <typename T>
+FILE* fdopen(const android::base::unique_fd_impl<T>&, const char* mode)
+    __attribute__((__unavailable__("fdopen takes ownership of the fd passed in; either dup the "
+                                   "unique_fd, or use android::base::Fdopen to pass ownership")));
+
+template <typename T>
+DIR* fdopendir(const android::base::unique_fd_impl<T>&) __attribute__((
+    __unavailable__("fdopendir takes ownership of the fd passed in; either dup the "
+                    "unique_fd, or use android::base::Fdopendir to pass ownership")));
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h
old mode 100755
new mode 100644
index c9cc1ab..1a414ec
--- a/base/include/android-base/utf8.h
+++ b/base/include/android-base/utf8.h
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_UTF8_H
-#define ANDROID_BASE_UTF8_H
+#pragma once
 
 #ifdef _WIN32
+#include <sys/types.h>
 #include <string>
 #else
 // Bring in prototypes for standard APIs so that we can import them into the utf8 namespace.
@@ -102,5 +102,3 @@
 }  // namespace utf8
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_UTF8_H
diff --git a/base/logging.cpp b/base/logging.cpp
index a31feef..f89168c 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>
 
@@ -52,73 +53,67 @@
 #include <unistd.h>
 #endif
 
+#include <android-base/file.h>
 #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__)
-  uint64_t tid;
-  pthread_threadid_np(NULL, &tid);
-  return tid;
-#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] = {};
 
   if (first) {
-    CHAR longname[MAX_PATH];
-    DWORD nchars = GetModuleFileNameA(nullptr, longname, arraysize(longname));
-    if ((nchars >= arraysize(longname)) || (nchars == 0)) {
-      // String truncation or some other error.
-      strcpy(progname, "<unknown>");
-    } else {
-      strcpy(progname, basename(longname));
-    }
+    snprintf(progname, sizeof(progname), "%s",
+             android::base::Basename(android::base::GetExecutablePath()).c_str());
     first = false;
   }
 
   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();
@@ -183,7 +178,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];
@@ -223,8 +218,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", tag ? tag : "nullptr", severity_char, timestamp,
-          getpid(), GetThreadId(), file, line, message);
+  fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n", tag ? tag : "nullptr", severity_char,
+          timestamp, getpid(), GetThreadId(), file, line, message);
+}
+
+void 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) {
@@ -341,22 +346,6 @@
   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 {
@@ -416,10 +405,6 @@
                        const char* tag, int error)
     : data_(new LogMessageData(file, line, id, severity, tag, error)) {}
 
-LogMessage::LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity,
-                       int error)
-    : LogMessage(file, line, id, severity, nullptr, error) {}
-
 LogMessage::~LogMessage() {
   // Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM.
   if (!WOULD_LOG(data_->GetSeverity())) {
@@ -432,6 +417,14 @@
   }
   std::string msg(data_->ToString());
 
+  if (data_->GetSeverity() == FATAL) {
+#ifdef __ANDROID__
+    // Set the bionic abort message early to avoid liblog doing it
+    // with the individual lines, so that we get the whole message.
+    android_set_abort_message(msg.c_str());
+#endif
+  }
+
   {
     // Do the actual logging with the lock held.
     std::lock_guard<std::mutex> lock(LoggingLock());
@@ -476,11 +469,6 @@
   }
 }
 
-void LogMessage::LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity,
-                         const char* message) {
-  LogLine(file, line, id, severity, nullptr, message);
-}
-
 LogSeverity GetMinimumLogSeverity() {
     return gMinimumLogSeverity;
 }
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index 5f689fa..3113fb4 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -206,12 +206,8 @@
 }
 #endif
 
-static void CheckMessage(const CapturedStderr& cap, android::base::LogSeverity severity,
+static void CheckMessage(const std::string& output, android::base::LogSeverity severity,
                          const char* expected, const char* expected_tag = nullptr) {
-  std::string output;
-  ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
-  android::base::ReadFdToString(cap.fd(), &output);
-
   // 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.
@@ -233,20 +229,28 @@
 #endif
 }
 
+static void CheckMessage(CapturedStderr& cap, android::base::LogSeverity severity,
+                         const char* expected, const char* expected_tag = nullptr) {
+  cap.Stop();
+  std::string output = cap.str();
+  return CheckMessage(output, severity, expected, expected_tag);
+}
 
-#define CHECK_LOG_STREAM_DISABLED(severity) \
-  { \
+#define CHECK_LOG_STREAM_DISABLED(severity)                      \
+  {                                                              \
     android::base::ScopedLogSeverity sls1(android::base::FATAL); \
-    CapturedStderr cap1; \
-    LOG_STREAM(severity) << "foo bar"; \
-    ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
-  } \
-  { \
+    CapturedStderr cap1;                                         \
+    LOG_STREAM(severity) << "foo bar";                           \
+    cap1.Stop();                                                 \
+    ASSERT_EQ("", cap1.str());                                   \
+  }                                                              \
+  {                                                              \
     android::base::ScopedLogSeverity sls1(android::base::FATAL); \
-    CapturedStderr cap1; \
-    LOG_STREAM(::android::base::severity) << "foo bar"; \
-    ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
-  } \
+    CapturedStderr cap1;                                         \
+    LOG_STREAM(::android::base::severity) << "foo bar";          \
+    cap1.Stop();                                                 \
+    ASSERT_EQ("", cap1.str());                                   \
+  }
 
 #define CHECK_LOG_STREAM_ENABLED(severity) \
   { \
@@ -267,7 +271,7 @@
 }
 
 TEST(logging, LOG_STREAM_FATAL_WITHOUT_ABORT_enabled) {
-  CHECK_LOG_STREAM_ENABLED(FATAL_WITHOUT_ABORT);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(FATAL_WITHOUT_ABORT));
 }
 
 TEST(logging, LOG_STREAM_ERROR_disabled) {
@@ -275,7 +279,7 @@
 }
 
 TEST(logging, LOG_STREAM_ERROR_enabled) {
-  CHECK_LOG_STREAM_ENABLED(ERROR);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(ERROR));
 }
 
 TEST(logging, LOG_STREAM_WARNING_disabled) {
@@ -283,7 +287,7 @@
 }
 
 TEST(logging, LOG_STREAM_WARNING_enabled) {
-  CHECK_LOG_STREAM_ENABLED(WARNING);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(WARNING));
 }
 
 TEST(logging, LOG_STREAM_INFO_disabled) {
@@ -291,7 +295,7 @@
 }
 
 TEST(logging, LOG_STREAM_INFO_enabled) {
-  CHECK_LOG_STREAM_ENABLED(INFO);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(INFO));
 }
 
 TEST(logging, LOG_STREAM_DEBUG_disabled) {
@@ -299,7 +303,7 @@
 }
 
 TEST(logging, LOG_STREAM_DEBUG_enabled) {
-  CHECK_LOG_STREAM_ENABLED(DEBUG);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(DEBUG));
 }
 
 TEST(logging, LOG_STREAM_VERBOSE_disabled) {
@@ -307,26 +311,27 @@
 }
 
 TEST(logging, LOG_STREAM_VERBOSE_enabled) {
-  CHECK_LOG_STREAM_ENABLED(VERBOSE);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(VERBOSE));
 }
 
 #undef CHECK_LOG_STREAM_DISABLED
 #undef CHECK_LOG_STREAM_ENABLED
 
-
-#define CHECK_LOG_DISABLED(severity) \
-  { \
+#define CHECK_LOG_DISABLED(severity)                             \
+  {                                                              \
     android::base::ScopedLogSeverity sls1(android::base::FATAL); \
-    CapturedStderr cap1; \
-    LOG(severity) << "foo bar"; \
-    ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
-  } \
-  { \
+    CapturedStderr cap1;                                         \
+    LOG(severity) << "foo bar";                                  \
+    cap1.Stop();                                                 \
+    ASSERT_EQ("", cap1.str());                                   \
+  }                                                              \
+  {                                                              \
     android::base::ScopedLogSeverity sls1(android::base::FATAL); \
-    CapturedStderr cap1; \
-    LOG(::android::base::severity) << "foo bar"; \
-    ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
-  } \
+    CapturedStderr cap1;                                         \
+    LOG(::android::base::severity) << "foo bar";                 \
+    cap1.Stop();                                                 \
+    ASSERT_EQ("", cap1.str());                                   \
+  }
 
 #define CHECK_LOG_ENABLED(severity) \
   { \
@@ -352,7 +357,7 @@
 }
 
 TEST(logging, LOG_FATAL_WITHOUT_ABORT_enabled) {
-  CHECK_LOG_ENABLED(FATAL_WITHOUT_ABORT);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(FATAL_WITHOUT_ABORT));
 }
 
 TEST(logging, LOG_ERROR_disabled) {
@@ -360,7 +365,7 @@
 }
 
 TEST(logging, LOG_ERROR_enabled) {
-  CHECK_LOG_ENABLED(ERROR);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(ERROR));
 }
 
 TEST(logging, LOG_WARNING_disabled) {
@@ -368,7 +373,7 @@
 }
 
 TEST(logging, LOG_WARNING_enabled) {
-  CHECK_LOG_ENABLED(WARNING);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(WARNING));
 }
 
 TEST(logging, LOG_INFO_disabled) {
@@ -376,7 +381,7 @@
 }
 
 TEST(logging, LOG_INFO_enabled) {
-  CHECK_LOG_ENABLED(INFO);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(INFO));
 }
 
 TEST(logging, LOG_DEBUG_disabled) {
@@ -384,7 +389,7 @@
 }
 
 TEST(logging, LOG_DEBUG_enabled) {
-  CHECK_LOG_ENABLED(DEBUG);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(DEBUG));
 }
 
 TEST(logging, LOG_VERBOSE_disabled) {
@@ -392,28 +397,28 @@
 }
 
 TEST(logging, LOG_VERBOSE_enabled) {
-  CHECK_LOG_ENABLED(VERBOSE);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(VERBOSE));
 }
 
 #undef CHECK_LOG_DISABLED
 #undef CHECK_LOG_ENABLED
 
-
 TEST(logging, LOG_complex_param) {
-#define CHECK_LOG_COMBINATION(use_scoped_log_severity_info, use_logging_severity_info)             \
-  {                                                                                                \
-    android::base::ScopedLogSeverity sls(                                                          \
-        (use_scoped_log_severity_info) ? ::android::base::INFO : ::android::base::WARNING);        \
-    CapturedStderr cap;                                                                            \
-    LOG((use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING)            \
-        << "foobar";                                                                               \
-    if ((use_scoped_log_severity_info) || !(use_logging_severity_info)) {                          \
-      CheckMessage(cap,                                                                            \
-                   (use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING, \
-                   "foobar");                                                                      \
-    } else {                                                                                       \
-      ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_CUR));                                                  \
-    }                                                                                              \
+#define CHECK_LOG_COMBINATION(use_scoped_log_severity_info, use_logging_severity_info)         \
+  {                                                                                            \
+    android::base::ScopedLogSeverity sls(                                                      \
+        (use_scoped_log_severity_info) ? ::android::base::INFO : ::android::base::WARNING);    \
+    CapturedStderr cap;                                                                        \
+    LOG((use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING)        \
+        << "foobar";                                                                           \
+    if ((use_scoped_log_severity_info) || !(use_logging_severity_info)) {                      \
+      ASSERT_NO_FATAL_FAILURE(CheckMessage(                                                    \
+          cap, (use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING, \
+          "foobar"));                                                                          \
+    } else {                                                                                   \
+      cap.Stop();                                                                              \
+      ASSERT_EQ("", cap.str());                                                                \
+    }                                                                                          \
   }
 
   CHECK_LOG_COMBINATION(false,false);
@@ -431,7 +436,7 @@
   LOG(INFO) << (errno = 67890);
   EXPECT_EQ(12345, errno) << "errno was not restored";
 
-  CheckMessage(cap, android::base::INFO, "67890");
+  ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::INFO, "67890"));
 }
 
 TEST(logging, PLOG_does_not_clobber_errno) {
@@ -440,7 +445,7 @@
   PLOG(INFO) << (errno = 67890);
   EXPECT_EQ(12345, errno) << "errno was not restored";
 
-  CheckMessage(cap, android::base::INFO, "67890");
+  ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::INFO, "67890"));
 }
 
 TEST(logging, LOG_does_not_have_dangling_if) {
@@ -466,19 +471,21 @@
   EXPECT_FALSE(flag) << "LOG macro probably has a dangling if with no else";
 }
 
-#define CHECK_PLOG_DISABLED(severity) \
-  { \
+#define CHECK_PLOG_DISABLED(severity)                            \
+  {                                                              \
     android::base::ScopedLogSeverity sls1(android::base::FATAL); \
-    CapturedStderr cap1; \
-    PLOG(severity) << "foo bar"; \
-    ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
-  } \
-  { \
+    CapturedStderr cap1;                                         \
+    PLOG(severity) << "foo bar";                                 \
+    cap1.Stop();                                                 \
+    ASSERT_EQ("", cap1.str());                                   \
+  }                                                              \
+  {                                                              \
     android::base::ScopedLogSeverity sls1(android::base::FATAL); \
-    CapturedStderr cap1; \
-    PLOG(severity) << "foo bar"; \
-    ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
-  } \
+    CapturedStderr cap1;                                         \
+    PLOG(severity) << "foo bar";                                 \
+    cap1.Stop();                                                 \
+    ASSERT_EQ("", cap1.str());                                   \
+  }
 
 #define CHECK_PLOG_ENABLED(severity) \
   { \
@@ -506,7 +513,7 @@
 }
 
 TEST(logging, PLOG_FATAL_WITHOUT_ABORT_enabled) {
-  CHECK_PLOG_ENABLED(FATAL_WITHOUT_ABORT);
+  ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(FATAL_WITHOUT_ABORT));
 }
 
 TEST(logging, PLOG_ERROR_disabled) {
@@ -514,7 +521,7 @@
 }
 
 TEST(logging, PLOG_ERROR_enabled) {
-  CHECK_PLOG_ENABLED(ERROR);
+  ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(ERROR));
 }
 
 TEST(logging, PLOG_WARNING_disabled) {
@@ -522,7 +529,7 @@
 }
 
 TEST(logging, PLOG_WARNING_enabled) {
-  CHECK_PLOG_ENABLED(WARNING);
+  ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(WARNING));
 }
 
 TEST(logging, PLOG_INFO_disabled) {
@@ -530,7 +537,7 @@
 }
 
 TEST(logging, PLOG_INFO_enabled) {
-  CHECK_PLOG_ENABLED(INFO);
+  ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(INFO));
 }
 
 TEST(logging, PLOG_DEBUG_disabled) {
@@ -538,7 +545,7 @@
 }
 
 TEST(logging, PLOG_DEBUG_enabled) {
-  CHECK_PLOG_ENABLED(DEBUG);
+  ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(DEBUG));
 }
 
 TEST(logging, PLOG_VERBOSE_disabled) {
@@ -546,7 +553,7 @@
 }
 
 TEST(logging, PLOG_VERBOSE_enabled) {
-  CHECK_PLOG_ENABLED(VERBOSE);
+  ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(VERBOSE));
 }
 
 #undef CHECK_PLOG_DISABLED
@@ -559,7 +566,7 @@
   CapturedStderr cap;
   errno = ENOENT;
   UNIMPLEMENTED(ERROR);
-  CheckMessage(cap, android::base::ERROR, expected.c_str());
+  ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::ERROR, expected.c_str()));
 }
 
 static void NoopAborter(const char* msg ATTRIBUTE_UNUSED) {
@@ -567,17 +574,19 @@
 }
 
 TEST(logging, LOG_FATAL_NOOP_ABORTER) {
+  CapturedStderr cap;
   {
     android::base::SetAborter(NoopAborter);
 
     android::base::ScopedLogSeverity sls(android::base::ERROR);
-    CapturedStderr cap;
     LOG(FATAL) << "foobar";
-    CheckMessage(cap, android::base::FATAL, "foobar");
-    CheckMessage(cap, android::base::ERROR, "called noop");
+    cap.Stop();
 
     android::base::SetAborter(android::base::DefaultAborter);
   }
+  std::string output = cap.str();
+  ASSERT_NO_FATAL_FAILURE(CheckMessage(output, android::base::FATAL, "foobar"));
+  ASSERT_NO_FATAL_FAILURE(CheckMessage(output, android::base::ERROR, "called noop"));
 
   ASSERT_DEATH({SuppressAbortUI(); LOG(FATAL) << "foobar";}, "foobar");
 }
@@ -621,5 +630,21 @@
     LOG(INFO) << expected_msg;
     android::base::SetDefaultTag(old_default_tag);
   }
-  CheckMessage(cap, android::base::LogSeverity::INFO, expected_msg, expected_tag);
+  ASSERT_NO_FATAL_FAILURE(
+      CheckMessage(cap, android::base::LogSeverity::INFO, expected_msg, expected_tag));
+}
+
+TEST(logging, StdioLogger) {
+  CapturedStderr cap_err;
+  CapturedStdout cap_out;
+  android::base::SetLogger(android::base::StdioLogger);
+  LOG(INFO) << "out";
+  LOG(ERROR) << "err";
+  cap_err.Stop();
+  cap_out.Stop();
+
+  // For INFO we expect just the literal "out\n".
+  ASSERT_EQ("out\n", cap_out.str());
+  // Whereas ERROR logging includes the program name.
+  ASSERT_EQ(android::base::Basename(android::base::GetExecutablePath()) + ": err\n", cap_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/mapped_file.cpp b/base/mapped_file.cpp
new file mode 100644
index 0000000..f60de56
--- /dev/null
+++ b/base/mapped_file.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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/mapped_file.h"
+
+#include <errno.h>
+
+#include "android-base/unique_fd.h"
+
+namespace android {
+namespace base {
+
+static off64_t InitPageSize() {
+#if defined(_WIN32)
+  SYSTEM_INFO si;
+  GetSystemInfo(&si);
+  return si.dwAllocationGranularity;
+#else
+  return sysconf(_SC_PAGE_SIZE);
+#endif
+}
+
+std::unique_ptr<MappedFile> MappedFile::FromFd(borrowed_fd fd, off64_t offset, size_t length,
+                                               int prot) {
+  static off64_t page_size = InitPageSize();
+  size_t slop = offset % page_size;
+  off64_t file_offset = offset - slop;
+  off64_t file_length = length + slop;
+
+#if defined(_WIN32)
+  HANDLE handle =
+      CreateFileMapping(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), nullptr,
+                        (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
+  if (handle == nullptr) {
+    // http://b/119818070 "app crashes when reading asset of zero length".
+    // Return a MappedFile that's only valid for reading the size.
+    if (length == 0) {
+      return std::unique_ptr<MappedFile>(new MappedFile{nullptr, 0, 0, nullptr});
+    }
+    return nullptr;
+  }
+  void* base = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ, 0,
+                             file_offset, file_length);
+  if (base == nullptr) {
+    CloseHandle(handle);
+    return nullptr;
+  }
+  return std::unique_ptr<MappedFile>(
+      new MappedFile{static_cast<char*>(base), length, slop, handle});
+#else
+  void* base = mmap(nullptr, file_length, prot, MAP_SHARED, fd.get(), file_offset);
+  if (base == MAP_FAILED) {
+    // http://b/119818070 "app crashes when reading asset of zero length".
+    // mmap fails with EINVAL for a zero length region.
+    if (errno == EINVAL && length == 0) {
+      return std::unique_ptr<MappedFile>(new MappedFile{nullptr, 0, 0});
+    }
+    return nullptr;
+  }
+  return std::unique_ptr<MappedFile>(new MappedFile{static_cast<char*>(base), length, slop});
+#endif
+}
+
+MappedFile::~MappedFile() {
+#if defined(_WIN32)
+  if (base_ != nullptr) UnmapViewOfFile(base_);
+  if (handle_ != nullptr) CloseHandle(handle_);
+#else
+  if (base_ != nullptr) munmap(base_, size_ + offset_);
+#endif
+
+  base_ = nullptr;
+  offset_ = size_ = 0;
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/mapped_file_test.cpp b/base/mapped_file_test.cpp
new file mode 100644
index 0000000..cfde73c
--- /dev/null
+++ b/base/mapped_file_test.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/mapped_file.h"
+
+#include <gtest/gtest.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "android-base/file.h"
+
+TEST(mapped_file, smoke) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+  ASSERT_TRUE(android::base::WriteStringToFd("hello world", tf.fd));
+
+  auto m = android::base::MappedFile::FromFd(tf.fd, 3, 2, PROT_READ);
+  ASSERT_EQ(2u, m->size());
+  ASSERT_EQ('l', m->data()[0]);
+  ASSERT_EQ('o', m->data()[1]);
+}
+
+TEST(mapped_file, zero_length_mapping) {
+  // http://b/119818070 "app crashes when reading asset of zero length".
+  // mmap fails with EINVAL for a zero length region.
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  auto m = android::base::MappedFile::FromFd(tf.fd, 4096, 0, PROT_READ);
+  ASSERT_EQ(0u, m->size());
+}
diff --git a/base/parsedouble_test.cpp b/base/parsedouble_test.cpp
index 8734c42..ec3c10c 100644
--- a/base/parsedouble_test.cpp
+++ b/base/parsedouble_test.cpp
@@ -18,7 +18,7 @@
 
 #include <gtest/gtest.h>
 
-TEST(parsedouble, smoke) {
+TEST(parsedouble, double_smoke) {
   double d;
   ASSERT_FALSE(android::base::ParseDouble("", &d));
   ASSERT_FALSE(android::base::ParseDouble("x", &d));
@@ -35,4 +35,33 @@
   ASSERT_FALSE(android::base::ParseDouble("3.0", &d, -1.0, 2.0));
   ASSERT_TRUE(android::base::ParseDouble("1.0", &d, 0.0, 2.0));
   ASSERT_DOUBLE_EQ(1.0, d);
+
+  ASSERT_FALSE(android::base::ParseDouble("123.4x", nullptr));
+  ASSERT_TRUE(android::base::ParseDouble("-123.4", nullptr));
+  ASSERT_FALSE(android::base::ParseDouble("3.0", nullptr, -1.0, 2.0));
+  ASSERT_TRUE(android::base::ParseDouble("1.0", nullptr, 0.0, 2.0));
+}
+
+TEST(parsedouble, float_smoke) {
+  float f;
+  ASSERT_FALSE(android::base::ParseFloat("", &f));
+  ASSERT_FALSE(android::base::ParseFloat("x", &f));
+  ASSERT_FALSE(android::base::ParseFloat("123.4x", &f));
+
+  ASSERT_TRUE(android::base::ParseFloat("123.4", &f));
+  ASSERT_FLOAT_EQ(123.4, f);
+  ASSERT_TRUE(android::base::ParseFloat("-123.4", &f));
+  ASSERT_FLOAT_EQ(-123.4, f);
+
+  ASSERT_TRUE(android::base::ParseFloat("0", &f, 0.0));
+  ASSERT_FLOAT_EQ(0.0, f);
+  ASSERT_FALSE(android::base::ParseFloat("0", &f, 1e-9));
+  ASSERT_FALSE(android::base::ParseFloat("3.0", &f, -1.0, 2.0));
+  ASSERT_TRUE(android::base::ParseFloat("1.0", &f, 0.0, 2.0));
+  ASSERT_FLOAT_EQ(1.0, f);
+
+  ASSERT_FALSE(android::base::ParseFloat("123.4x", nullptr));
+  ASSERT_TRUE(android::base::ParseFloat("-123.4", nullptr));
+  ASSERT_FALSE(android::base::ParseFloat("3.0", nullptr, -1.0, 2.0));
+  ASSERT_TRUE(android::base::ParseFloat("1.0", nullptr, 0.0, 2.0));
 }
diff --git a/base/parseint_test.cpp b/base/parseint_test.cpp
index 483b1d3..e449c33 100644
--- a/base/parseint_test.cpp
+++ b/base/parseint_test.cpp
@@ -16,17 +16,30 @@
 
 #include "android-base/parseint.h"
 
+#include <errno.h>
+
 #include <gtest/gtest.h>
 
 TEST(parseint, signed_smoke) {
+  errno = 0;
   int i = 0;
   ASSERT_FALSE(android::base::ParseInt("x", &i));
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
   ASSERT_FALSE(android::base::ParseInt("123x", &i));
+  ASSERT_EQ(EINVAL, errno);
 
   ASSERT_TRUE(android::base::ParseInt("123", &i));
   ASSERT_EQ(123, i);
+  ASSERT_EQ(0, errno);
+  i = 0;
+  EXPECT_TRUE(android::base::ParseInt("  123", &i));
+  EXPECT_EQ(123, i);
   ASSERT_TRUE(android::base::ParseInt("-123", &i));
   ASSERT_EQ(-123, i);
+  i = 0;
+  EXPECT_TRUE(android::base::ParseInt("  -123", &i));
+  EXPECT_EQ(-123, i);
 
   short s = 0;
   ASSERT_TRUE(android::base::ParseInt("1234", &s));
@@ -34,18 +47,43 @@
 
   ASSERT_TRUE(android::base::ParseInt("12", &i, 0, 15));
   ASSERT_EQ(12, i);
+  errno = 0;
   ASSERT_FALSE(android::base::ParseInt("-12", &i, 0, 15));
+  ASSERT_EQ(ERANGE, errno);
+  errno = 0;
   ASSERT_FALSE(android::base::ParseInt("16", &i, 0, 15));
+  ASSERT_EQ(ERANGE, errno);
+
+  errno = 0;
+  ASSERT_FALSE(android::base::ParseInt<int>("x", nullptr));
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  ASSERT_FALSE(android::base::ParseInt<int>("123x", nullptr));
+  ASSERT_EQ(EINVAL, errno);
+  ASSERT_TRUE(android::base::ParseInt<int>("1234", nullptr));
 }
 
 TEST(parseint, unsigned_smoke) {
+  errno = 0;
   unsigned int i = 0u;
   ASSERT_FALSE(android::base::ParseUint("x", &i));
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
   ASSERT_FALSE(android::base::ParseUint("123x", &i));
+  ASSERT_EQ(EINVAL, errno);
 
   ASSERT_TRUE(android::base::ParseUint("123", &i));
   ASSERT_EQ(123u, i);
+  ASSERT_EQ(0, errno);
+  i = 0u;
+  EXPECT_TRUE(android::base::ParseUint("  123", &i));
+  EXPECT_EQ(123u, i);
+  errno = 0;
   ASSERT_FALSE(android::base::ParseUint("-123", &i));
+  EXPECT_EQ(EINVAL, errno);
+  errno = 0;
+  EXPECT_FALSE(android::base::ParseUint("  -123", &i));
+  EXPECT_EQ(EINVAL, errno);
 
   unsigned short s = 0u;
   ASSERT_TRUE(android::base::ParseUint("1234", &s));
@@ -53,8 +91,28 @@
 
   ASSERT_TRUE(android::base::ParseUint("12", &i, 15u));
   ASSERT_EQ(12u, i);
+  errno = 0;
   ASSERT_FALSE(android::base::ParseUint("-12", &i, 15u));
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
   ASSERT_FALSE(android::base::ParseUint("16", &i, 15u));
+  ASSERT_EQ(ERANGE, errno);
+
+  errno = 0;
+  ASSERT_FALSE(android::base::ParseUint<unsigned short>("x", nullptr));
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  ASSERT_FALSE(android::base::ParseUint<unsigned short>("123x", nullptr));
+  ASSERT_EQ(EINVAL, errno);
+  ASSERT_TRUE(android::base::ParseUint<unsigned short>("1234", nullptr));
+
+  errno = 0;
+  unsigned long long int lli;
+  EXPECT_FALSE(android::base::ParseUint("-123", &lli));
+  EXPECT_EQ(EINVAL, errno);
+  errno = 0;
+  EXPECT_FALSE(android::base::ParseUint("  -123", &lli));
+  EXPECT_EQ(EINVAL, errno);
 }
 
 TEST(parseint, no_implicit_octal) {
@@ -71,10 +129,16 @@
   int i = 0;
   ASSERT_TRUE(android::base::ParseInt("0x123", &i));
   ASSERT_EQ(0x123, i);
+  i = 0;
+  EXPECT_TRUE(android::base::ParseInt("  0x123", &i));
+  EXPECT_EQ(0x123, i);
 
   unsigned int u = 0u;
   ASSERT_TRUE(android::base::ParseUint("0x123", &u));
   ASSERT_EQ(0x123u, u);
+  u = 0u;
+  EXPECT_TRUE(android::base::ParseUint("  0x123", &u));
+  EXPECT_EQ(0x123u, u);
 }
 
 TEST(parseint, string) {
@@ -93,6 +157,47 @@
   ASSERT_EQ(123, i);
 
   unsigned int u = 123u;
-  ASSERT_FALSE(android::base::ParseInt("456x", &u));
+  ASSERT_FALSE(android::base::ParseUint("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/process.cpp b/base/process.cpp
new file mode 100644
index 0000000..b8cabf6
--- /dev/null
+++ b/base/process.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/process.h"
+
+namespace android {
+namespace base {
+
+void AllPids::PidIterator::Increment() {
+  if (!dir_) {
+    return;
+  }
+
+  dirent* de;
+  while ((de = readdir(dir_.get())) != nullptr) {
+    pid_t pid = atoi(de->d_name);
+    if (pid != 0) {
+      pid_ = pid;
+      return;
+    }
+  }
+  pid_ = -1;
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/process_test.cpp b/base/process_test.cpp
new file mode 100644
index 0000000..056f667
--- /dev/null
+++ b/base/process_test.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/process.h"
+
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+TEST(process, find_ourselves) {
+#if defined(__linux__)
+  bool found_our_pid = false;
+  for (const auto& pid : android::base::AllPids{}) {
+    if (pid == getpid()) {
+      found_our_pid = true;
+    }
+  }
+
+  EXPECT_TRUE(found_our_pid);
+
+#endif
+}
diff --git a/base/properties.cpp b/base/properties.cpp
index 6cf43f9..d5a5918 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -14,16 +14,18 @@
  * 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>
@@ -31,24 +33,6 @@
 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;
-
-  std::string property_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);
-
-  // 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 GetBoolProperty(const std::string& key, bool default_value) {
   std::string value = GetProperty(key, "");
   if (value == "1" || value == "y" || value == "yes" || value == "on" || value == "true") {
@@ -85,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;
@@ -175,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/result_test.cpp b/base/result_test.cpp
new file mode 100644
index 0000000..2ee4057
--- /dev/null
+++ b/base/result_test.cpp
@@ -0,0 +1,422 @@
+/*
+ * 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/result.h"
+
+#include "errno.h"
+
+#include <istream>
+#include <string>
+
+#include <gtest/gtest.h>
+
+using namespace std::string_literals;
+
+namespace android {
+namespace base {
+
+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_void) {
+  Result<void> ok = {};
+  EXPECT_TRUE(ok);
+  ok.value();  // should not crash
+  ASSERT_DEATH(ok.error(), "");
+
+  Result<void> fail = Error() << "failure" << 1;
+  EXPECT_FALSE(fail);
+  EXPECT_EQ("failure1", fail.error().message());
+  EXPECT_EQ(0, fail.error().code());
+  EXPECT_TRUE(ok != fail);
+  ASSERT_DEATH(fail.value(), "");
+
+  auto test = [](bool ok) -> Result<void> {
+    if (ok) return {};
+    else return Error() << "failure" << 1;
+  };
+  EXPECT_TRUE(test(true));
+  EXPECT_FALSE(test(false));
+  test(true).value();  // should not crash
+  ASSERT_DEATH(test(true).error(), "");
+  ASSERT_DEATH(test(false).value(), "");
+  EXPECT_EQ("failure1", test(false).error().message());
+}
+
+TEST(result, result_error) {
+  Result<void> result = Error() << "failure" << 1;
+  ASSERT_FALSE(result);
+  ASSERT_FALSE(result.has_value());
+
+  EXPECT_EQ(0, result.error().code());
+  EXPECT_EQ("failure1", result.error().message());
+}
+
+TEST(result, result_error_empty) {
+  Result<void> result = Error();
+  ASSERT_FALSE(result);
+  ASSERT_FALSE(result.has_value());
+
+  EXPECT_EQ(0, result.error().code());
+  EXPECT_EQ("", result.error().message());
+}
+
+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<void> { return Error() << "failure" << 1; };
+  ASSERT_FALSE(MakeRvalueErrorResult());
+  ASSERT_FALSE(MakeRvalueErrorResult().has_value());
+
+  EXPECT_EQ(0, MakeRvalueErrorResult().error().code());
+  EXPECT_EQ("failure1", MakeRvalueErrorResult().error().message());
+}
+
+TEST(result, result_errno_error) {
+  constexpr int test_errno = 6;
+  errno = test_errno;
+  Result<void> result = ErrnoError() << "failure" << 1;
+
+  ASSERT_FALSE(result);
+  ASSERT_FALSE(result.has_value());
+
+  EXPECT_EQ(test_errno, result.error().code());
+  EXPECT_EQ("failure1: "s + strerror(test_errno), result.error().message());
+}
+
+TEST(result, result_errno_error_no_text) {
+  constexpr int test_errno = 6;
+  errno = test_errno;
+  Result<void> result = ErrnoError();
+
+  ASSERT_FALSE(result);
+  ASSERT_FALSE(result.has_value());
+
+  EXPECT_EQ(test_errno, result.error().code());
+  EXPECT_EQ(strerror(test_errno), result.error().message());
+}
+
+TEST(result, result_error_from_other_result) {
+  auto error_text = "test error"s;
+  Result<void> 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, result2.error().code());
+  EXPECT_EQ(error_text, result2.error().message());
+}
+
+TEST(result, result_error_through_ostream) {
+  auto error_text = "test error"s;
+  Result<void> 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, result2.error().code());
+  EXPECT_EQ(error_text, result2.error().message());
+}
+
+TEST(result, result_errno_error_through_ostream) {
+  auto error_text = "test error"s;
+  constexpr int test_errno = 6;
+  errno = 6;
+  Result<void> 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, result2.error().code());
+  EXPECT_EQ(error_text + ": " + strerror(test_errno), result2.error().message());
+}
+
+TEST(result, constructor_forwarding) {
+  auto result = Result<std::string>(std::in_place, 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<void>> { return Result<void>(); };
+  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<void>> {
+    return Result<void>(ResultError("failure string", 6));
+  };
+  auto result = return_result_result_with_error();
+  ASSERT_TRUE(result);
+  ASSERT_FALSE(*result);
+  EXPECT_EQ("failure string", (*result).error().message());
+  EXPECT_EQ(6, (*result).error().code());
+}
+
+// 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>(std::in_place, Result<TestStruct>(std::in_place, 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(), "");
+}
+
+template <class CharT>
+std::basic_ostream<CharT>& SetErrnoToTwo(std::basic_ostream<CharT>& ss) {
+  errno = 2;
+  return ss;
+}
+
+TEST(result, preserve_errno) {
+  errno = 1;
+  int old_errno = errno;
+  Result<int> result = Error() << "Failed" << SetErrnoToTwo<char>;
+  ASSERT_FALSE(result);
+  EXPECT_EQ(old_errno, errno);
+
+  errno = 1;
+  old_errno = errno;
+  Result<int> result2 = ErrnoError() << "Failed" << SetErrnoToTwo<char>;
+  ASSERT_FALSE(result2);
+  EXPECT_EQ(old_errno, errno);
+  EXPECT_EQ(old_errno, result2.error().code());
+}
+
+TEST(result, error_with_fmt) {
+  Result<int> result = Errorf("{} {}!", "hello", "world");
+  EXPECT_EQ("hello world!", result.error().message());
+
+  result = Errorf("{} {}!", std::string("hello"), std::string("world"));
+  EXPECT_EQ("hello world!", result.error().message());
+
+  result = Errorf("{h} {w}!", fmt::arg("w", "world"), fmt::arg("h", "hello"));
+  EXPECT_EQ("hello world!", result.error().message());
+
+  result = Errorf("hello world!");
+  EXPECT_EQ("hello world!", result.error().message());
+
+  Result<int> result2 = Errorf("error occurred with {}", result.error());
+  EXPECT_EQ("error occurred with hello world!", result2.error().message());
+
+  constexpr int test_errno = 6;
+  errno = test_errno;
+  result = ErrnoErrorf("{} {}!", "hello", "world");
+  EXPECT_EQ(test_errno, result.error().code());
+  EXPECT_EQ("hello world!: "s + strerror(test_errno), result.error().message());
+}
+
+TEST(result, error_with_fmt_carries_errno) {
+  constexpr int inner_errno = 6;
+  errno = inner_errno;
+  Result<int> inner_result = ErrnoErrorf("inner failure");
+  errno = 0;
+  EXPECT_EQ(inner_errno, inner_result.error().code());
+
+  // outer_result is created with Errorf, but its error code is got from inner_result.
+  Result<int> outer_result = Errorf("outer failure caused by {}", inner_result.error());
+  EXPECT_EQ(inner_errno, outer_result.error().code());
+  EXPECT_EQ("outer failure caused by inner failure: "s + strerror(inner_errno),
+            outer_result.error().message());
+
+  // now both result objects are created with ErrnoErrorf. errno from the inner_result
+  // is not passed to outer_result.
+  constexpr int outer_errno = 10;
+  errno = outer_errno;
+  outer_result = ErrnoErrorf("outer failure caused by {}", inner_result.error());
+  EXPECT_EQ(outer_errno, outer_result.error().code());
+  EXPECT_EQ("outer failure caused by inner failure: "s + strerror(inner_errno) + ": "s +
+                strerror(outer_errno),
+            outer_result.error().message());
+}
+
+TEST(result, errno_chaining_multiple) {
+  constexpr int errno1 = 6;
+  errno = errno1;
+  Result<int> inner1 = ErrnoErrorf("error1");
+
+  constexpr int errno2 = 10;
+  errno = errno2;
+  Result<int> inner2 = ErrnoErrorf("error2");
+
+  // takes the error code of inner2 since its the last one.
+  Result<int> outer = Errorf("two errors: {}, {}", inner1.error(), inner2.error());
+  EXPECT_EQ(errno2, outer.error().code());
+  EXPECT_EQ("two errors: error1: "s + strerror(errno1) + ", error2: "s + strerror(errno2),
+            outer.error().message());
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/strings.cpp b/base/strings.cpp
index a8bb2a9..bb3167e 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -87,50 +87,33 @@
 template std::string Join(const std::vector<std::string>&, const std::string&);
 template std::string Join(const std::vector<const char*>&, const std::string&);
 
-bool StartsWith(const std::string& s, const char* prefix) {
-  return strncmp(s.c_str(), prefix, strlen(prefix)) == 0;
+bool StartsWith(std::string_view s, std::string_view prefix) {
+  return s.substr(0, prefix.size()) == prefix;
 }
 
-bool StartsWith(const std::string& s, const std::string& prefix) {
-  return strncmp(s.c_str(), prefix.c_str(), prefix.size()) == 0;
+bool StartsWith(std::string_view s, char prefix) {
+  return !s.empty() && s.front() == prefix;
 }
 
-bool StartsWithIgnoreCase(const std::string& s, const char* prefix) {
-  return strncasecmp(s.c_str(), prefix, strlen(prefix)) == 0;
+bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix) {
+  return s.size() >= prefix.size() && strncasecmp(s.data(), prefix.data(), prefix.size()) == 0;
 }
 
-bool StartsWithIgnoreCase(const std::string& s, const std::string& prefix) {
-  return strncasecmp(s.c_str(), prefix.c_str(), prefix.size()) == 0;
+bool EndsWith(std::string_view s, std::string_view suffix) {
+  return s.size() >= suffix.size() && s.substr(s.size() - suffix.size(), suffix.size()) == suffix;
 }
 
-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;
-  }
-  size_t offset = string_length - suffix_length;
-  return (case_sensitive ? strncmp : strncasecmp)(s.c_str() + offset, suffix, suffix_length) == 0;
+bool EndsWith(std::string_view s, char suffix) {
+  return !s.empty() && s.back() == suffix;
 }
 
-bool EndsWith(const std::string& s, const char* suffix) {
-  return EndsWith(s, suffix, strlen(suffix), true);
+bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix) {
+  return s.size() >= suffix.size() &&
+         strncasecmp(s.data() + (s.size() - suffix.size()), suffix.data(), suffix.size()) == 0;
 }
 
-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, 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) {
-  return strcasecmp(lhs.c_str(), rhs.c_str()) == 0;
+bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs) {
+  return lhs.size() == rhs.size() && strncasecmp(lhs.data(), rhs.data(), lhs.size()) == 0;
 }
 
 }  // namespace base
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index b8639ea..ca3c0b8 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -198,6 +198,12 @@
   ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "BAR"));
 }
 
+TEST(strings, StartsWith_char) {
+  ASSERT_FALSE(android::base::StartsWith("", 'f'));
+  ASSERT_TRUE(android::base::StartsWith("foo", 'f'));
+  ASSERT_FALSE(android::base::StartsWith("foo", 'o'));
+}
+
 TEST(strings, EndsWith_empty) {
   ASSERT_FALSE(android::base::EndsWith("", "foo"));
   ASSERT_TRUE(android::base::EndsWith("", ""));
@@ -273,6 +279,12 @@
   ASSERT_FALSE(android::base::EndsWithIgnoreCase("GoOdByE", std::string{"lo"}));
 }
 
+TEST(strings, EndsWith_char) {
+  ASSERT_FALSE(android::base::EndsWith("", 'o'));
+  ASSERT_TRUE(android::base::EndsWith("foo", 'o'));
+  ASSERT_FALSE(android::base::EndsWith("foo", "f"));
+}
+
 TEST(strings, EqualsIgnoreCase) {
   ASSERT_TRUE(android::base::EqualsIgnoreCase("foo", "FOO"));
   ASSERT_TRUE(android::base::EqualsIgnoreCase("FOO", "foo"));
@@ -283,3 +295,19 @@
 TEST(strings, ubsan_28729303) {
   android::base::Split("/dev/null", ":");
 }
+
+TEST(strings, ConsumePrefix) {
+  std::string_view s{"foo.bar"};
+  ASSERT_FALSE(android::base::ConsumePrefix(&s, "bar."));
+  ASSERT_EQ("foo.bar", s);
+  ASSERT_TRUE(android::base::ConsumePrefix(&s, "foo."));
+  ASSERT_EQ("bar", s);
+}
+
+TEST(strings, ConsumeSuffix) {
+  std::string_view s{"foo.bar"};
+  ASSERT_FALSE(android::base::ConsumeSuffix(&s, ".foo"));
+  ASSERT_EQ("foo.bar", s);
+  ASSERT_TRUE(android::base::ConsumeSuffix(&s, ".bar"));
+  ASSERT_EQ("foo", s);
+}
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 9d8dfb2..36b4cdf 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>
@@ -23,129 +22,54 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-#if defined(_WIN32)
-#include <windows.h>
-#include <direct.h>
-#define OS_PATH_SEPARATOR '\\'
-#else
-#define OS_PATH_SEPARATOR '/'
-#endif
-
 #include <string>
 
-#ifdef _WIN32
-int mkstemp(char* template_name) {
-  if (_mktemp(template_name) == nullptr) {
-    return -1;
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+CapturedStdFd::CapturedStdFd(int std_fd) : std_fd_(std_fd), old_fd_(-1) {
+  Start();
+}
+
+CapturedStdFd::~CapturedStdFd() {
+  if (old_fd_ != -1) {
+    Stop();
   }
-  // Use open() to match the close() that TemporaryFile's destructor does.
-  // Use O_BINARY to match base file APIs.
-  return open(template_name, O_CREAT | O_EXCL | O_RDWR | O_BINARY,
-              S_IRUSR | S_IWUSR);
 }
 
-char* mkdtemp(char* template_name) {
-  if (_mktemp(template_name) == nullptr) {
-    return nullptr;
-  }
-  if (_mkdir(template_name) == -1) {
-    return nullptr;
-  }
-  return template_name;
-}
-#endif
-
-static std::string GetSystemTempDir() {
-#if defined(__ANDROID__)
-  const char* tmpdir = "/data/local/tmp";
-  if (access(tmpdir, R_OK | W_OK | X_OK) == 0) {
-    return tmpdir;
-  }
-  // Tests running in app context can't access /data/local/tmp,
-  // so try current directory if /data/local/tmp is not accessible.
-  return ".";
-#elif defined(_WIN32)
-  char tmp_dir[MAX_PATH];
-  DWORD result = GetTempPathA(sizeof(tmp_dir), tmp_dir);
-  CHECK_NE(result, 0ul) << "GetTempPathA failed, error: " << GetLastError();
-  CHECK_LT(result, sizeof(tmp_dir)) << "path truncated to: " << result;
-
-  // GetTempPath() returns a path with a trailing slash, but init()
-  // does not expect that, so remove it.
-  CHECK_EQ(tmp_dir[result - 1], '\\');
-  tmp_dir[result - 1] = '\0';
-  return tmp_dir;
-#else
-  return "/tmp";
-#endif
-}
-
-TemporaryFile::TemporaryFile() {
-  init(GetSystemTempDir());
-}
-
-TemporaryFile::TemporaryFile(const std::string& tmp_dir) {
-  init(tmp_dir);
-}
-
-TemporaryFile::~TemporaryFile() {
-  if (fd != -1) {
-    close(fd);
-  }
-  unlink(path);
-}
-
-int TemporaryFile::release() {
-  int result = fd;
-  fd = -1;
-  return result;
-}
-
-void TemporaryFile::init(const std::string& tmp_dir) {
-  snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(),
-           OS_PATH_SEPARATOR);
-  fd = mkstemp(path);
-}
-
-TemporaryDir::TemporaryDir() {
-  init(GetSystemTempDir());
-}
-
-TemporaryDir::~TemporaryDir() {
-  rmdir(path);
-}
-
-bool TemporaryDir::init(const std::string& tmp_dir) {
-  snprintf(path, sizeof(path), "%s%cTemporaryDir-XXXXXX", tmp_dir.c_str(),
-           OS_PATH_SEPARATOR);
-  return (mkdtemp(path) != nullptr);
-}
-
-CapturedStderr::CapturedStderr() : old_stderr_(-1) {
-  init();
-}
-
-CapturedStderr::~CapturedStderr() {
-  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::Reset() {
+  // Do not reset while capturing.
+  CHECK_EQ(-1, old_fd_);
+  CHECK_EQ(0, TEMP_FAILURE_RETRY(lseek(fd(), 0, SEEK_SET)));
+  CHECK_EQ(0, ftruncate(fd(), 0));
+}
+
+void CapturedStdFd::Start() {
 #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, nullptr, _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::Stop() {
+  CHECK_NE(-1, old_fd_);
+  CHECK_NE(-1, dup2(old_fd_, std_fd_));
+  close(old_fd_);
+  old_fd_ = -1;
   // Note: cannot restore prior setvbuf() setting.
 }
diff --git a/base/test_utils_test.cpp b/base/test_utils_test.cpp
index 597271a..15a79dd 100644
--- a/base/test_utils_test.cpp
+++ b/base/test_utils_test.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <stdio.h>
+
 #include "android-base/test_utils.h"
 
 #include <gtest/gtest-spi.h>
@@ -42,5 +44,43 @@
   EXPECT_NONFATAL_FAILURE(EXPECT_NOT_MATCH("foobar", R"(foobar)"), "regex mismatch");
 }
 
+TEST(TestUtilsTest, CaptureStdout_smoke) {
+  CapturedStdout cap;
+  printf("This should be captured.\n");
+  cap.Stop();
+  printf("This will not be captured.\n");
+  ASSERT_EQ("This should be captured.\n", cap.str());
+
+  cap.Start();
+  printf("And this text should be captured too.\n");
+  cap.Stop();
+  ASSERT_EQ("This should be captured.\nAnd this text should be captured too.\n", cap.str());
+
+  printf("Still not going to be captured.\n");
+  cap.Reset();
+  cap.Start();
+  printf("Only this will be captured.\n");
+  ASSERT_EQ("Only this will be captured.\n", cap.str());
+}
+
+TEST(TestUtilsTest, CaptureStderr_smoke) {
+  CapturedStderr cap;
+  fprintf(stderr, "This should be captured.\n");
+  cap.Stop();
+  fprintf(stderr, "This will not be captured.\n");
+  ASSERT_EQ("This should be captured.\n", cap.str());
+
+  cap.Start();
+  fprintf(stderr, "And this text should be captured too.\n");
+  cap.Stop();
+  ASSERT_EQ("This should be captured.\nAnd this text should be captured too.\n", cap.str());
+
+  fprintf(stderr, "Still not going to be captured.\n");
+  cap.Reset();
+  cap.Start();
+  fprintf(stderr, "Only this will be captured.\n");
+  ASSERT_EQ("Only this will be captured.\n", cap.str());
+}
+
 }  // 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/utf8_test.cpp b/base/utf8_test.cpp
index fcb25c3..472e82c 100644
--- a/base/utf8_test.cpp
+++ b/base/utf8_test.cpp
@@ -21,8 +21,8 @@
 #include <fcntl.h>
 #include <stdlib.h>
 
+#include "android-base/file.h"
 #include "android-base/macros.h"
-#include "android-base/test_utils.h"
 #include "android-base/unique_fd.h"
 
 namespace android {
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index 3ddadbc..5e2d171 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -64,7 +64,6 @@
     defaults: ["bootstat_defaults"],
     static_libs: ["libbootstat"],
     shared_libs: [
-        "liblogcat",
         "libstatslog"
     ],
     init_rc: ["bootstat.rc"],
diff --git a/bootstat/OWNERS b/bootstat/OWNERS
index 7fe0443..50b2097 100644
--- a/bootstat/OWNERS
+++ b/bootstat/OWNERS
@@ -1 +1,2 @@
 jhawkins@google.com
+salyzyn@google.com
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index 4b7ab36..5ca9b09 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -29,7 +29,6 @@
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
-#include <android-base/test_utils.h>
 #include <android-base/unique_fd.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 79702a6..8979b0c 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -24,6 +24,9 @@
 NORMAL="${ESCAPE}[0m"
 # Best guess to an average device's reboot time, refined as tests return
 DURATION_DEFAULT=45
+STOP_ON_FAILURE=false
+progname="${0##*/}"
+progpath="${0%${progname}}"
 
 # Helper functions
 
@@ -41,20 +44,56 @@
   adb devices | grep -v 'List of devices attached' | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
 }
 
+[ "USAGE: adb_sh <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
+
+Returns: true if the command succeeded" ]
+adb_sh() {
+  local args=
+  for i in "${@}"; do
+    [ -z "${args}" ] || args="${args} "
+    if [ X"${i}" != X"${i#\'}" ]; then
+      args="${args}${i}"
+    elif [ X"${i}" != X"${i#*\\}" ]; then
+      args="${args}`echo ${i} | sed 's/\\\\/\\\\\\\\/g'`"
+    elif [ X"${i}" != X"${i#* }" ]; then
+      args="${args}'${i}'"
+    elif [ X"${i}" != X"${i#*${TAB}}" ]; then
+      args="${args}'${i}'"
+    else
+      args="${args}${i}"
+    fi
+  done
+  adb shell "${args}"
+}
+
+[ "USAGE: adb_su <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
+
+Returns: true if the command running as root succeeded" ]
+adb_su() {
+  adb_sh su root "${@}"
+}
+
 [ "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
+  if inAdb && [ 0 -eq `adb_su ls /sys/fs/pstore </dev/null | wc -l` ]; then
     false
   fi
 }
 
+[ "USAGE: get_property <prop>
+
+Returns the property value" ]
+get_property() {
+  adb_sh getprop ${1} 2>&1 </dev/null
+}
+
 [ "USAGE: isDebuggable
 
 Returns: true if device is (likely) a debug build" ]
 isDebuggable() {
-  if inAdb && [ 1 -ne `adb shell getprop ro.debuggable` ]; then
+  if inAdb && [ 1 -ne `get_property ro.debuggable` ]; then
     false
   fi
 }
@@ -81,19 +120,19 @@
 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
+  if [ -z "`adb_sh ls /etc/init/bootstat-debug.rc 2>/dev/null </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'" |
+  if adb_su "cat /proc/cmdline | tr '\\0 ' '\\n\\n'" </dev/null |
      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="`adb shell getprop persist.test.boot.reason 2>/dev/null`"
+  adb_su setprop persist.test.boot.reason "'${1}'" 2>/dev/null </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
@@ -188,9 +227,9 @@
       if [ 0 != ${counter} ]; then
         adb wait-for-device </dev/null >/dev/null 2>/dev/null
       fi
-      if [ -n "`adb shell getprop sys.boot.reason </dev/null 2>/dev/null`" ]
+      if [ -n "`get_property sys.boot.reason`" ]
       then
-        vals=`adb shell getprop </dev/null 2>/dev/null |
+        vals=`get_property |
               sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\)[]]: [[]\([01]\)[]]$/\1=\2/p'`
         if [ "${vals}" = "`echo boot_completed=1 ; echo logbootcomplete=1`" ]
         then
@@ -223,15 +262,38 @@
   rval="${2}"
   shift 2
   if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
-    echo "ERROR: expected \"${lval}\" got \"${rval}\"" >&2
-    if [ -n "${*}" ] ; then
-      echo "       ${*}" >&2
+    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
-      echo "INFO: ok \"${lval}\"(=\"${rval}\") ${*}" >&2
+      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
@@ -239,6 +301,8 @@
   return 0
 }
 
+BAD_BOOTLOADER_REASON=
+
 [ "USAGE: EXPECT_PROPERTY <prop> <value> [--allow_failure]
 
 Returns true (0) if current return (regex) value is true and the result matches
@@ -248,14 +312,32 @@
   property="${1}"
   value="${2}"
   shift 2
-  val=`adb shell getprop ${property} 2>&1`
-  EXPECT_EQ "${value}" "${val}" for Android property ${property} ||
-    [ -n "${1}" ] ||
-    save_ret=${?}
+  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> ...
+[ "USAGE: adb_date >/dev/stdout
+
+Returns: report device epoch time (suitable for logcat -t)" ]
+adb_date() {
+  adb_sh date +%s.%N </dev/null
+}
+
+[ "USAGE: report_bootstat_logs [-t<timestamp>] <expected> ...
 
 if not prefixed with a minus (-), <expected> will become a series of expected
 matches:
@@ -270,8 +352,11 @@
 report_bootstat_logs() {
   save_ret=${?}
   match=
+  timestamp=-d
   for i in "${@}"; do
-    if [ X"${i}" != X"${i#-}" ] ; then
+    if [ X"${i}" != X"${i#-t}" ]; then
+      timestamp="${i}"
+    elif [ X"${i}" != X"${i#-}" ]; then
       match="${match}
 ${i#-}"
     else
@@ -279,14 +364,16 @@
 bootstat: Canonical boot reason: ${i}"
     fi
   done
-  adb logcat -b all -d |
+  adb logcat -b all ${timestamp} |
   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 --set_system_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...
@@ -296,18 +383,24 @@
 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 (ro.boot.bootreason=* && post-fs) from (/system/etc/init/bootstat.rc
+init    : processing action (zygote-start) 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 --set_system_boot_reason)'...
+ (/system/bin/bootstat --set_system_boot_reason)' (pid${SPACE}
  (/system/bin/bootstat -l)'...
  (/system/bin/bootstat -l)' (pid " |
   grep -v 'bootstat: Unknown boot reason: $' # Hikey Special
@@ -377,6 +470,9 @@
     echo "${GREEN}[       OK ]${NORMAL} ${TEST} ${*}"
   else
     echo "${RED}[  FAILED  ]${NORMAL} ${TEST} ${*}"
+    if ${STOP_ON_FAILURE}; then
+      exit ${save_ret}
+    fi
   fi
   return ${save_ret}
 }
@@ -407,29 +503,35 @@
        tr ' \f\t\r\n' '_____'`
   case ${var} in
     watchdog | watchdog,?* ) ;;
-    kernel_panic | kernel_panic,?*) ;;
-    recovery | recovery,?*) ;;
-    bootloader | bootloader,?*) ;;
-    cold | cold,?*) ;;
-    hard | hard,?*) ;;
-    warm | warm,?*) ;;
-    shutdown | shutdown,?*) ;;
+    kernel_panic | kernel_panic,?* ) ;;
+    recovery | recovery,?* ) ;;
+    bootloader | bootloader,?* ) ;;
+    cold | cold,?* ) ;;
+    hard | hard,?* ) ;;
+    warm | warm,?* ) ;;
+    shutdown | shutdown,?* ) ;;
     reboot,reboot | reboot,reboot,* )     var=${var#reboot,} ; var=${var%,} ;;
     reboot,cold | reboot,cold,* )         var=${var#reboot,} ; var=${var%,} ;;
     reboot,hard | reboot,hard,* )         var=${var#reboot,} ; var=${var%,} ;;
     reboot,warm | reboot,warm,* )         var=${var#reboot,} ; var=${var%,} ;;
     reboot,recovery | reboot,recovery,* ) var=${var#reboot,} ; var=${var%,} ;;
     reboot,bootloader | reboot,bootloader,* ) var=${var#reboot,} ; var=${var%,} ;;
-    reboot | reboot,?*) ;;
+    reboot | reboot,?* ) ;;
     # Aliases and Heuristics
-    *wdog* | *watchdog* )     var="watchdog" ;;
-    *powerkey* )              var="cold,powerkey" ;;
-    *panic* | *kernel_panic*) var="kernel_panic" ;;
-    *thermal*)                var="shutdown,thermal" ;;
-    *s3_wakeup*)              var="warm,s3_wakeup" ;;
-    *hw_reset*)               var="hard,hw_reset" ;;
-    *bootloader*)             var="bootloader" ;;
-    *)                        var="reboot" ;;
+    *wdog* | *watchdog* )                   var="watchdog" ;;
+    *powerkey* | *power_key* | *PowerKey* ) var="cold,powerkey" ;;
+    *panic* | *kernel_panic* )              var="kernel_panic" ;;
+    *thermal* )                             var="shutdown,thermal" ;;
+    *s3_wakeup* )                           var="warm,s3_wakeup" ;;
+    *hw_reset* )                            var="hard,hw_reset" ;;
+    *usb* )                                 var="cold,charger" ;;
+    *rtc* )                                 var="cold,rtc" ;;
+    *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}
 }
@@ -441,7 +543,7 @@
 
 NB: must also roughly match heuristics in system/core/bootstat/bootstat.cpp" ]
 validate_property() {
-  val="`adb shell getprop ${1} 2>&1`"
+  val=`get_property ${1}`
   ret=`validate_reason "${val}"`
   if [ "reboot" = "${ret}" ]; then
     ret=`validate_reason "reboot,${val}"`
@@ -449,6 +551,17 @@
   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
 #
@@ -459,18 +572,20 @@
 - (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
-  check_set="ro.boot.bootreason persist.sys.boot.reason sys.boot.reason"
+  # 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 persist.sys.boot.reason
+  #        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"
@@ -480,7 +595,7 @@
     reason=`validate_property ${prop}`
     EXPECT_PROPERTY ${prop} ${reason} || retval=${?}
   done
-  # sys.boot.reason is last for a reason
+  check_boilerplate_properties || retval=${?}
   report_bootstat_logs ${reason} ${bootloader}
   return ${retval}
 }
@@ -533,7 +648,8 @@
   popd >&2
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason "\(reboot,ota\|bootloader\)"
-  EXPECT_PROPERTY persist.sys.boot.reason bootloader
+  EXPECT_PROPERTY sys.boot.reason.last bootloader
+  check_boilerplate_properties
   report_bootstat_logs reboot,ota bootloader
 }
 
@@ -543,11 +659,12 @@
 test_optional_ota() {
   checkDebugBuild || return
   duration_test
-  adb shell su root touch /data/misc/bootstat/build_date >&2
+  adb_su touch /data/misc/bootstat/build_date >&2 </dev/null
   adb reboot ota
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,ota
-  EXPECT_PROPERTY persist.sys.boot.reason reboot,ota
+  EXPECT_PROPERTY sys.boot.reason.last reboot,ota
+  check_boilerplate_properties
   report_bootstat_logs reboot,ota
 }
 
@@ -566,15 +683,21 @@
   duration_test
   case ${TEST} in
     bootloader | recovery | cold | hard | warm ) reason=${TEST} ;;
-    *)                                           reason=reboot,${TEST} ;;
+    *)                                           reason=reboot,${TEST#optional_} ;;
   esac
-  adb reboot ${TEST}
+  adb reboot ${TEST#optional_}
   wait_for_screen
   bootloader_reason=`validate_property ro.boot.bootreason`
   EXPECT_PROPERTY ro.boot.bootreason ${bootloader_reason}
-  EXPECT_PROPERTY sys.boot.reason ${reason}
-  EXPECT_PROPERTY persist.sys.boot.reason ${reason}
-  report_bootstat_logs ${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
@@ -602,11 +725,12 @@
 test_factory_reset() {
   checkDebugBuild || return
   duration_test
-  adb shell su root rm /data/misc/bootstat/build_date >&2
+  adb_su rm /data/misc/bootstat/build_date >&2 </dev/null
   adb reboot >&2
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
-  EXPECT_PROPERTY persist.sys.boot.reason "reboot,.*"
+  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"
@@ -637,7 +761,8 @@
   wait_for_screen
   ( exit ${save_ret} )  # because one can not just do ?=${save_ret}
   EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
-  EXPECT_PROPERTY persist.sys.boot.reason ""
+  EXPECT_PROPERTY sys.boot.reason.last "\(\|bootloader\)"
+  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" \
@@ -687,12 +812,12 @@
   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
+    echo 'healthd: battery l=2 ' | adb_su tee /dev/kmsg >/dev/null
   done
   adb reboot cold >&2
   adb wait-for-device
   wait_for_screen
-  adb shell su root \
+  adb_su </dev/null \
     cat /proc/fs/pstore/console-ramoops \
         /proc/fs/pstore/console-ramoops-0 2>/dev/null |
     grep 'healthd: battery l=' |
@@ -701,7 +826,7 @@
       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
+          echo 'healthd: battery l=2 ' | adb_su tee /dev/kmsg >/dev/null
         done
         adb reboot cold >&2
         adb wait-for-device
@@ -710,7 +835,8 @@
     )
 
   EXPECT_PROPERTY sys.boot.reason shutdown,battery
-  EXPECT_PROPERTY persist.sys.boot.reason cold
+  EXPECT_PROPERTY sys.boot.reason.last cold
+  check_boilerplate_properties
   report_bootstat_logs shutdown,battery "-bootstat: Battery level at shutdown 2%"
   exitPstore
 }
@@ -726,12 +852,13 @@
 test_optional_battery() {
   duration_test ">60"
   echo "      power on request" >&2
-  adb shell setprop sys.powerctl shutdown,battery
+  adb_sh setprop sys.powerctl shutdown,battery </dev/null
   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 persist.sys.boot.reason shutdown,battery
+  EXPECT_PROPERTY sys.boot.reason.last shutdown,battery
+  check_boilerplate_properties
   report_bootstat_logs shutdown,battery
 }
 
@@ -746,12 +873,13 @@
 test_optional_battery_thermal() {
   duration_test ">60"
   echo "      power on request" >&2
-  adb shell setprop sys.powerctl shutdown,thermal,battery
+  adb_sh setprop sys.powerctl shutdown,thermal,battery </dev/null
   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 persist.sys.boot.reason shutdown,thermal,battery
+  EXPECT_PROPERTY sys.boot.reason.last shutdown,thermal,battery
+  check_boilerplate_properties
   report_bootstat_logs shutdown,thermal,battery
 }
 
@@ -784,14 +912,74 @@
     panic_msg="\(kernel_panic,sysrq\|kernel_panic\)"
     pstore_ok=true
   fi
-  echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
+  echo c | adb_su tee /proc/sysrq-trigger >/dev/null
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason ${panic_msg}
-  EXPECT_PROPERTY persist.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_su tee /dev/kmsg
+  echo c | adb_su 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_su 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
@@ -814,12 +1002,13 @@
 test_thermal_shutdown() {
   duration_test ">60"
   echo "      power on request" >&2
-  adb shell setprop sys.powerctl shutdown,thermal
+  adb_sh setprop sys.powerctl shutdown,thermal </dev/null
   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 persist.sys.boot.reason shutdown,thermal
+  EXPECT_PROPERTY sys.boot.reason.last shutdown,thermal
+  check_boilerplate_properties
   report_bootstat_logs shutdown,thermal
 }
 
@@ -834,12 +1023,13 @@
 test_userrequested_shutdown() {
   duration_test ">60"
   echo "      power on request" >&2
-  adb shell setprop sys.powerctl shutdown,userrequested
+  adb_sh setprop sys.powerctl shutdown,userrequested </dev/null
   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 persist.sys.boot.reason shutdown,userrequested
+  EXPECT_PROPERTY sys.boot.reason.last shutdown,userrequested
+  check_boilerplate_properties
   report_bootstat_logs shutdown,userrequested
 }
 
@@ -852,10 +1042,11 @@
 - NB: should report reboot,shell" ]
 test_shell_reboot() {
   duration_test
-  adb shell reboot
+  adb_sh reboot </dev/null
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,shell
-  EXPECT_PROPERTY persist.sys.boot.reason reboot,shell
+  EXPECT_PROPERTY sys.boot.reason.last reboot,shell
+  check_boilerplate_properties
   report_bootstat_logs reboot,shell
 }
 
@@ -871,10 +1062,25 @@
   adb reboot
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,adb
-  EXPECT_PROPERTY persist.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\|reboot,rescueparty\)'
+}
+
 [ "USAGE: test_Its_Just_So_Hard_reboot
 
 Its Just So Hard reboot test:
@@ -889,26 +1095,11 @@
   else
     duration_test `expr ${DURATION_DEFAULT} + ${DURATION_DEFAULT}`
   fi
-  adb shell 'reboot "Its Just So Hard"'
+  adb_sh 'reboot "Its Just So Hard"' </dev/null
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
-  EXPECT_PROPERTY persist.sys.boot.reason "reboot,Its Just So Hard"
-  # Do not leave this test with an illegal value in persist.sys.boot.reason
-  save_ret=${?}           # hold on to error code from above two lines
-  if isDebuggable; then   # can do this easy, or we can do this hard.
-    adb shell su root setprop persist.sys.boot.reason reboot,its_just_so_hard
-    ( exit ${save_ret} )  # because one can not just do ?=${save_ret}
-  else
-    report_bootstat_logs reboot,its_just_so_hard  # report what we have so far
-    # user build mitigation
-    adb shell reboot its_just_so_hard
-    wait_for_screen
-    ( exit ${save_ret} )  # because one can not just do ?=${save_ret}
-    EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
-  fi
-  # Ensure persist.sys.boot.reason now valid, failure here acts as a signal
-  # that we could choke up following tests.  For example test_properties.
-  EXPECT_PROPERTY persist.sys.boot.reason reboot,its_just_so_hard ${flag}
+  EXPECT_PROPERTY sys.boot.reason.last reboot,its_just_so_hard
+  check_boilerplate_properties
   report_bootstat_logs reboot,its_just_so_hard
 }
 
@@ -957,7 +1148,8 @@
   # Check values
   EXPECT_PROPERTY ro.boot.bootreason "${bootloader_expected}"
   EXPECT_PROPERTY sys.boot.reason "${sys_expected}"
-  EXPECT_PROPERTY persist.sys.boot.reason "${last_expected}"
+  EXPECT_PROPERTY sys.boot.reason.last "${last_expected}"
+  check_boilerplate_properties
   report_bootstat_logs "${sys_expected}"
 }
 
@@ -1000,7 +1192,121 @@
   run_bootloader
 }
 
-[ "USAGE: ${0##*/} [-s SERIAL] [tests]
+[ "USAGE: run_kBootReasonMap [--boot_reason_enum] value expected
+
+bootloader boot reason injection tests:
+- if --boot_reason_enum run bootstat executable for result instead.
+- inject boot reason into sys.boot.reason
+- run bootstat --set_system_boot_reason
+- check for expected enum
+- " ]
+run_kBootReasonMap() {
+  if [ X"--boot_reason_enum" = X"${1}" ]; then
+    shift
+    local sys_expected="${1}"
+    shift
+    local enum_expected="${1}"
+    adb_su bootstat --boot_reason_enum="${sys_expected}" |
+      (
+        local retval=-1
+        while read -r id match; do
+          if [ ${retval} = -1 -a ${enum_expected} = ${id} ]; then
+            retval=0
+          fi
+          if [ ${enum_expected} != ${id} ]; then
+            echo "ERROR: ${enum_expected} ${sys_expected} got ${id} ${match}" >&2
+            retval=1
+          fi
+        done
+        exit ${retval}
+      )
+    return
+  fi
+  local sys_expected="${1}"
+  shift
+  local enum_expected="${1}"
+  adb_su setprop sys.boot.reason "${sys_expected}" </dev/null
+  adb_su bootstat --record_boot_reason </dev/null
+  # Check values
+  EXPECT_PROPERTY sys.boot.reason "${sys_expected}"
+  local retval=${?}
+  local result=`adb_su stat -c %Y /data/misc/bootstat/system_boot_reason </dev/null 2>/dev/null`
+  [ "${enum_expected}" = "${result}" ] ||
+    (
+      [ -n "${result}" ] || result="<nothing>"
+      echo "ERROR: ${enum_expected} ${sys_expected} got ${result}" >&2
+      false
+    ) ||
+    retval=${?}
+  return ${retval}
+}
+
+[ "USAGE: filter_kBootReasonMap </dev/stdin >/dev/stdout
+
+convert any regex expressions into a series of non-regex test strings" ]
+filter_kBootReasonMap() {
+  while read -r id match; do
+    case ${match} in
+      'reboot,[empty]')
+        echo ${id}          # matches b/c of special case
+        echo ${id} reboot,y # matches b/c of regex
+        echo 1 reboot,empty # negative test (ID for unknown is 1)
+        ;;
+      reboot)
+        echo 1 reboog       # negative test (ID for unknown is 1)
+        ;;
+      'reboot,pmic_off_fault,.*')
+        echo ${id} reboot,pmic_off_fault,hello,world
+        echo ${id} reboot,pmic_off_fault,
+        echo 1 reboot,pmic_off_fault
+        ;;
+    esac
+    echo ${id} "${match}"   # matches b/c of exact
+  done
+}
+
+[ "USAGE: test_kBootReasonMap
+
+kBootReasonMap test
+- (wait until screen is up, boot has completed)
+- read bootstat for kBootReasonMap entries and test them all" ]
+test_kBootReasonMap() {
+  checkDebugBuild || return
+  duration_test 15
+  local tempfile="`mktemp`"
+  local arg=--boot_reason_enum
+  adb_su bootstat ${arg} </dev/null 2>/dev/null |
+    filter_kBootReasonMap >${tempfile}
+  if [ ! -s "${tempfile}" ]; then
+    wait_for_screen
+    arg=
+    sed -n <${progpath}bootstat.cpp \
+      '/kBootReasonMap = {/,/^};/s/.*{"\([^"]*\)", *\([0-9][0-9]*\)},.*/\2 \1/p' |
+      sed 's/\\\\/\\/g' |
+      filter_kBootReasonMap >${tempfile}
+  fi
+  T=`adb_date`
+  retval=0
+  while read -r enum string; do
+    if [ X"${string}" != X"${string#*[[].[]]}" -o X"${string}" != X"${string#*\\.}" ]; then
+      if [ 'reboot\.empty' != "${string}" ]; then
+        echo "WARNING: regex snuck through filter_kBootReasonMap ${enum} ${string}" >&2
+        enum=1
+      fi
+    fi
+    run_kBootReasonMap ${arg} "${string}" "${enum}" </dev/null || retval=${?}
+  done <${tempfile}
+  rm ${tempfile}
+  ( exit ${retval} )
+  # See filter_kBootReasonMap() for negative tests and add them here too
+  report_bootstat_logs -t${T} \
+    '-bootstat: Service started: bootstat --boot_reason_enum=' \
+    '-bootstat: Unknown boot reason: reboot,empty' \
+    '-bootstat: Unknown boot reason: reboog' \
+    '-bootstat: Unknown boot reason: reboot,pmic_off_fault'
+}
+
+[ "USAGE: ${progname} [-s SERIAL] [tests]...
 
 Mainline executive to run the above tests" ]
 
@@ -1011,88 +1317,98 @@
   shift 2
 fi
 
-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
+# Helpful for debugging, allows us to import the functions.
+if [ X"--macros" != X"${1}" ]; then
 
-# Check if all conditions for the script are sane
+  if [ X"--help" = X"${1}" -o X"-h" = X"${1}" -o X"-?" = X"${1}" ]; then
+    echo "USAGE: ${progname} [-s SERIAL] [tests]..."
+    echo tests - `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
+    exit 0
+  fi
 
-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
+  if [ X"--stop" = X"${1}" ]; then
+    STOP_ON_FAILURE=true
+    shift
   fi
-  echo "WARNING: no target device specified" >&2
-fi
 
-ret=0
+  # Check if all conditions for the script are sane
 
-# 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 above (search/permission issues)
-    eval set properties ota cold factory_reset hard battery unknown \
-             kernel_panic warm thermal_shutdown userrequested_shutdown \
-             shell_reboot adb_reboot Its_Just_So_Hard_reboot \
-             bootloader_normal bootloader_watchdog bootloader_kernel_panic \
-             bootloader_oem_powerkey bootloader_wdog_reset \
-             bootloader_wdog_reset bootloader_wdog_reset bootloader_hard \
-             bootloader_recovery
-  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}"
+  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
-  else
-    ret=${retval}
-    if [ -z "${failures}" ]; then
-      failures=${t}
-    else
-      failures="${failures} ${t}"
+    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 kBootReasonMap
+    fi
+    if [ X"nothing" = X"${1}" ]; then
+      shift 1
+    fi
+  fi
+  echo "INFO: selected test(s): ${@}" >&2
   echo
-done
+  # 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}"
+  if [ -n "${successes}" ]; then
+    echo "${GREEN}[  PASSED  ]${NORMAL} ${successes}"
+  fi
+  if [ -n "${failures}" ]; then
+    echo "${RED}[  FAILED  ]${NORMAL} ${failures}"
+  fi
+  exit ${ret}
+
 fi
-if [ -n "${failures}" ]; then
-  echo "${RED}[  FAILED  ]${NORMAL} ${failures}"
-fi
-exit ${ret}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 7a67894..6936cc2 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -27,9 +27,12 @@
 #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>
@@ -41,7 +44,6 @@
 #include <android/log.h>
 #include <cutils/android_reboot.h>
 #include <cutils/properties.h>
-#include <log/logcat.h>
 #include <metricslogger/metrics_logger.h>
 #include <statslog.h>
 
@@ -87,7 +89,7 @@
 }
 
 void ShowHelp(const char* cmd) {
-  fprintf(stderr, "Usage: %s [options]\n", cmd);
+  fprintf(stderr, "Usage: %s [options]...\n", cmd);
   fprintf(stderr,
           "options include:\n"
           "  -h, --help              Show this help\n"
@@ -97,7 +99,8 @@
           "  --value                 Optional value to associate with the boot event\n"
           "  --record_boot_complete  Record metrics related to the time for the device boot\n"
           "  --record_boot_reason    Record the reason why the device booted\n"
-          "  --record_time_since_factory_reset Record the time since the device was reset\n");
+          "  --record_time_since_factory_reset  Record the time since the device was reset\n"
+          "  --boot_reason_enum=<reason>  Report the match to the kBootReasonMap table\n");
 }
 
 // Constructs a readable, printable string from the givencommand line
@@ -112,35 +115,17 @@
   return cmd;
 }
 
-// Convenience wrapper over the property API that returns an
-// std::string.
-std::string GetProperty(const char* key) {
-  std::vector<char> temp(PROPERTY_VALUE_MAX);
-  const int len = property_get(key, &temp[0], nullptr);
-  if (len < 0) {
-    return "";
-  }
-  return std::string(&temp[0], len);
-}
-
-void SetProperty(const char* key, const std::string& val) {
-  property_set(key, val.c_str());
-}
-
-void SetProperty(const char* key, const char* val) {
-  property_set(key, val);
-}
-
 constexpr int32_t kEmptyBootReason = 0;
 constexpr int32_t kUnknownBootReason = 1;
 
 // A mapping from boot reason string, as read from the ro.boot.bootreason
 // system property, to a unique integer ID. Viewers of log data dashboards for
 // the boot_reason metric may refer to this mapping to discern the histogram
-// values.
+// values.  Regex matching, to manage the scale, as a minimum require either
+// [, \ or * to be present in the string to switch to checking.
 const std::map<std::string, int32_t> kBootReasonMap = {
-    {"empty", kEmptyBootReason},
-    {"unknown", kUnknownBootReason},
+    {"reboot,[empty]", kEmptyBootReason},
+    {"__BOOTSTAT_UNKNOWN__", kUnknownBootReason},
     {"normal", 2},
     {"recovery", 3},
     {"reboot", 4},
@@ -155,7 +140,7 @@
     {"mba_err", 13},
     {"Watchdog", 14},
     {"Panic", 15},
-    {"power_key", 16},
+    {"power_key", 16},  // Mediatek
     {"power_on", 17},
     {"Reboot", 18},
     {"rtc", 19},
@@ -184,7 +169,7 @@
     {"wdog_bark", 42},
     {"wdog_bite", 43},
     {"wdog_reset", 44},
-    {"shutdown,", 45},  // Trailing comma is intentional.
+    {"shutdown,", 45},  // Trailing comma is intentional. Do NOT use.
     {"shutdown,userrequested", 46},
     {"reboot,bootloader", 47},
     {"reboot,cold", 48},
@@ -193,12 +178,14 @@
     {"s3_wakeup", 51},
     {"kernel_panic,sysrq", 52},
     {"kernel_panic,NULL", 53},
+    {"kernel_panic,null", 53},
     {"kernel_panic,BUG", 54},
+    {"kernel_panic,bug", 54},
     {"bootloader", 55},
     {"cold", 56},
     {"hard", 57},
     {"warm", 58},
-    {"recovery", 59},
+    {"reboot,kernel_power_off_charging__reboot_system", 59},  // Can not happen
     {"thermal-shutdown", 60},
     {"shutdown,thermal", 61},
     {"shutdown,battery", 62},
@@ -216,23 +203,23 @@
     {"shutdown,hibernate", 74},  // Suspend to DISK
     {"power_on_key", 75},
     {"reboot_by_key", 76},
-    {"wdt_by_pass_pwk", 77},
+    {"wdt_by_pass_pwk", 77},  // Mediatek
     {"reboot_longkey", 78},
     {"powerkey", 79},
-    {"usb", 80},
-    {"wdt", 81},
-    {"tool_by_pass_pwk", 82},
-    {"2sec_reboot", 83},
+    {"usb", 80},               // Mediatek
+    {"wdt", 81},               // Mediatek
+    {"tool_by_pass_pwk", 82},  // Mediatek
+    {"2sec_reboot", 83},       // Mediatek
     {"reboot,by_key", 84},
     {"reboot,longkey", 85},
-    {"reboot,2sec", 86},
+    {"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
-    {"usb", 90},
+    {"reboot,rescueparty", 90},
     {"charge", 91},
     {"oem_tz_crash", 92},
-    {"uvlo", 93},
+    {"uvlo", 93},  // aliasReasons converts to reboot,undervoltage
     {"oem_ps_hold", 94},
     {"abnormal_reset", 95},
     {"oemerr_unknown", 96},
@@ -244,9 +231,9 @@
     {"watchdog_nonsec", 102},
     {"watchdog_apps_bark", 103},
     {"reboot_dmverity_corrupted", 104},
-    {"reboot_smpl", 105},
+    {"reboot_smpl", 105},  // aliasReasons converts to reboot,powerloss
     {"watchdog_sdi_apps_reset", 106},
-    {"smpl", 107},
+    {"smpl", 107},  // aliasReasons converts to reboot,powerloss
     {"oem_modem_failed_to_powerup", 108},
     {"reboot_normal", 109},
     {"oem_lpass_cfg", 110},
@@ -258,8 +245,8 @@
     {"oem_rpm_undef_error", 116},
     {"oem_crash_on_the_lk", 117},
     {"oem_rpm_reset", 118},
-    {"oem_lpass_cfg", 119},
-    {"oem_xpu_ns_error", 120},
+    {"reboot,powerloss", 119},
+    {"reboot,undervoltage", 120},
     {"factory_cable", 121},
     {"oem_ar6320_failed_to_powerup", 122},
     {"watchdog_rpm_bite", 123},
@@ -287,6 +274,43 @@
     {"oem_sdi_err_fatal", 145},
     {"pmic_watchdog", 146},
     {"software_master", 147},
+    {"cold,charger", 148},
+    {"cold,rtc", 149},
+    {"cold,rtc,2sec", 150},   // Mediatek
+    {"reboot,tool", 151},     // Mediatek
+    {"reboot,wdt", 152},      // Mediatek
+    {"reboot,unknown", 153},  // Mediatek
+    {"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},
+    {"recovery,quiescent", 169},
+    {"reboot,quiescent", 170},
+    {"reboot,rtc", 171},
+    {"reboot,dm-verity_device_corrupted", 172},
+    {"reboot,dm-verity_enforcing", 173},
+    {"reboot,keys_clear", 174},
+    {"reboot,pmic_off_fault,.*", 175},
+    {"reboot,pmic_off_s3rst,.*", 176},
+    {"reboot,pmic_off_other,.*", 177},
+    {"reboot,userrequested,fastboot", 178},
+    {"reboot,userrequested,recovery", 179},
+    {"reboot,userrequested,recovery,ui", 180},
+    {"shutdown,userrequested,fastboot", 181},
+    {"shutdown,userrequested,recovery", 182},
+    {"reboot,unknown[0-9]*", 183},
+    {"reboot,longkey,.*", 184},
 };
 
 // Converts a string value representing the reason the system booted to an
@@ -302,6 +326,16 @@
     return kEmptyBootReason;
   }
 
+  for (const auto& [match, id] : kBootReasonMap) {
+    // Regex matches as a minimum require either [, \ or * to be present.
+    if (match.find_first_of("[\\*") == match.npos) continue;
+    // enforce match from beginning to end
+    auto exact = match;
+    if (exact[0] != '^') exact = "^" + exact;
+    if (exact[exact.size() - 1] != '$') exact = exact + "$";
+    if (std::regex_search(boot_reason, std::regex(exact))) return id;
+  }
+
   LOG(INFO) << "Unknown boot reason: " << boot_reason;
   return kUnknownBootReason;
 }
@@ -463,11 +497,13 @@
     }
     return std::string::npos;
   }
+
+  operator const std::string&() const { return console; }
 };
 
 // If bit error match to needle, correct it.
 // Return true if any corrections were discovered and applied.
-bool correctForBer(std::string& reason, const std::string& needle) {
+bool correctForBitError(std::string& reason, const std::string& needle) {
   bool corrected = false;
   if (reason.length() < needle.length()) return corrected;
   const pstoreConsole console(reason);
@@ -485,20 +521,219 @@
   return corrected;
 }
 
+// If bit error match to needle, correct it.
+// Return true if any corrections were discovered and applied.
+// Try again if we can replace underline with spaces.
+bool correctForBitErrorOrUnderline(std::string& reason, const std::string& needle) {
+  bool corrected = correctForBitError(reason, needle);
+  std::string _needle(needle);
+  std::transform(_needle.begin(), _needle.end(), _needle.begin(),
+                 [](char c) { return (c == '_') ? ' ' : c; });
+  if (needle != _needle) {
+    corrected |= correctForBitError(reason, _needle);
+  }
+  return corrected;
+}
+
+// Converts a string value representing the reason the system booted to a
+// string complying with Android system standard reason.
+void transformReason(std::string& reason) {
+  std::transform(reason.begin(), reason.end(), reason.begin(), ::tolower);
+  std::transform(reason.begin(), reason.end(), reason.begin(),
+                 [](char c) { return ::isblank(c) ? '_' : c; });
+  std::transform(reason.begin(), reason.end(), reason.begin(),
+                 [](char c) { return ::isprint(c) ? c : '?'; });
+}
+
+// Check subreasons for reboot,<subreason> kernel_panic,sysrq,<subreason> or
+// kernel_panic,<subreason>.
+//
+// If quoted flag is set, pull out and correct single quoted ('), newline (\n)
+// or unprintable character terminated subreason, pos is supplied just beyond
+// first quote.  if quoted false, pull out and correct newline (\n) or
+// unprintable character terminated subreason.
+//
+// Heuristics to find termination is painted into a corner:
+
+// single bit error for quote ' that we can block.  It is acceptable for
+// the others 7, g in reason.  2/9 chance will miss the terminating quote,
+// but there is always the terminating newline that usually immediately
+// follows to fortify our chances.
+bool likely_single_quote(char c) {
+  switch (static_cast<uint8_t>(c)) {
+    case '\'':         // '\''
+    case '\'' ^ 0x01:  // '&'
+    case '\'' ^ 0x02:  // '%'
+    case '\'' ^ 0x04:  // '#'
+    case '\'' ^ 0x08:  // '/'
+      return true;
+    case '\'' ^ 0x10:  // '7'
+      break;
+    case '\'' ^ 0x20:  // '\a' (unprintable)
+      return true;
+    case '\'' ^ 0x40:  // 'g'
+      break;
+    case '\'' ^ 0x80:  // 0xA7 (unprintable)
+      return true;
+  }
+  return false;
+}
+
+// ::isprint(c) and likely_space() will prevent us from being called for
+// fundamentally printable entries, except for '\r' and '\b'.
+//
+// Except for * and J, single bit errors for \n, all others are non-
+// printable so easy catch.  It is _acceptable_ for *, J or j to exist in
+// the reason string, so 2/9 chance we will miss the terminating newline.
+//
+// NB: J might not be acceptable, except if at the beginning or preceded
+//     with a space, '(' or any of the quotes and their BER aliases.
+// NB: * might not be acceptable, except if at the beginning or preceded
+//     with a space, another *, or any of the quotes or their BER aliases.
+//
+// To reduce the chances to closer to 1/9 is too complicated for the gain.
+bool likely_newline(char c) {
+  switch (static_cast<uint8_t>(c)) {
+    case '\n':         // '\n' (unprintable)
+    case '\n' ^ 0x01:  // '\r' (unprintable)
+    case '\n' ^ 0x02:  // '\b' (unprintable)
+    case '\n' ^ 0x04:  // 0x0E (unprintable)
+    case '\n' ^ 0x08:  // 0x02 (unprintable)
+    case '\n' ^ 0x10:  // 0x1A (unprintable)
+      return true;
+    case '\n' ^ 0x20:  // '*'
+    case '\n' ^ 0x40:  // 'J'
+      break;
+    case '\n' ^ 0x80:  // 0x8A (unprintable)
+      return true;
+  }
+  return false;
+}
+
+// ::isprint(c) will prevent us from being called for all the printable
+// matches below.  If we let unprintables through because of this, they
+// get converted to underscore (_) by the validation phase.
+bool likely_space(char c) {
+  switch (static_cast<uint8_t>(c)) {
+    case ' ':          // ' '
+    case ' ' ^ 0x01:   // '!'
+    case ' ' ^ 0x02:   // '"'
+    case ' ' ^ 0x04:   // '$'
+    case ' ' ^ 0x08:   // '('
+    case ' ' ^ 0x10:   // '0'
+    case ' ' ^ 0x20:   // '\0' (unprintable)
+    case ' ' ^ 0x40:   // 'P'
+    case ' ' ^ 0x80:   // 0xA0 (unprintable)
+    case '\t':         // '\t'
+    case '\t' ^ 0x01:  // '\b' (unprintable) (likely_newline counters)
+    case '\t' ^ 0x02:  // '\v' (unprintable)
+    case '\t' ^ 0x04:  // '\r' (unprintable) (likely_newline counters)
+    case '\t' ^ 0x08:  // 0x01 (unprintable)
+    case '\t' ^ 0x10:  // 0x19 (unprintable)
+    case '\t' ^ 0x20:  // ')'
+    case '\t' ^ 0x40:  // '1'
+    case '\t' ^ 0x80:  // 0x89 (unprintable)
+      return true;
+  }
+  return false;
+}
+
+std::string getSubreason(const std::string& content, size_t pos, bool quoted) {
+  static constexpr size_t max_reason_length = 256;
+
+  std::string subReason(content.substr(pos, max_reason_length));
+  // Correct against any known strings that Bit Error Match
+  for (const auto& s : knownReasons) {
+    correctForBitErrorOrUnderline(subReason, s);
+  }
+  std::string terminator(quoted ? "'" : "");
+  for (const auto& m : kBootReasonMap) {
+    if (m.first.length() <= strlen("cold")) continue;  // too short?
+    if (correctForBitErrorOrUnderline(subReason, m.first + terminator)) continue;
+    if (m.first.length() <= strlen("reboot,cold")) continue;  // short?
+    if (android::base::StartsWith(m.first, "reboot,")) {
+      correctForBitErrorOrUnderline(subReason, m.first.substr(strlen("reboot,")) + terminator);
+    } else if (android::base::StartsWith(m.first, "kernel_panic,sysrq,")) {
+      correctForBitErrorOrUnderline(subReason,
+                                    m.first.substr(strlen("kernel_panic,sysrq,")) + terminator);
+    } else if (android::base::StartsWith(m.first, "kernel_panic,")) {
+      correctForBitErrorOrUnderline(subReason, m.first.substr(strlen("kernel_panic,")) + terminator);
+    }
+  }
+  for (pos = 0; pos < subReason.length(); ++pos) {
+    char c = subReason[pos];
+    if (!(::isprint(c) || likely_space(c)) || likely_newline(c) ||
+        (quoted && likely_single_quote(c))) {
+      subReason.erase(pos);
+      break;
+    }
+  }
+  transformReason(subReason);
+  return subReason;
+}
+
 bool addKernelPanicSubReason(const pstoreConsole& console, std::string& ret) {
   // Check for kernel panic types to refine information
-  if (console.rfind("SysRq : Trigger a crash") != std::string::npos) {
-    // Can not happen, except on userdebug, during testing/debugging.
+  if ((console.rfind("SysRq : Trigger a crash") != std::string::npos) ||
+      (console.rfind("PC is at sysrq_handle_crash+") != std::string::npos)) {
     ret = "kernel_panic,sysrq";
+    // Invented for Android to allow daemons that specifically trigger sysrq
+    // to communicate more accurate boot subreasons via last console messages.
+    static constexpr char sysrqSubreason[] = "SysRq : Trigger a crash : '";
+    auto pos = console.rfind(sysrqSubreason);
+    if (pos != std::string::npos) {
+      ret += "," + getSubreason(console, pos + strlen(sysrqSubreason), /* quoted */ true);
+    }
     return true;
   }
   if (console.rfind("Unable to handle kernel NULL pointer dereference at virtual address") !=
       std::string::npos) {
-    ret = "kernel_panic,NULL";
+    ret = "kernel_panic,null";
     return true;
   }
   if (console.rfind("Kernel BUG at ") != std::string::npos) {
-    ret = "kernel_panic,BUG";
+    ret = "kernel_panic,bug";
+    return true;
+  }
+
+  std::string panic("Kernel panic - not syncing: ");
+  auto pos = console.rfind(panic);
+  if (pos != std::string::npos) {
+    static const std::vector<std::pair<const std::string, const std::string>> panicReasons = {
+        {"Out of memory", "oom"},
+        {"out of memory", "oom"},
+        {"Oh boy, that early out of memory", "oom"},  // omg
+        {"BUG!", "bug"},
+        {"hung_task: blocked tasks", "hung"},
+        {"audit: ", "audit"},
+        {"scheduling while atomic", "atomic"},
+        {"Attempted to kill init!", "init"},
+        {"Requested init", "init"},
+        {"No working init", "init"},
+        {"Could not decompress init", "init"},
+        {"RCU Stall", "hung,rcu"},
+        {"stack-protector", "stack"},
+        {"kernel stack overflow", "stack"},
+        {"Corrupt kernel stack", "stack"},
+        {"low stack detected", "stack"},
+        {"corrupted stack end", "stack"},
+        {"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;
@@ -508,33 +743,58 @@
   return addKernelPanicSubReason(pstoreConsole(content), ret);
 }
 
-// std::transform Helper callback functions:
-// Converts a string value representing the reason the system booted to a
-// string complying with Android system standard reason.
-char tounderline(char c) {
-  return ::isblank(c) ? '_' : c;
-}
-
-char toprintable(char c) {
-  return ::isprint(c) ? c : '?';
-}
-
-// Cleanup boot_reason regarding acceptable character set
-void transformReason(std::string& reason) {
-  std::transform(reason.begin(), reason.end(), reason.begin(), ::tolower);
-  std::transform(reason.begin(), reason.end(), reason.begin(), tounderline);
-  std::transform(reason.begin(), reason.end(), reason.begin(), toprintable);
-}
-
 const char system_reboot_reason_property[] = "sys.boot.reason";
 const char last_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY;
+const char 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 = android::base::GetProperty(system_reboot_reason_property, "");
+  if (!android::base::SetProperty(system_reboot_reason_property, system_boot_reason)) {
+    android::base::SetProperty(system_reboot_reason_property,
+                               system_boot_reason.substr(0, PROPERTY_VALUE_MAX - 1));
+  }
+  auto reason_history =
+      android::base::Split(android::base::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 (!android::base::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) {
-  static const size_t max_reason_length = 256;
-
-  std::string ret(GetProperty(system_reboot_reason_property));
+  auto ret = android::base::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 = "";
@@ -568,26 +828,43 @@
     // A series of checks to take some officially unsupported reasons
     // reported by the bootloader and find some logical and canonical
     // sense.  In an ideal world, we would require those bootloaders
-    // to behave and follow our standards.
+    // to behave and follow our CTS standards.
+    //
+    // first member is the output
+    // second member is an unanchored regex for an alias
+    //
+    // If output has a prefix of <bang> '!', we do not use it as a
+    // match needle (and drop the <bang> prefix when landing in output),
+    // otherwise look for it as well. This helps keep the scale of the
+    // following table smaller.
     static const std::vector<std::pair<const std::string, const std::string>> aliasReasons = {
         {"watchdog", "wdog"},
-        {"cold,powerkey", "powerkey"},
+        {"cold,powerkey", "powerkey|power_key|PowerKey"},
         {"kernel_panic", "panic"},
         {"shutdown,thermal", "thermal"},
         {"warm,s3_wakeup", "s3_wakeup"},
         {"hard,hw_reset", "hw_reset"},
-        {"reboot,2sec", "2sec_reboot"},
+        {"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"},
+        {"!reboot,undervoltage", "uvlo"},
+        {"!reboot,powerloss", "smpl"},
         {"bootloader", ""},
     };
 
-    // Either the primary or alias is found _somewhere_ in the reason string.
     for (auto& s : aliasReasons) {
-      if (reason.find(s.first) != std::string::npos) {
+      size_t firstHasNot = s.first[0] == '!';
+      if (!firstHasNot && (reason.find(s.first) != std::string::npos)) {
         ret = s.first;
         break;
       }
-      if (s.second.size() && (reason.find(s.second) != std::string::npos)) {
-        ret = s.first;
+      if (s.second.size() && std::regex_search(reason, std::regex(s.second))) {
+        ret = s.first.substr(firstHasNot);
         break;
       }
     }
@@ -626,28 +903,7 @@
       static const char cmd[] = "reboot: Restarting system with command '";
       size_t pos = console.rfind(cmd);
       if (pos != std::string::npos) {
-        pos += strlen(cmd);
-        std::string subReason(content.substr(pos, max_reason_length));
-        // Correct against any known strings that Bit Error Match
-        for (const auto& s : knownReasons) {
-          correctForBer(subReason, s);
-        }
-        for (const auto& m : kBootReasonMap) {
-          if (m.first.length() <= strlen("cold")) continue;  // too short?
-          if (correctForBer(subReason, m.first + "'")) continue;
-          if (m.first.length() <= strlen("reboot,cold")) continue;  // short?
-          if (!android::base::StartsWith(m.first, "reboot,")) continue;
-          correctForBer(subReason, m.first.substr(strlen("reboot,")) + "'");
-        }
-        for (pos = 0; pos < subReason.length(); ++pos) {
-          char c = subReason[pos];
-          // #, &, %, / are common single bit error for ' that we can block
-          if (!::isprint(c) || (c == '\'') || (c == '#') || (c == '&') || (c == '%') || (c == '/')) {
-            subReason.erase(pos);
-            break;
-          }
-        }
-        transformReason(subReason);
+        std::string subReason(getSubreason(content, pos + strlen(cmd), /* quoted */ true));
         if (subReason != "") {  // Will not land "reboot" as that is too blunt.
           if (isKernelRebootReason(subReason)) {
             ret = "reboot," + subReason;  // User space can't talk kernel reasons.
@@ -657,6 +913,10 @@
             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.
@@ -668,109 +928,13 @@
       }
     }
 
-    // The following battery test should migrate to a default system health HAL
-
-    // Let us not worry if the reboot command was issued, for the cases of
-    // reboot -p, reboot <no reason>, reboot cold, reboot warm and reboot hard.
-    // Same for bootloader and ro.boot.bootreasons of this set, but a dead
-    // battery could conceivably lead to these, so worthy of override.
-    if (isBluntRebootReason(ret)) {
-      // Heuristic to determine if shutdown possibly because of a dead battery?
-      // Really a hail-mary pass to find it in last klog content ...
-      static const int battery_dead_threshold = 2;  // percent
-      static const char battery[] = "healthd: battery l=";
-      const pstoreConsole console(content);
-      size_t pos = console.rfind(battery);  // last one
-      std::string digits;
-      if (pos != std::string::npos) {
-        digits = content.substr(pos + strlen(battery), strlen("100 "));
-        // correct common errors
-        correctForBer(digits, "100 ");
-        if (digits[0] == '!') digits[0] = '1';
-        if (digits[1] == '!') digits[1] = '1';
-      }
-      const char* endptr = digits.c_str();
-      unsigned level = 0;
-      while (::isdigit(*endptr)) {
-        level *= 10;
-        level += *endptr++ - '0';
-        // make sure no leading zeros, except zero itself, and range check.
-        if ((level == 0) || (level > 100)) break;
-      }
-      // example bit error rate issues for 10%
-      //   'l=10 ' no bits in error
-      //   'l=00 ' single bit error (fails above)
-      //   'l=1  ' single bit error
-      //   'l=0  ' double bit error
-      // There are others, not typically critical because of 2%
-      // battery_dead_threshold. KISS check, make sure second
-      // character after digit sequence is not a space.
-      if ((level <= 100) && (endptr != digits.c_str()) && (endptr[0] == ' ') && (endptr[1] != ' ')) {
-        LOG(INFO) << "Battery level at shutdown " << level << "%";
-        if (level <= battery_dead_threshold) {
-          ret = "shutdown,battery";
-        }
-      } else {        // Most likely
-        digits = "";  // reset digits
-
-        // Content buffer no longer will have console data. Beware if more
-        // checks added below, that depend on parsing console content.
-        content = "";
-
-        LOG(DEBUG) << "Can not find last low battery in last console messages";
-        android_logcat_context ctx = create_android_logcat();
-        FILE* fp = android_logcat_popen(&ctx, "logcat -b kernel -v brief -d");
-        if (fp != nullptr) {
-          android::base::ReadFdToString(fileno(fp), &content);
-        }
-        android_logcat_pclose(&ctx, fp);
-        static const char logcat_battery[] = "W/healthd (    0): battery l=";
-        const char* match = logcat_battery;
-
-        if (content == "") {
-          // Service logd.klog not running, go to smaller buffer in the kernel.
-          int rc = klogctl(KLOG_SIZE_BUFFER, nullptr, 0);
-          if (rc > 0) {
-            ssize_t len = rc + 1024;  // 1K Margin should it grow between calls.
-            std::unique_ptr<char[]> buf(new char[len]);
-            rc = klogctl(KLOG_READ_ALL, buf.get(), len);
-            if (rc < len) {
-              len = rc + 1;
-            }
-            buf[--len] = '\0';
-            content = buf.get();
-          }
-          match = battery;
-        }
-
-        pos = content.find(match);  // The first one it finds.
-        if (pos != std::string::npos) {
-          digits = content.substr(pos + strlen(match), strlen("100 "));
-        }
-        endptr = digits.c_str();
-        level = 0;
-        while (::isdigit(*endptr)) {
-          level *= 10;
-          level += *endptr++ - '0';
-          // make sure no leading zeros, except zero itself, and range check.
-          if ((level == 0) || (level > 100)) break;
-        }
-        if ((level <= 100) && (endptr != digits.c_str()) && (*endptr == ' ')) {
-          LOG(INFO) << "Battery level at startup " << level << "%";
-          if (level <= battery_dead_threshold) {
-            ret = "shutdown,battery";
-          }
-        } else {
-          LOG(DEBUG) << "Can not find first battery level in dmesg or logcat";
-        }
-      }
-    }
+    // 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);
+      content = android::base::GetProperty(last_reboot_reason_property, "");
       transformReason(content);
 
       // Anything in last is better than 'super-blunt' reboot or shutdown.
@@ -801,10 +965,6 @@
   }
 
   LOG(INFO) << "Canonical boot reason: " << ret;
-  if (isKernelRebootReason(ret) && (GetProperty(last_reboot_reason_property) != "")) {
-    // Rewrite as it must be old news, kernel reasons trump user space.
-    SetProperty(last_reboot_reason_property, ret);
-  }
   return ret;
 }
 
@@ -818,7 +978,7 @@
   static const std::string kBuildDateKey = "build_date";
   std::string boot_complete_prefix = "boot_complete";
 
-  std::string build_date_str = GetProperty("ro.build.date.utc");
+  auto build_date_str = android::base::GetProperty("ro.build.date.utc", "");
   int32_t build_date;
   if (!android::base::ParseInt(build_date_str, &build_date)) {
     return std::string();
@@ -829,13 +989,11 @@
   if (!boot_event_store.GetBootEvent(kBuildDateKey, &record)) {
     boot_complete_prefix = "factory_reset_" + boot_complete_prefix;
     boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
-    LOG(INFO) << "Canonical boot reason: reboot,factory_reset";
-    SetProperty(system_reboot_reason_property, "reboot,factory_reset");
+    BootReasonAddToHistory("reboot,factory_reset");
   } else if (build_date != record.second) {
     boot_complete_prefix = "ota_" + boot_complete_prefix;
     boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
-    LOG(INFO) << "Canonical boot reason: reboot,ota";
-    SetProperty(system_reboot_reason_property, "reboot,ota");
+    BootReasonAddToHistory("reboot,ota");
   }
 
   return boot_complete_prefix;
@@ -843,7 +1001,7 @@
 
 // Records the value of a given ro.boottime.init property in milliseconds.
 void RecordInitBootTimeProp(BootEventRecordStore* boot_event_store, const char* property) {
-  std::string value = GetProperty(property);
+  auto value = android::base::GetProperty(property, "");
 
   int32_t time_in_ms;
   if (android::base::ParseInt(value, &time_in_ms)) {
@@ -861,7 +1019,7 @@
 
   // |ro.boot.boottime| is of the form 'stage1:time1,...,stageN:timeN',
   // where timeN is in milliseconds.
-  std::string value = GetProperty("ro.boot.boottime");
+  auto value = android::base::GetProperty("ro.boot.boottime", "");
   if (value.empty()) {
     // ro.boot.boottime is not reported on all devices.
     return BootloaderTimingMap();
@@ -873,6 +1031,7 @@
     auto stageTimingValues = android::base::Split(stageTiming, ":");
     DCHECK_EQ(2U, stageTimingValues.size());
 
+    if (stageTimingValues.size() < 2) continue;
     std::string stageName = stageTimingValues[0];
     int32_t time_ms;
     if (android::base::ParseInt(stageTimingValues[1], &time_ms)) {
@@ -934,17 +1093,8 @@
 void LogBootInfoToStatsd(std::chrono::milliseconds end_time,
                          std::chrono::milliseconds total_duration, int32_t bootloader_duration_ms,
                          double time_since_last_boot_sec) {
-  const std::string reason(GetProperty(bootloader_reboot_reason_property));
-
-  if (reason.empty()) {
-    android::util::stats_write(android::util::BOOT_SEQUENCE_REPORTED, "<EMPTY>", "<EMPTY>",
-                               end_time.count(), total_duration.count(),
-                               (int64_t)bootloader_duration_ms,
-                               (int64_t)time_since_last_boot_sec * 1000);
-    return;
-  }
-
-  const std::string system_reason(GetProperty(system_reboot_reason_property));
+  const auto reason = android::base::GetProperty(bootloader_reboot_reason_property, "<EMPTY>");
+  const auto system_reason = android::base::GetProperty(system_reboot_reason_property, "<EMPTY>");
   android::util::stats_write(android::util::BOOT_SEQUENCE_REPORTED, reason.c_str(),
                              system_reason.c_str(), end_time.count(), total_duration.count(),
                              (int64_t)bootloader_duration_ms,
@@ -952,10 +1102,20 @@
 }
 
 void SetSystemBootReason() {
-  const std::string bootloader_boot_reason(GetProperty(bootloader_reboot_reason_property));
+  const auto bootloader_boot_reason =
+      android::base::GetProperty(bootloader_reboot_reason_property, "");
   const std::string system_boot_reason(BootReasonStrToReason(bootloader_boot_reason));
   // Record the scrubbed system_boot_reason to the property
-  SetProperty(system_reboot_reason_property, system_boot_reason);
+  BootReasonAddToHistory(system_boot_reason);
+  // Shift last_reboot_reason_property to last_last_reboot_reason_property
+  auto last_boot_reason = android::base::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);
+  }
+  android::base::SetProperty(last_last_reboot_reason_property, last_boot_reason);
+  android::base::SetProperty(last_reboot_reason_property, "");
 }
 
 // Gets the boot time offset. This is useful when Android is running in a
@@ -1021,6 +1181,7 @@
   boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime_s.count());
 
   RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init");
+  RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.first_stage");
   RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.selinux");
   RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.cold_boot_wait");
 
@@ -1042,7 +1203,7 @@
 // Records the boot_reason metric by querying the ro.boot.bootreason system
 // property.
 void RecordBootReason() {
-  const std::string reason(GetProperty(bootloader_reboot_reason_property));
+  const auto reason = android::base::GetProperty(bootloader_reboot_reason_property, "");
 
   if (reason.empty()) {
     // Log an empty boot reason value as '<EMPTY>' to ensure the value is intentional
@@ -1060,12 +1221,12 @@
   boot_event_store.AddBootEventWithValue("boot_reason", boot_reason);
 
   // Log the scrubbed system_boot_reason.
-  const std::string system_reason(GetProperty(system_reboot_reason_property));
+  const auto system_reason = android::base::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);
+    android::base::SetProperty(bootloader_reboot_reason_property, system_reason);
   }
 }
 
@@ -1119,6 +1280,19 @@
   boot_event_store.AddBootEventWithValue("time_since_factory_reset", time_since_factory_reset);
 }
 
+// List the associated boot reason(s), if arg is nullptr then all.
+void PrintBootReasonEnum(const char* arg) {
+  int value = -1;
+  if (arg != nullptr) {
+    value = BootReasonStrToEnum(arg);
+  }
+  for (const auto& [match, id] : kBootReasonMap) {
+    if ((value < 0) || (value == id)) {
+      printf("%u\t%s\n", id, match.c_str());
+    }
+  }
+}
+
 }  // namespace
 
 int main(int argc, char** argv) {
@@ -1133,6 +1307,7 @@
   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 char boot_reason_enum_str[] = "boot_reason_enum";
   static const struct option long_options[] = {
       // clang-format off
       { "help",                 no_argument,       NULL,   'h' },
@@ -1144,6 +1319,7 @@
       { boot_complete_str,      no_argument,       NULL,   0 },
       { boot_reason_str,        no_argument,       NULL,   0 },
       { factory_reset_str,      no_argument,       NULL,   0 },
+      { boot_reason_enum_str,   optional_argument, NULL,   0 },
       { NULL,                   0,                 NULL,   0 }
       // clang-format on
   };
@@ -1168,6 +1344,8 @@
           RecordBootReason();
         } else if (option_name == factory_reset_str) {
           RecordFactoryReset();
+        } else if (option_name == boot_reason_enum_str) {
+          PrintBootReasonEnum(optarg);
         } else {
           LOG(ERROR) << "Invalid option: " << option_name;
         }
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index 1300a27..85caf25 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -1,7 +1,9 @@
 # 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=*
+# Mirror bootloader boot reason to system boot reason
+# ro.boot.bootreason should be set by init already
+# before post-fs trigger
+on post-fs && property:ro.boot.bootreason=*
     setprop sys.boot.reason ${ro.boot.bootreason}
 
 on post-fs-data
@@ -66,11 +68,16 @@
 on property:init.svc.zygote=stopping
     setprop sys.logbootcomplete 0
 
+# Set boot reason
+on zygote-start
+    # Converts bootloader boot reason and persist.sys.boot.reason to system boot reason
+    # Need go after persist peroperties are loaded which is right before zygote-start trigger
+    exec_background - system log -- /system/bin/bootstat --set_system_boot_reason
+
 # 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).
     # Record the boot reason.
     # Record time since factory reset.
     # Log all boot events.
-    exec_background - system log -- /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
+    exec_background - system log -- /system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
diff --git a/cpio/Android.bp b/cpio/Android.bp
new file mode 100644
index 0000000..baa0319
--- /dev/null
+++ b/cpio/Android.bp
@@ -0,0 +1,11 @@
+// Copyright 2005 The Android Open Source Project
+
+cc_binary_host {
+    name: "mkbootfs",
+    srcs: ["mkbootfs.c"],
+    cflags: ["-Werror"],
+    shared_libs: ["libcutils"],
+    dist: {
+        targets: ["dist_files"],
+    },
+}
diff --git a/cpio/Android.mk b/cpio/Android.mk
deleted file mode 100644
index 2aa7297..0000000
--- a/cpio/Android.mk
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright 2005 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-	mkbootfs.c
-
-LOCAL_MODULE := mkbootfs
-
-LOCAL_CFLAGS := -Werror
-
-LOCAL_SHARED_LIBRARIES := libcutils
-
-include $(BUILD_HOST_EXECUTABLE)
-
-$(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE))
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 7e6f24d..2e226da 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -9,7 +9,6 @@
         "-Wno-nullability-completeness",
         "-Os",
     ],
-    cpp_std: "gnu++17",
 
     local_include_dirs: ["include"],
 }
@@ -17,6 +16,7 @@
 cc_library_headers {
     name: "libdebuggerd_common_headers",
     export_include_dirs: ["common/include"],
+    recovery_available: true,
 }
 
 cc_library_shared {
@@ -42,10 +42,11 @@
     export_include_dirs: ["tombstoned/include"],
 }
 
-// Utility library to tombstoned and get an output fd.
+// Utility library to talk 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",
@@ -67,6 +68,7 @@
 cc_library_static {
     name: "libdebuggerd_handler_core",
     defaults: ["debuggerd_defaults"],
+    recovery_available: true,
     srcs: ["handler/debuggerd_handler.cpp"],
 
     header_libs: [
@@ -101,6 +103,7 @@
 cc_library_static {
     name: "libdebuggerd_handler_fallback",
     defaults: ["debuggerd_defaults"],
+    recovery_available: true,
     srcs: [
         "handler/debuggerd_fallback.cpp",
     ],
@@ -111,13 +114,18 @@
         "libasync_safe",
         "libbase",
         "libdebuggerd",
-        "libbacktrace",
-        "libunwind",
         "libunwindstack",
-        "libdexfile",
+        "libdexfile_support_static",  // libunwindstack dependency
         "liblzma",
         "libcutils",
     ],
+    target: {
+        recovery: {
+            exclude_static_libs: [
+                "libdexfile_support_static",
+            ],
+        },
+    },
 
     export_include_dirs: ["include"],
 }
@@ -135,6 +143,7 @@
     shared_libs: [
         "libbase",
         "libcutils",
+        "libprocinfo",
     ],
 
     export_header_lib_headers: ["libdebuggerd_common_headers"],
@@ -144,10 +153,10 @@
 cc_library_static {
     name: "libdebuggerd",
     defaults: ["debuggerd_defaults"],
+    recovery_available: true,
 
     srcs: [
         "libdebuggerd/backtrace.cpp",
-        "libdebuggerd/elf_utils.cpp",
         "libdebuggerd/open_files_list.cpp",
         "libdebuggerd/tombstone.cpp",
         "libdebuggerd/utility.cpp",
@@ -156,15 +165,30 @@
     local_include_dirs: ["libdebuggerd/include"],
     export_include_dirs: ["libdebuggerd/include"],
 
+    // Needed for private/bionic_fdsan.h
+    include_dirs: ["bionic/libc"],
+
     static_libs: [
-        "libbacktrace",
-        "libunwind",
+        "libdexfile_support_static",  // libunwindstack dependency
         "libunwindstack",
         "liblzma",
         "libbase",
         "libcutils",
         "liblog",
     ],
+    target: {
+        recovery: {
+            exclude_static_libs: [
+                "libdexfile_support_static",
+            ],
+        },
+    },
+
+    product_variables: {
+        debuggable: {
+            cflags: ["-DROOT_POSSIBLE"],
+        },
+    },
 }
 
 cc_test {
@@ -194,18 +218,17 @@
     },
 
     shared_libs: [
-        "libbacktrace",
         "libbase",
         "libcutils",
         "libdebuggerd_client",
         "liblog",
         "libminijail",
         "libnativehelper",
+        "libunwindstack",
     ],
 
     static_libs: [
         "libdebuggerd",
-        "libunwindstack",
     ],
 
     local_include_dirs: [
@@ -221,6 +244,8 @@
             stem: "debuggerd_test64",
         },
     },
+
+    test_suites: ["device-tests"],
 }
 
 cc_benchmark {
@@ -258,7 +283,6 @@
     ],
 
     shared_libs: [
-        "libbacktrace",
         "libbase",
         "liblog",
         "libprocinfo",
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index cb7cbbe..60eb241 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -26,6 +26,7 @@
 
 #include <chrono>
 
+#include <android-base/cmsg.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
@@ -33,6 +34,7 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
+#include <procinfo/process.h>
 
 #include "debuggerd/handler.h"
 #include "protocol.h"
@@ -40,6 +42,7 @@
 
 using namespace std::chrono_literals;
 
+using android::base::SendFileDescriptors;
 using android::base::unique_fd;
 
 static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) {
@@ -62,8 +65,20 @@
   tv->tv_usec = static_cast<long>(microseconds.count());
 }
 
-bool debuggerd_trigger_dump(pid_t pid, DebuggerdDumpType dump_type, unsigned int timeout_ms,
+bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int timeout_ms,
                             unique_fd output_fd) {
+  pid_t pid = tid;
+  if (dump_type == kDebuggerdJavaBacktrace) {
+    // Java dumps always get sent to the tgid, so we need to resolve our tid to a tgid.
+    android::procinfo::ProcessInfo procinfo;
+    std::string error;
+    if (!android::procinfo::GetProcessInfo(tid, &procinfo, &error)) {
+      LOG(ERROR) << "libdebugged_client: failed to get process info: " << error;
+      return false;
+    }
+    pid = procinfo.pid;
+  }
+
   LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
   unique_fd sockfd;
   const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
@@ -133,22 +148,27 @@
     PLOG(ERROR) << "failed to set pipe buffer size";
   }
 
-  if (send_fd(set_timeout(sockfd), &req, sizeof(req), std::move(pipe_write)) != sizeof(req)) {
+  ssize_t rc = SendFileDescriptors(set_timeout(sockfd), &req, sizeof(req), pipe_write.get());
+  pipe_write.reset();
+  if (rc != sizeof(req)) {
     PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
     return false;
   }
 
   // Check to make sure we've successfully registered.
   InterceptResponse response;
-  ssize_t rc =
-      TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
+  rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
   if (rc == 0) {
-    LOG(ERROR) << "libdebuggerd_client: failed to read response from tombstoned: timeout reached?";
+    LOG(ERROR) << "libdebuggerd_client: failed to read initial response from tombstoned: "
+               << "timeout reached?";
+    return false;
+  } else if (rc == -1) {
+    PLOG(ERROR) << "libdebuggerd_client: failed to read initial response from tombstoned";
     return false;
   } else if (rc != sizeof(response)) {
-    LOG(ERROR)
-        << "libdebuggerd_client: received packet of unexpected length from tombstoned: expected "
-        << sizeof(response) << ", received " << rc;
+    LOG(ERROR) << "libdebuggerd_client: received packet of unexpected length from tombstoned while "
+                  "reading initial response: expected "
+               << sizeof(response) << ", received " << rc;
     return false;
   }
 
@@ -158,18 +178,22 @@
     return false;
   }
 
-  if (!send_signal(pid, dump_type)) {
+  if (!send_signal(tid, dump_type)) {
     return false;
   }
 
   rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
   if (rc == 0) {
-    LOG(ERROR) << "libdebuggerd_client: failed to read response from tombstoned: timeout reached?";
+    LOG(ERROR) << "libdebuggerd_client: failed to read status response from tombstoned: "
+                  "timeout reached?";
+    return false;
+  } else if (rc == -1) {
+    PLOG(ERROR) << "libdebuggerd_client: failed to read status response from tombstoned";
     return false;
   } else if (rc != sizeof(response)) {
-    LOG(ERROR)
-      << "libdebuggerd_client: received packet of unexpected length from tombstoned: expected "
-      << sizeof(response) << ", received " << rc;
+    LOG(ERROR) << "libdebuggerd_client: received packet of unexpected length from tombstoned while "
+                  "reading confirmation response: expected "
+               << sizeof(response) << ", received " << rc;
     return false;
   }
 
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 40cf6c3..cb55745 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -34,6 +34,7 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/macros.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -47,7 +48,12 @@
 #define ATRACE_TAG ATRACE_TAG_BIONIC
 #include <utils/Trace.h>
 
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
 
 #include "libdebuggerd/backtrace.h"
 #include "libdebuggerd/tombstone.h"
@@ -62,8 +68,6 @@
 using android::base::unique_fd;
 using android::base::StringPrintf;
 
-using unwindstack::Regs;
-
 static bool pid_contains_tid(int pid_proc_fd, pid_t tid) {
   struct stat st;
   std::string task_path = StringPrintf("task/%d", tid);
@@ -249,24 +253,50 @@
 }
 
 static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,
-                          std::unique_ptr<unwindstack::Regs>* regs, uintptr_t* abort_address) {
+                          std::unique_ptr<unwindstack::Regs>* regs, uintptr_t* abort_msg_address,
+                          uintptr_t* fdsan_table_address) {
   std::aligned_storage<sizeof(CrashInfo) + 1, alignof(CrashInfo)>::type buf;
+  CrashInfo* crash_info = reinterpret_cast<CrashInfo*>(&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);
+  } else {
+    ssize_t expected_size = 0;
+    switch (crash_info->header.version) {
+      case 1:
+        expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV1);
+        break;
+
+      case 2:
+        expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV2);
+        break;
+
+      default:
+        LOG(FATAL) << "unexpected CrashInfo version: " << crash_info->header.version;
+        break;
+    };
+
+    if (rc != expected_size) {
+      LOG(FATAL) << "read " << rc << " bytes when reading target crash information, expected "
+                 << expected_size;
+    }
   }
 
-  CrashInfo* crash_info = reinterpret_cast<CrashInfo*>(&buf);
-  if (crash_info->version != 1) {
-    LOG(FATAL) << "version mismatch, expected 1, received " << crash_info->version;
-  }
+  *fdsan_table_address = 0;
+  switch (crash_info->header.version) {
+    case 2:
+      *fdsan_table_address = crash_info->data.v2.fdsan_table_address;
+      FALLTHROUGH_INTENDED;
+    case 1:
+      *abort_msg_address = crash_info->data.v1.abort_msg_address;
+      *siginfo = crash_info->data.v1.siginfo;
+      regs->reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(),
+                                                        &crash_info->data.v1.ucontext));
+      break;
 
-  *siginfo = crash_info->siginfo;
-  regs->reset(Regs::CreateFromUcontext(Regs::CurrentArch(), &crash_info->ucontext));
-  *abort_address = crash_info->abort_msg_address;
+    default:
+      __builtin_unreachable();
+  }
 }
 
 // Wait for a process to clone and return the child's pid.
@@ -322,8 +352,22 @@
   return vm_pid;
 }
 
+static void InstallSigPipeHandler() {
+  struct sigaction action = {};
+  action.sa_handler = SIG_IGN;
+  action.sa_flags = SA_RESTART;
+  sigaction(SIGPIPE, &action, nullptr);
+}
+
 int main(int argc, char** argv) {
   DefuseSignalHandlers();
+  InstallSigPipeHandler();
+
+  // There appears to be a bug in the kernel where our death causes SIGHUP to
+  // be sent to our process group if we exit while it has stopped jobs (e.g.
+  // because of wait_for_gdb). Use setsid to create a new process group to
+  // avoid hitting this.
+  setsid();
 
   atrace_begin(ATRACE_TAG, "before reparent");
   pid_t target_process = getppid();
@@ -369,7 +413,8 @@
   ATRACE_NAME("after reparent");
   pid_t pseudothread_tid;
   DebuggerdDumpType dump_type;
-  uintptr_t abort_address = 0;
+  uintptr_t abort_msg_address = 0;
+  uintptr_t fdsan_table_address = 0;
 
   Initialize(argv);
   ParseArgs(argc, argv, &pseudothread_tid, &dump_type);
@@ -387,7 +432,7 @@
   OpenFilesList open_files;
   {
     ATRACE_NAME("open files");
-    populate_open_files_list(g_target_thread, &open_files);
+    populate_open_files_list(&open_files, g_target_thread);
   }
 
   // In order to reduce the duration that we pause the process for, we ptrace
@@ -418,6 +463,7 @@
       ThreadInfo info;
       info.pid = target_process;
       info.tid = thread;
+      info.uid = getuid();
       info.process_name = process_name;
       info.thread_name = get_thread_name(thread);
 
@@ -429,11 +475,12 @@
 
       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);
+        ReadCrashInfo(input_pipe, &siginfo, &info.registers, &abort_msg_address,
+                      &fdsan_table_address);
         info.siginfo = &siginfo;
         info.signo = info.siginfo->si_signo;
       } else {
-        info.registers.reset(Regs::RemoteGet(thread));
+        info.registers.reset(unwindstack::Regs::RemoteGet(thread));
         if (!info.registers) {
           PLOG(WARNING) << "failed to fetch registers for thread " << thread;
           ptrace(PTRACE_DETACH, thread, 0, 0);
@@ -504,8 +551,8 @@
     g_output_fd = std::move(devnull);
   }
 
-  LOG(INFO) << "performing dump of process " << target_process << " (target tid = " << g_target_thread
-            << ")";
+  LOG(INFO) << "performing dump of process " << target_process
+            << " (target tid = " << g_target_thread << ")";
 
   int signo = siginfo.si_signo;
   bool fatal_signal = signo != DEBUGGER_SIGNAL;
@@ -526,24 +573,26 @@
   }
 
   // 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";
+  unwindstack::UnwinderFromPid unwinder(256, vm_pid);
+  if (!unwinder.Init(unwindstack::Regs::CurrentArch())) {
+    LOG(FATAL) << "Failed to init unwinder object.";
   }
 
   std::string amfd_data;
   if (backtrace) {
     ATRACE_NAME("dump_backtrace");
-    dump_backtrace(std::move(g_output_fd), map.get(), thread_info, g_target_thread);
+    dump_backtrace(std::move(g_output_fd), &unwinder, thread_info, g_target_thread);
   } else {
-    ATRACE_NAME("engrave_tombstone");
-    engrave_tombstone(std::move(g_output_fd), map.get(), process_memory.get(), thread_info,
-                      g_target_thread, abort_address, &open_files, &amfd_data);
+    {
+      ATRACE_NAME("fdsan table dump");
+      populate_fdsan_table(&open_files, unwinder.GetProcessMemory(), fdsan_table_address);
+    }
+
+    {
+      ATRACE_NAME("engrave_tombstone");
+      engrave_tombstone(std::move(g_output_fd), &unwinder, thread_info, g_target_thread,
+                        abort_msg_address, &open_files, &amfd_data);
+    }
   }
 
   if (fatal_signal) {
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 4b32b9d..3041664 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -183,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");
@@ -191,12 +193,14 @@
     fprintf(stderr, "  kuser_memory_barrier  call kuser_memory_barrier\n");
     fprintf(stderr, "  kuser_cmpxchg64       call kuser_cmpxchg64\n");
 #endif
+    fprintf(stderr, "  xom                   read execute-only memory\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "  LOG_ALWAYS_FATAL      call liblog LOG_ALWAYS_FATAL\n");
     fprintf(stderr, "  LOG_ALWAYS_FATAL_IF   call liblog LOG_ALWAYS_FATAL_IF\n");
     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,7 +225,7 @@
     // Prefixes.
     if (!strncmp(arg, "wait-", strlen("wait-"))) {
       char buf[1];
-      TEMP_FAILURE_RETRY(read(STDIN_FILENO, buf, sizeof(buf)));
+      UNUSED(TEMP_FAILURE_RETRY(read(STDIN_FILENO, buf, sizeof(buf))));
       return do_action(arg + strlen("wait-"));
     } else if (!strncmp(arg, "exhaustfd-", strlen("exhaustfd-"))) {
       errno = 0;
@@ -235,62 +239,87 @@
 
     // 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");
+#if !defined(__clang_analyzer__)
     } else if (!strcasecmp(arg, "fortify")) {
-        char buf[10];
-        __read_chk(-1, buf, 32, 10);
-        while (true) pause();
+      // FORTIFY is disabled when running clang-tidy and other tools, so this
+      // shouldn't depend on internal implementation details of it.
+      char buf[10];
+      __read_chk(-1, buf, 32, 10);
+      while (true) pause();
+#endif
+    } 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();
+      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_system_seccomp_filter();
-        syscall(99999);
+      set_system_seccomp_filter();
+      syscall(99999);
+#if defined(__LP64__)
+    } else if (!strcasecmp(arg, "xom")) {
+      // Try to read part of our code, which will fail if XOM is active.
+      printf("*%lx = %lx\n", reinterpret_cast<long>(usage), *reinterpret_cast<long*>(usage));
+#endif
 #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 b016e23ee..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_test.cpp b/debuggerd/debuggerd_test.cpp
index 397ff2f..1f0e420 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -29,13 +29,16 @@
 #include <regex>
 #include <thread>
 
+#include <android/fdsan.h>
 #include <android/set_abort_message.h>
 
+#include <android-base/cmsg.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/test_utils.h>
 #include <android-base/unique_fd.h>
@@ -51,6 +54,8 @@
 #include "util.h"
 
 using namespace std::chrono_literals;
+
+using android::base::SendFileDescriptors;
 using android::base::unique_fd;
 
 #if defined(__LP64__)
@@ -80,8 +85,13 @@
     return value;                                                  \
   }()
 
+// 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+ \()" frame_name R"(\+)");
+  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) {
@@ -116,12 +126,14 @@
 
   ASSERT_GE(pipe_buffer_size, 1024 * 1024);
 
-  if (send_fd(intercept_fd->get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
+  ssize_t rc = SendFileDescriptors(intercept_fd->get(), &req, sizeof(req), output_pipe_write.get());
+  output_pipe_write.reset();
+  if (rc != sizeof(req)) {
     FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
   }
 
   InterceptResponse response;
-  ssize_t rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
+  rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
   if (rc == -1) {
     FAIL() << "failed to read response from tombstoned: " << strerror(errno);
   } else if (rc == 0) {
@@ -231,9 +243,10 @@
 
 void CrasherTest::AssertDeath(int signo) {
   int status;
-  pid_t pid = TIMEOUT(5, waitpid(crasher_pid, &status, 0));
+  pid_t pid = TIMEOUT(10, waitpid(crasher_pid, &status, 0));
   if (pid != crasher_pid) {
-    printf("failed to wait for crasher (pid %d)\n", crasher_pid);
+    printf("failed to wait for crasher (expected pid %d, return value %d): %s\n", crasher_pid, pid,
+           strerror(errno));
     sleep(100);
     FAIL() << "failed to wait for crasher: " << strerror(errno);
   }
@@ -346,7 +359,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:)");
 }
 
@@ -354,7 +369,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);
@@ -366,7 +388,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) {
@@ -572,9 +594,28 @@
     "/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;
+  std::string policy;
+  if (!android::base::ReadFileToString(kDebuggerdSeccompPolicy, &policy)) {
+    PLOG(FATAL) << "failed to read policy file";
+  }
+
+  // Allow a bunch of syscalls used by the tests.
+  policy += "\nclone: 1";
+  policy += "\nsigaltstack: 1";
+  policy += "\nnanosleep: 1";
+
+  FILE* tmp_file = tmpfile();
+  if (!tmp_file) {
+    PLOG(FATAL) << "tmpfile failed";
+  }
+
+  unique_fd tmp_fd(dup(fileno(tmp_file)));
+  if (!android::base::WriteStringToFd(policy, tmp_fd.get())) {
+    PLOG(FATAL) << "failed to write policy to tmpfile";
+  }
+
+  if (lseek(tmp_fd.get(), 0, SEEK_SET) != 0) {
+    PLOG(FATAL) << "failed to seek tmp_fd";
   }
 
   ScopedMinijail jail{minijail_new()};
@@ -585,7 +626,7 @@
   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());
+  minijail_parse_seccomp_filters_from_fd(jail.get(), tmp_fd.release());
 
   pid_t result = fork();
   if (result == -1) {
@@ -720,6 +761,16 @@
   ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
 }
 
+extern "C" void foo() {
+  LOG(INFO) << "foo";
+  std::this_thread::sleep_for(1s);
+}
+
+extern "C" void bar() {
+  LOG(INFO) << "bar";
+  std::this_thread::sleep_for(1s);
+}
+
 TEST_F(CrasherTest, seccomp_backtrace) {
   int intercept_result;
   unique_fd output_fd;
@@ -727,6 +778,11 @@
   static const auto dump_type = kDebuggerdNativeBacktrace;
   StartProcess(
       []() {
+        std::thread a(foo);
+        std::thread b(bar);
+
+        std::this_thread::sleep_for(100ms);
+
         raise_debugger_signal(dump_type);
         _exit(0);
       },
@@ -741,6 +797,8 @@
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
   ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
+  ASSERT_BACKTRACE_FRAME(result, "foo");
+  ASSERT_BACKTRACE_FRAME(result, "bar");
 }
 
 TEST_F(CrasherTest, seccomp_crash_logcat) {
@@ -787,6 +845,31 @@
   AssertDeath(SIGABRT);
 }
 
+TEST_F(CrasherTest, fdsan_warning_abort_message) {
+  int intercept_result;
+  unique_fd output_fd;
+
+  StartProcess([]() {
+    android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ONCE);
+    unique_fd fd(open("/dev/null", O_RDONLY | O_CLOEXEC));
+    if (fd == -1) {
+      abort();
+    }
+    close(fd.get());
+    _exit(0);
+  });
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(0);
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, "Abort message: 'attempted to close");
+}
+
 TEST(crash_dump, zombie) {
   pid_t forkpid = fork();
 
@@ -977,3 +1060,42 @@
   ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));
   ASSERT_STREQ("any", outbuf);
 }
+
+TEST(tombstoned, interceptless_backtrace) {
+  // Generate 50 backtraces, and then check to see that we haven't created 50 new tombstones.
+  auto get_tombstone_timestamps = []() -> std::map<int, time_t> {
+    std::map<int, time_t> result;
+    for (int i = 0; i < 99; ++i) {
+      std::string path = android::base::StringPrintf("/data/tombstones/tombstone_%02d", i);
+      struct stat st;
+      if (stat(path.c_str(), &st) == 0) {
+        result[i] = st.st_mtim.tv_sec;
+      }
+    }
+    return result;
+  };
+
+  auto before = get_tombstone_timestamps();
+  for (int i = 0; i < 50; ++i) {
+    raise_debugger_signal(kDebuggerdNativeBacktrace);
+  }
+  auto after = get_tombstone_timestamps();
+
+  int diff = 0;
+  for (int i = 0; i < 99; ++i) {
+    if (after.count(i) == 0) {
+      continue;
+    }
+    if (before.count(i) == 0) {
+      ++diff;
+      continue;
+    }
+    if (before[i] != after[i]) {
+      ++diff;
+    }
+  }
+
+  // We can't be sure that nothing's crash looping in the background.
+  // This should be good enough, though...
+  ASSERT_LT(diff, 10) << "too many new tombstones; is something crashing in the background?";
+}
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index dea2e17..bbec612 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -42,11 +42,15 @@
 #include <android-base/file.h>
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
-#include <backtrace/BacktraceMap.h>
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
 
 #include "debuggerd/handler.h"
+#include "handler/fallback.h"
 #include "tombstoned/tombstoned.h"
 #include "util.h"
 
@@ -54,7 +58,6 @@
 #include "libdebuggerd/tombstone.h"
 
 using android::base::unique_fd;
-using unwindstack::Regs;
 
 extern "C" bool __linker_enable_fallback_allocator();
 extern "C" void __linker_disable_fallback_allocator();
@@ -72,17 +75,22 @@
   }
 
   {
-    std::unique_ptr<Regs> regs;
+    std::unique_ptr<unwindstack::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));
+    unwindstack::ArchEnum arch = unwindstack::Regs::CurrentArch();
+    thread.registers.reset(unwindstack::Regs::CreateFromUcontext(arch, 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);
+    unwindstack::UnwinderFromPid unwinder(kMaxFrames, getpid());
+    if (unwinder.Init(arch)) {
+      dump_backtrace_thread(output_fd, &unwinder, thread);
+    } else {
+      async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Unable to init unwinder.");
+    }
   }
   __linker_disable_fallback_allocator();
 }
@@ -187,7 +195,7 @@
 static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
   static std::atomic<uint64_t> trace_output(pack_thread_fd(-1, -1));
 
-  if (info->si_value.sival_int == ~0) {
+  if (info->si_value.sival_ptr == kDebuggerdFallbackSivalPtrRequestDump) {
     // Asked to dump by the original signal recipient.
     uint64_t val = trace_output.load();
     auto [tid, fd] = unpack_thread_fd(val);
@@ -249,17 +257,18 @@
         }
 
         uint64_t expected = pack_thread_fd(-1, -1);
-        if (!trace_output.compare_exchange_strong(expected,
-                                                  pack_thread_fd(tid, pipe_write.release()))) {
+        int sent_fd = pipe_write.release();
+        if (!trace_output.compare_exchange_strong(expected, pack_thread_fd(tid, sent_fd))) {
           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);
+          close(sent_fd);
           return false;
         }
 
         siginfo_t siginfo = {};
         siginfo.si_code = SI_QUEUE;
-        siginfo.si_value.sival_int = ~0;
+        siginfo.si_value.sival_ptr = kDebuggerdFallbackSivalPtrRequestDump;
         siginfo.si_pid = getpid();
         siginfo.si_uid = getuid();
 
@@ -304,7 +313,16 @@
 
   crash_mutex.lock();
   if (lock_count++ > 0) {
-    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "recursed signal handler call, exiting");
+    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);
   }
 
@@ -322,7 +340,7 @@
 
 extern "C" void debuggerd_fallback_handler(siginfo_t* info, ucontext_t* ucontext,
                                            void* abort_message) {
-  if (info->si_signo == DEBUGGER_SIGNAL && info->si_value.sival_int != 0) {
+  if (info->si_signo == DEBUGGER_SIGNAL && info->si_value.sival_ptr != nullptr) {
     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 05e6efa..598ea85 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -58,8 +58,19 @@
 #include "dump_type.h"
 #include "protocol.h"
 
+#include "handler/fallback.h"
+
 using android::base::Pipe;
-using android::base::unique_fd;
+
+// 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)
@@ -99,6 +110,7 @@
   int saved_errno_;
 };
 
+extern "C" void* android_fdsan_get_fd_table();
 extern "C" void debuggerd_fallback_handler(siginfo_t*, ucontext_t*, void*);
 
 static debuggerd_callbacks_t g_callbacks;
@@ -169,24 +181,26 @@
     return;
   }
 
-  const char* signal_name = get_signame(info->si_signo);
-  bool has_address = signal_has_si_addr(info->si_signo, info->si_code);
-
-  // Many signals don't have an address.
+  // Many signals don't have an address or sender.
   char addr_desc[32] = "";  // ", fault addr 0x1234"
-  if (has_address) {
+  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);
+  }
 
   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), code %d (%s)%s in tid %d (%s), pid %d (%s)",
-      info->si_signo, signal_name, info->si_code, get_sigcode(info->si_signo, info->si_code),
-      addr_desc, __gettid(), thread_name, __getpid(), main_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);
 }
 
 /*
@@ -254,8 +268,15 @@
       _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.
+    // crash_dump is ptracing both sides of the fork; it'll let the parent exit,
+    // but keep the orphan stopped to peek at its memory.
+
+    // There appears to be a bug in the kernel where our death causes SIGHUP to
+    // be sent to our process group if we exit while it has stopped jobs (e.g.
+    // because of wait_for_gdb). Use setsid to create a new process group to
+    // avoid hitting this.
+    setsid();
+
     _exit(0);
   }
 
@@ -275,6 +296,7 @@
   siginfo_t* siginfo;
   void* ucontext;
   uintptr_t abort_msg;
+  uintptr_t fdsan_table;
 };
 
 // Logging and contacting debuggerd requires free file descriptors, which we might not have.
@@ -297,7 +319,8 @@
   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));
@@ -318,23 +341,23 @@
   }
 
   // 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);
+  uint32_t version = 2;
+  constexpr size_t expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV2);
 
   errno = 0;
   if (fcntl(output_write.get(), F_SETPIPE_SZ, expected) < static_cast<int>(expected)) {
-    fatal_errno("failed to set pipe bufer size");
+    fatal_errno("failed to set pipe buffer size");
   }
 
-  struct iovec iovs[4] = {
+  struct iovec iovs[5] = {
       {.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)},
+      {.iov_base = &thread_info->fdsan_table, .iov_len = sizeof(uintptr_t)},
   };
 
-  ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, 4));
+  ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, 5));
   if (rc == -1) {
     fatal_errno("failed to write crash info");
   } else if (rc != expected) {
@@ -367,7 +390,9 @@
 
     execle(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type,
            nullptr, nullptr);
-    fatal_errno("exec failed");
+    async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to exec crash_dump helper: %s",
+                          strerror(errno));
+    return 1;
   }
 
   input_write.reset();
@@ -443,14 +468,14 @@
     info = nullptr;
   }
 
-  struct siginfo si = {};
+  struct siginfo dummy_info = {};
   if (!info) {
-    memset(&si, 0, sizeof(si));
-    si.si_signo = signal_number;
-    si.si_code = SI_USER;
-    si.si_pid = __getpid();
-    si.si_uid = getuid();
-    info = &si;
+    memset(&dummy_info, 0, sizeof(dummy_info));
+    dummy_info.si_signo = signal_number;
+    dummy_info.si_code = SI_USER;
+    dummy_info.si_pid = __getpid();
+    dummy_info.si_uid = getuid();
+    info = &dummy_info;
   } else if (info->si_code >= 0 || info->si_code == SI_TKILL) {
     // rt_tgsigqueueinfo(2)'s documentation appears to be incorrect on kernels
     // that contain commit 66dd34a (3.9+). The manpage claims to only allow
@@ -459,8 +484,20 @@
   }
 
   void* abort_message = nullptr;
-  if (signal_number != DEBUGGER_SIGNAL && g_callbacks.get_abort_message) {
-    abort_message = g_callbacks.get_abort_message();
+  uintptr_t si_val = reinterpret_cast<uintptr_t>(info->si_ptr);
+  if (signal_number == DEBUGGER_SIGNAL) {
+    if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) {
+      // Allow for the abort message to be explicitly specified via the sigqueue value.
+      // Keep the bottom bit intact for representing whether we want a backtrace or a tombstone.
+      if (si_val != kDebuggerdFallbackSivalUintptrRequestDump) {
+        abort_message = reinterpret_cast<void*>(si_val & ~1);
+        info->si_ptr = reinterpret_cast<void*>(si_val & 1);
+      }
+    }
+  } else {
+    if (g_callbacks.get_abort_message) {
+      abort_message = g_callbacks.get_abort_message();
+    }
   }
 
   // If sival_int is ~0, it means that the fallback handler has been called
@@ -468,7 +505,8 @@
   // 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) {
+  if (si_val == kDebuggerdFallbackSivalUintptrRequestDump ||
+      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.
@@ -492,6 +530,7 @@
       .siginfo = info,
       .ucontext = context,
       .abort_msg = reinterpret_cast<uintptr_t>(abort_message),
+      .fdsan_table = reinterpret_cast<uintptr_t>(android_fdsan_get_fd_table()),
   };
 
   // Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us.
diff --git a/debuggerd/handler/fallback.h b/debuggerd/handler/fallback.h
new file mode 100644
index 0000000..597f582
--- /dev/null
+++ b/debuggerd/handler/fallback.h
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+static void* const kDebuggerdFallbackSivalPtrRequestDump = reinterpret_cast<void*>(~0UL);
+static const uintptr_t kDebuggerdFallbackSivalUintptrRequestDump = ~0UL;
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index f0a01f4..c606970 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -35,8 +35,8 @@
 #include <string>
 
 #include <android-base/unique_fd.h>
-#include <backtrace/Backtrace.h>
 #include <log/log.h>
+#include <unwindstack/Unwinder.h>
 
 #include "libdebuggerd/types.h"
 #include "libdebuggerd/utility.h"
@@ -59,25 +59,25 @@
   _LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid);
 }
 
-void dump_backtrace_thread(int output_fd, BacktraceMap* map, const ThreadInfo& thread) {
+void dump_backtrace_thread(int output_fd, unwindstack::Unwinder* unwinder,
+                           const ThreadInfo& thread) {
   log_t log;
   log.tfd = output_fd;
   log.amfd_data = nullptr;
 
   _LOG(&log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", thread.thread_name.c_str(), thread.tid);
 
-  std::vector<backtrace_frame_data_t> frames;
-  if (!Backtrace::Unwind(thread.registers.get(), map, &frames, 0, nullptr)) {
+  unwinder->SetRegs(thread.registers.get());
+  unwinder->Unwind();
+  if (unwinder->NumFrames() == 0) {
     _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());
-  }
+  log_backtrace(&log, unwinder, "  ");
 }
 
-void dump_backtrace(android::base::unique_fd output_fd, BacktraceMap* map,
+void dump_backtrace(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
                     const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread) {
   log_t log;
   log.tfd = output_fd.get();
@@ -91,10 +91,10 @@
 
   dump_process_header(&log, target->second.pid, target->second.process_name.c_str());
 
-  dump_backtrace_thread(output_fd.get(), map, target->second);
+  dump_backtrace_thread(output_fd.get(), unwinder, target->second);
   for (const auto& [tid, info] : thread_info) {
     if (tid != target_thread) {
-      dump_backtrace_thread(output_fd.get(), map, info);
+      dump_backtrace_thread(output_fd.get(), unwinder, info);
     }
   }
 
diff --git a/debuggerd/libdebuggerd/elf_utils.cpp b/debuggerd/libdebuggerd/elf_utils.cpp
deleted file mode 100644
index d7afc0b..0000000
--- a/debuggerd/libdebuggerd/elf_utils.cpp
+++ /dev/null
@@ -1,121 +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 "DEBUG"
-
-#include "libdebuggerd/elf_utils.h"
-
-#include <elf.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <string>
-
-#include <android-base/stringprintf.h>
-#include <log/log.h>
-#include <unwindstack/Memory.h>
-
-#define NOTE_ALIGN(size)  (((size) + 3) & ~3)
-
-template <typename HdrType, typename PhdrType, typename NhdrType>
-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 (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 (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.
-    if (phdr.p_type == PT_NOTE) {
-      size_t hdr_size = phdr.p_filesz;
-      uintptr_t addr = base_addr + phdr.p_offset;
-      while (hdr_size >= sizeof(NhdrType)) {
-        NhdrType nhdr;
-        if (memory->Read(addr, reinterpret_cast<uint8_t*>(&nhdr), sizeof(nhdr)) != sizeof(nhdr)) {
-          return false;
-        }
-        addr += sizeof(nhdr);
-        if (nhdr.n_type == NT_GNU_BUILD_ID) {
-          // Skip the name (which is the owner and should be "GNU").
-          addr += NOTE_ALIGN(nhdr.n_namesz);
-          uint8_t build_id_data[160];
-          if (nhdr.n_descsz > sizeof(build_id_data)) {
-            ALOGE("Possible corrupted note, desc size value is too large: %u",
-                  nhdr.n_descsz);
-            return false;
-          }
-          if (memory->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) {
-            return false;
-          }
-
-          build_id->clear();
-          for (size_t bytes = 0; bytes < nhdr.n_descsz; bytes++) {
-            *build_id += android::base::StringPrintf("%02x", build_id_data[bytes]);
-          }
-
-          return true;
-        } else {
-          // Move past the extra note data.
-          hdr_size -= sizeof(nhdr);
-          size_t skip_bytes = NOTE_ALIGN(nhdr.n_namesz) + NOTE_ALIGN(nhdr.n_descsz);
-          addr += skip_bytes;
-          if (hdr_size < skip_bytes) {
-            break;
-          }
-          hdr_size -= skip_bytes;
-        }
-      }
-    }
-  }
-  return false;
-}
-
-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 (memory->Read(addr, e_ident, SELFMAG) != SELFMAG) {
-    return false;
-  }
-
-  if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) {
-    return false;
-  }
-
-  // Read the rest of EI_NIDENT.
-  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>(memory, addr, e_ident, build_id);
-  } else if (e_ident[EI_CLASS] == ELFCLASS64) {
-    return get_build_id<Elf64_Ehdr, Elf64_Phdr, Elf64_Nhdr>(memory, addr, e_ident, build_id);
-  }
-
-  return false;
-}
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
index 119e59b..c20d090 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
@@ -28,15 +28,19 @@
 #include "types.h"
 #include "utility.h"
 
-class BacktraceMap;
+// Forward delcaration
+namespace unwindstack {
+class Unwinder;
+}
 
 // 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,
+void dump_backtrace(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
                     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_thread(int output_fd, unwindstack::Unwinder* unwinder,
+                           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
deleted file mode 100644
index 5d0d924..0000000
--- a/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
+++ /dev/null
@@ -1,29 +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>
-
-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
index 4727ca4..d47f2dd 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
@@ -14,23 +14,31 @@
  * limitations under the License.
  */
 
-#ifndef _DEBUGGERD_OPEN_FILES_LIST_H
-#define _DEBUGGERD_OPEN_FILES_LIST_H
+#pragma once
 
+#include <stdint.h>
 #include <sys/types.h>
 
+#include <map>
+#include <optional>
 #include <string>
 #include <utility>
-#include <vector>
 
 #include "utility.h"
 
-typedef std::vector<std::pair<int, std::string>> OpenFilesList;
+struct FDInfo {
+  std::optional<std::string> path;
+  std::optional<uint64_t> fdsan_owner;
+};
 
-/* Populates the given list with open files for the given process. */
-void populate_open_files_list(pid_t pid, OpenFilesList* list);
+using OpenFilesList = std::map<int, FDInfo>;
 
-/* Dumps the open files list to the log. */
+// Populates the given list with open files for the given process.
+void populate_open_files_list(OpenFilesList* list, pid_t pid);
+
+// Populates the given list with the target process's fdsan table.
+void populate_fdsan_table(OpenFilesList* list, std::shared_ptr<unwindstack::Memory> memory,
+                          uint64_t fdsan_table_address);
+
+// 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
index be90d0f..7133f77 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -29,7 +29,13 @@
 #include "open_files_list.h"
 #include "types.h"
 
-class BacktraceMap;
+// Forward declarations
+namespace unwindstack {
+class Unwinder;
+}
+
+// The maximum number of frames to save when unwinding.
+constexpr size_t kMaxFrames = 256;
 
 /* Create and open a tombstone file for writing.
  * Returns a writable file descriptor, or -1 with errno set appropriately.
@@ -38,16 +44,15 @@
 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(int tombstone_fd, unwindstack::Unwinder* unwinder,
+                       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,
+void engrave_tombstone(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
                        const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,
                        uint64_t abort_msg_address, OpenFilesList* open_files,
                        std::string* amfd_data);
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index 70583af..eb4b1b8 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -23,6 +23,9 @@
 
 struct ThreadInfo {
   std::unique_ptr<unwindstack::Regs> registers;
+
+  pid_t uid;
+
   pid_t tid;
   std::string thread_name;
 
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index 7b04e71..f189c45 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -18,6 +18,7 @@
 #ifndef _DEBUGGERD_UTILITY_H
 #define _DEBUGGERD_UTILITY_H
 
+#include <inttypes.h>
 #include <signal.h>
 #include <stdbool.h>
 #include <sys/types.h>
@@ -25,7 +26,6 @@
 #include <string>
 
 #include <android-base/macros.h>
-#include <backtrace/Backtrace.h>
 
 struct log_t {
   // Tombstone file descriptor.
@@ -61,21 +61,34 @@
   OPEN_FILES
 };
 
+#if defined(__LP64__)
+#define PRIPTR "016" PRIx64
+typedef uint64_t word_t;
+#else
+#define PRIPTR "08" PRIx64
+typedef uint32_t word_t;
+#endif
+
 // Log information onto the tombstone.
 void _LOG(log_t* log, logtype ltype, const char* fmt, ...) __attribute__((format(printf, 3, 4)));
 
 namespace unwindstack {
+class Unwinder;
 class Memory;
 }
 
+void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix);
+
 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_si_addr(int si_signo, int si_code);
-const char* get_signame(int sig);
-const char* get_sigcode(int signo, int code);
+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/open_files_list.cpp b/debuggerd/libdebuggerd/open_files_list.cpp
index b12703e..743a2e7 100644
--- a/debuggerd/libdebuggerd/open_files_list.cpp
+++ b/debuggerd/libdebuggerd/open_files_list.cpp
@@ -18,6 +18,7 @@
 
 #include "libdebuggerd/open_files_list.h"
 
+#include <android/fdsan.h>
 #include <dirent.h>
 #include <errno.h>
 #include <stdio.h>
@@ -32,10 +33,12 @@
 
 #include <android-base/file.h>
 #include <log/log.h>
+#include <unwindstack/Memory.h>
 
 #include "libdebuggerd/utility.h"
+#include "private/bionic_fdsan.h"
 
-void populate_open_files_list(pid_t pid, OpenFilesList* list) {
+void populate_open_files_list(OpenFilesList* list, pid_t pid) {
   std::string fd_dir_name = "/proc/" + std::to_string(pid) + "/fd";
   std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(fd_dir_name.c_str()), closedir);
   if (dir == nullptr) {
@@ -53,17 +56,85 @@
     std::string path = fd_dir_name + "/" + std::string(de->d_name);
     std::string target;
     if (android::base::Readlink(path, &target)) {
-      list->emplace_back(fd, target);
+      (*list)[fd].path = target;
     } else {
+      (*list)[fd].path = "???";
       ALOGE("failed to readlink %s: %s", path.c_str(), strerror(errno));
-      list->emplace_back(fd, "???");
     }
   }
 }
 
+void populate_fdsan_table(OpenFilesList* list, std::shared_ptr<unwindstack::Memory> memory,
+                          uint64_t fdsan_table_address) {
+  constexpr size_t inline_fds = sizeof(FdTable::entries) / sizeof(*FdTable::entries);
+  static_assert(inline_fds == 128);
+  size_t entry_offset = offsetof(FdTable, entries);
+  for (size_t i = 0; i < inline_fds; ++i) {
+    uint64_t address = fdsan_table_address + entry_offset + sizeof(FdEntry) * i;
+    FdEntry entry;
+    if (!memory->Read(address, &entry, sizeof(entry))) {
+      ALOGE("failed to read fdsan table entry %zu: %s", i, strerror(errno));
+      return;
+    }
+    if (entry.close_tag) {
+      (*list)[i].fdsan_owner = entry.close_tag.load();
+    }
+  }
+
+  size_t overflow_offset = offsetof(FdTable, overflow);
+  uintptr_t overflow = 0;
+  if (!memory->Read(fdsan_table_address + overflow_offset, &overflow, sizeof(overflow))) {
+    ALOGE("failed to read fdsan table overflow pointer: %s", strerror(errno));
+    return;
+  }
+
+  if (!overflow) {
+    return;
+  }
+
+  size_t overflow_length;
+  if (!memory->Read(overflow, &overflow_length, sizeof(overflow_length))) {
+    ALOGE("failed to read fdsan overflow table length: %s", strerror(errno));
+    return;
+  }
+
+  if (overflow_length > 131072) {
+    ALOGE("unreasonable large fdsan overflow table size %zu, bailing out", overflow_length);
+    return;
+  }
+
+  for (size_t i = 0; i < overflow_length; ++i) {
+    int fd = i + inline_fds;
+    uint64_t address = overflow + offsetof(FdTableOverflow, entries) + i * sizeof(FdEntry);
+    FdEntry entry;
+    if (!memory->Read(address, &entry, sizeof(entry))) {
+      ALOGE("failed to read fdsan overflow entry for fd %d: %s", fd, strerror(errno));
+      return;
+    }
+    if (entry.close_tag) {
+      (*list)[fd].fdsan_owner = entry.close_tag;
+    }
+  }
+  return;
+}
+
 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());
+  for (auto& [fd, entry] : files) {
+    const std::optional<std::string>& path = entry.path;
+    const std::optional<uint64_t>& fdsan_owner = entry.fdsan_owner;
+    if (path && fdsan_owner) {
+      const char* type = android_fdsan_get_tag_type(*fdsan_owner);
+      uint64_t value = android_fdsan_get_tag_value(*fdsan_owner);
+      _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s (owned by %s %#" PRIx64 ")\n", prefix, fd,
+           path->c_str(), type, value);
+    } else if (path && !fdsan_owner) {
+      _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s (unowned)\n", prefix, fd, path->c_str());
+    } else if (!path && fdsan_owner) {
+      _LOG(log, logtype::OPEN_FILES, "%sfd %i: <MISSING> (owned by %#" PRIx64 ")\n", prefix, fd,
+           *fdsan_owner);
+    } else {
+      ALOGE("OpenFilesList contains an entry (fd %d) with no path or owner", fd);
+    }
   }
 }
 
diff --git a/debuggerd/libdebuggerd/test/BacktraceMock.h b/debuggerd/libdebuggerd/test/BacktraceMock.h
deleted file mode 100644
index e7dbed7..0000000
--- a/debuggerd/libdebuggerd/test/BacktraceMock.h
+++ /dev/null
@@ -1,32 +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_BACKTRACE_MOCK_H
-#define _DEBUGGERD_TEST_BACKTRACE_MOCK_H
-
-#include <backtrace/BacktraceMap.h>
-
-class BacktraceMapMock : public BacktraceMap {
- public:
-  BacktraceMapMock() : BacktraceMap(0) {}
-  virtual ~BacktraceMapMock() {}
-
-  void AddMap(backtrace_map_t& map) {
-    maps_.push_back(map);
-  }
-};
-
-#endif //  _DEBUGGERD_TEST_BACKTRACE_MOCK_H
diff --git a/debuggerd/libdebuggerd/test/UnwinderMock.h b/debuggerd/libdebuggerd/test/UnwinderMock.h
new file mode 100644
index 0000000..023a578
--- /dev/null
+++ b/debuggerd/libdebuggerd/test/UnwinderMock.h
@@ -0,0 +1,40 @@
+/*
+ * 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 <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Unwinder.h>
+
+class UnwinderMock : public unwindstack::Unwinder {
+ public:
+  UnwinderMock() : Unwinder(128, new unwindstack::Maps, nullptr) {}
+  virtual ~UnwinderMock() { delete GetMaps(); }
+
+  void MockAddMap(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, std::string name,
+                  uint64_t load_bias) {
+    GetMaps()->Add(start, end, offset, flags, name, load_bias);
+  }
+
+  void MockSetBuildID(uint64_t offset, const std::string& build_id) {
+    unwindstack::MapInfo* map_info = GetMaps()->Find(offset);
+    if (map_info != nullptr) {
+      std::string* new_build_id = new std::string(build_id);
+      map_info->build_id = reinterpret_cast<uintptr_t>(new_build_id);
+    }
+  }
+};
diff --git a/debuggerd/libdebuggerd/test/open_files_list_test.cpp b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
index acac72c..3e920eb 100644
--- a/debuggerd/libdebuggerd/test/open_files_list_test.cpp
+++ b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
@@ -20,10 +20,9 @@
 
 #include <string>
 
+#include <android-base/file.h>
 #include <gtest/gtest.h>
 
-#include "android-base/test_utils.h"
-
 #include "libdebuggerd/open_files_list.h"
 
 // Check that we can produce a list of open files for the current process, and
@@ -34,13 +33,13 @@
 
   // Get the list of open files for this process.
   OpenFilesList list;
-  populate_open_files_list(getpid(), &list);
+  populate_open_files_list(&list, getpid());
 
   // Verify our open file is in the list.
   bool found = false;
-  for (auto&  file : list) {
+  for (auto& file : list) {
     if (file.first == tf.fd) {
-      EXPECT_EQ(file.second, std::string(tf.path));
+      EXPECT_EQ(file.second.path.value_or(""), std::string(tf.path));
       found = true;
       break;
     }
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 421ce43..88c206f 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -15,6 +15,8 @@
  */
 
 #include <stdlib.h>
+#include <sys/mman.h>
+#include <time.h>
 
 #include <memory>
 #include <string>
@@ -25,26 +27,16 @@
 
 #include "libdebuggerd/utility.h"
 
-#include "BacktraceMock.h"
-#include "elf_fake.h"
+#include "UnwinderMock.h"
 #include "host_signal_fixup.h"
 #include "log_fake.h"
 
 #include "tombstone.cpp"
 
-void dump_registers(log_t*, pid_t) {
-}
-
-void dump_memory_and_code(log_t*, Backtrace*) {
-}
-
-void dump_backtrace_to_log(Backtrace*, log_t*, char const*) {
-}
-
 class TombstoneTest : public ::testing::Test {
  protected:
   virtual void SetUp() {
-    map_mock_.reset(new BacktraceMapMock());
+    unwinder_mock_.reset(new UnwinderMock());
 
     char tmp_file[256];
     const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX";
@@ -70,7 +62,6 @@
     log_.should_retrieve_logcat = false;
 
     resetLogs();
-    elf_set_fake_build_id("");
   }
 
   virtual void TearDown() {
@@ -79,24 +70,20 @@
     }
   }
 
-  std::unique_ptr<BacktraceMapMock> map_mock_;
+  std::unique_ptr<UnwinderMock> unwinder_mock_;
 
   log_t log_;
   std::string amfd_data_;
 };
 
 TEST_F(TombstoneTest, single_map) {
-  backtrace_map_t map;
 #if defined(__LP64__)
-  map.start = 0x123456789abcd000UL;
-  map.end = 0x123456789abdf000UL;
+  unwinder_mock_->MockAddMap(0x123456789abcd000UL, 0x123456789abdf000UL, 0, 0, "", 0);
 #else
-  map.start = 0x1234000;
-  map.end = 0x1235000;
+  unwinder_mock_->MockAddMap(0x1234000, 0x1235000, 0, 0, "", 0);
 #endif
-  map_mock_->AddMap(map);
 
-  dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
+  dump_all_maps(&log_, unwinder_mock_.get(), 0);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -118,20 +105,25 @@
 }
 
 TEST_F(TombstoneTest, single_map_elf_build_id) {
-  backtrace_map_t map;
+  uint64_t build_id_offset;
 #if defined(__LP64__)
-  map.start = 0x123456789abcd000UL;
-  map.end = 0x123456789abdf000UL;
+  build_id_offset = 0x123456789abcd000UL;
+  unwinder_mock_->MockAddMap(build_id_offset, 0x123456789abdf000UL, 0, PROT_READ,
+                             "/system/lib/libfake.so", 0);
 #else
-  map.start = 0x1234000;
-  map.end = 0x1235000;
+  build_id_offset = 0x1234000;
+  unwinder_mock_->MockAddMap(0x1234000, 0x1235000, 0, PROT_READ, "/system/lib/libfake.so", 0);
 #endif
-  map.flags = PROT_READ;
-  map.name = "/system/lib/libfake.so";
-  map_mock_->AddMap(map);
 
-  elf_set_fake_build_id("abcdef1234567890abcdef1234567890");
-  dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
+  unwinder_mock_->MockSetBuildID(
+      build_id_offset,
+      std::string{static_cast<char>(0xab), static_cast<char>(0xcd), static_cast<char>(0xef),
+                  static_cast<char>(0x12), static_cast<char>(0x34), static_cast<char>(0x56),
+                  static_cast<char>(0x78), static_cast<char>(0x90), static_cast<char>(0xab),
+                  static_cast<char>(0xcd), static_cast<char>(0xef), static_cast<char>(0x12),
+                  static_cast<char>(0x34), static_cast<char>(0x56), static_cast<char>(0x78),
+                  static_cast<char>(0x90)});
+  dump_all_maps(&log_, unwinder_mock_.get(), 0);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -152,83 +144,15 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-// Even though build id is present, it should not be printed in either of
-// these cases.
-TEST_F(TombstoneTest, single_map_no_build_id) {
-  backtrace_map_t map;
-#if defined(__LP64__)
-  map.start = 0x123456789abcd000UL;
-  map.end = 0x123456789abdf000UL;
-#else
-  map.start = 0x1234000;
-  map.end = 0x1235000;
-#endif
-  map.flags = PROT_WRITE;
-  map_mock_->AddMap(map);
-
-  map.name = "/system/lib/libfake.so";
-  map_mock_->AddMap(map);
-
-  elf_set_fake_build_id("abcdef1234567890abcdef1234567890");
-  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 (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";
-#else
-"    01234000-01234fff -w-         0      1000\n"
-"    01234000-01234fff -w-         0      1000  /system/lib/libfake.so\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("", getFakeLogPrint().c_str());
-}
-
 TEST_F(TombstoneTest, multiple_maps) {
-  backtrace_map_t map;
+  unwinder_mock_->MockAddMap(0xa234000, 0xa235000, 0, 0, "", 0);
+  unwinder_mock_->MockAddMap(0xa334000, 0xa335000, 0xf000, PROT_READ, "", 0);
+  unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000);
+  unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000);
+  unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+                             "/system/lib/fake.so", 0);
 
-  map.start = 0xa234000;
-  map.end = 0xa235000;
-  map_mock_->AddMap(map);
-
-  map.start = 0xa334000;
-  map.end = 0xa335000;
-  map.offset = 0xf000;
-  map.flags = PROT_READ;
-  map_mock_->AddMap(map);
-
-  map.start = 0xa434000;
-  map.end = 0xa435000;
-  map.offset = 0x1000;
-  map.load_bias = 0xd000;
-  map.flags = PROT_WRITE;
-  map_mock_->AddMap(map);
-
-  map.start = 0xa534000;
-  map.end = 0xa535000;
-  map.offset = 0x3000;
-  map.load_bias = 0x2000;
-  map.flags = PROT_EXEC;
-  map_mock_->AddMap(map);
-
-  map.start = 0xa634000;
-  map.end = 0xa635000;
-  map.offset = 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(&log_, map_mock_.get(), nullptr, 0);
+  dump_all_maps(&log_, unwinder_mock_.get(), 0);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -258,31 +182,12 @@
 }
 
 TEST_F(TombstoneTest, multiple_maps_fault_address_before) {
-  backtrace_map_t map;
+  unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000);
+  unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000);
+  unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+                             "/system/lib/fake.so", 0);
 
-  map.start = 0xa434000;
-  map.end = 0xa435000;
-  map.offset = 0x1000;
-  map.load_bias = 0xd000;
-  map.flags = PROT_WRITE;
-  map_mock_->AddMap(map);
-
-  map.start = 0xa534000;
-  map.end = 0xa535000;
-  map.offset = 0x3000;
-  map.load_bias = 0x2000;
-  map.flags = PROT_EXEC;
-  map_mock_->AddMap(map);
-
-  map.start = 0xa634000;
-  map.end = 0xa635000;
-  map.offset = 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(&log_, map_mock_.get(), nullptr, 0x1000);
+  dump_all_maps(&log_, unwinder_mock_.get(), 0x1000);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -310,31 +215,12 @@
 }
 
 TEST_F(TombstoneTest, multiple_maps_fault_address_between) {
-  backtrace_map_t map;
+  unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000);
+  unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000);
+  unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+                             "/system/lib/fake.so", 0);
 
-  map.start = 0xa434000;
-  map.end = 0xa435000;
-  map.offset = 0x1000;
-  map.load_bias = 0xd000;
-  map.flags = PROT_WRITE;
-  map_mock_->AddMap(map);
-
-  map.start = 0xa534000;
-  map.end = 0xa535000;
-  map.offset = 0x3000;
-  map.load_bias = 0x2000;
-  map.flags = PROT_EXEC;
-  map_mock_->AddMap(map);
-
-  map.start = 0xa634000;
-  map.end = 0xa635000;
-  map.offset = 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(&log_, map_mock_.get(), nullptr, 0xa533000);
+  dump_all_maps(&log_, unwinder_mock_.get(), 0xa533000);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -362,31 +248,12 @@
 }
 
 TEST_F(TombstoneTest, multiple_maps_fault_address_in_map) {
-  backtrace_map_t map;
+  unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000);
+  unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000);
+  unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+                             "/system/lib/fake.so", 0);
 
-  map.start = 0xa434000;
-  map.end = 0xa435000;
-  map.offset = 0x1000;
-  map.load_bias = 0xd000;
-  map.flags = PROT_WRITE;
-  map_mock_->AddMap(map);
-
-  map.start = 0xa534000;
-  map.end = 0xa535000;
-  map.offset = 0x3000;
-  map.load_bias = 0x2000;
-  map.flags = PROT_EXEC;
-  map_mock_->AddMap(map);
-
-  map.start = 0xa634000;
-  map.end = 0xa635000;
-  map.offset = 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(&log_, map_mock_.get(), nullptr, 0xa534040);
+  dump_all_maps(&log_, unwinder_mock_.get(), 0xa534040);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -412,36 +279,17 @@
 }
 
 TEST_F(TombstoneTest, multiple_maps_fault_address_after) {
-  backtrace_map_t map;
-
-  map.start = 0xa434000;
-  map.end = 0xa435000;
-  map.offset = 0x1000;
-  map.load_bias = 0xd000;
-  map.flags = PROT_WRITE;
-  map_mock_->AddMap(map);
-
-  map.start = 0xa534000;
-  map.end = 0xa535000;
-  map.offset = 0x3000;
-  map.load_bias = 0x2000;
-  map.flags = PROT_EXEC;
-  map_mock_->AddMap(map);
-
-  map.start = 0xa634000;
-  map.end = 0xa635000;
-  map.offset = 0;
-  map.load_bias = 0;
-  map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
-  map.name = "/system/lib/fake.so";
-  map_mock_->AddMap(map);
+  unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000);
+  unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000);
+  unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+                             "/system/lib/fake.so", 0);
 
 #if defined(__LP64__)
   uint64_t addr = 0x12345a534040UL;
 #else
   uint64_t addr = 0xf534040UL;
 #endif
-  dump_all_maps(&log_, map_mock_.get(), nullptr, addr);
+  dump_all_maps(&log_, unwinder_mock_.get(), addr);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -494,3 +342,484 @@
   expected += android::base::StringPrintf("ABI: '%s'\n", ABI_STRING);
   ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
 }
+
+TEST_F(TombstoneTest, dump_thread_info_uid) {
+  dump_thread_info(&log_, ThreadInfo{.uid = 1,
+                                     .pid = 2,
+                                     .tid = 3,
+                                     .thread_name = "some_thread",
+                                     .process_name = "some_process"});
+  std::string expected = "pid: 2, tid: 3, name: some_thread  >>> some_process <<<\nuid: 1\n";
+  ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
+}
+
+TEST_F(TombstoneTest, dump_timestamp) {
+  setenv("TZ", "UTC", 1);
+  tzset();
+  dump_timestamp(&log_, 0);
+  ASSERT_STREQ("Timestamp: 1970-01-01 00:00:00+0000\n", amfd_data_.c_str());
+}
+
+class MemoryPattern : public unwindstack::Memory {
+ public:
+  MemoryPattern() = default;
+  virtual ~MemoryPattern() = default;
+
+  size_t Read(uint64_t, void* dst, size_t size) override {
+    uint8_t* data = reinterpret_cast<uint8_t*>(dst);
+    for (size_t i = 0; i < size; i++) {
+      data[i] = (i % 0xff);
+    }
+    return size;
+  }
+};
+
+TEST_F(TombstoneTest, dump_stack_single_frame) {
+  std::vector<unwindstack::FrameData> frames;
+  unwindstack::Maps maps;
+  MemoryPattern memory;
+
+  frames.push_back(
+      unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
+  dump_stack(&log_, frames, &maps, &memory);
+
+  std::string contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
+
+  std::string expected =
+#if defined(__LP64__)
+      "         0000000000001f80  0706050403020100\n"
+      "         0000000000001f88  0f0e0d0c0b0a0908\n"
+      "         0000000000001f90  1716151413121110\n"
+      "         0000000000001f98  1f1e1d1c1b1a1918\n"
+      "         0000000000001fa0  2726252423222120\n"
+      "         0000000000001fa8  2f2e2d2c2b2a2928\n"
+      "         0000000000001fb0  3736353433323130\n"
+      "         0000000000001fb8  3f3e3d3c3b3a3938\n"
+      "         0000000000001fc0  4746454443424140\n"
+      "         0000000000001fc8  4f4e4d4c4b4a4948\n"
+      "         0000000000001fd0  5756555453525150\n"
+      "         0000000000001fd8  5f5e5d5c5b5a5958\n"
+      "         0000000000001fe0  6766656463626160\n"
+      "         0000000000001fe8  6f6e6d6c6b6a6968\n"
+      "         0000000000001ff0  7776757473727170\n"
+      "         0000000000001ff8  7f7e7d7c7b7a7978\n"
+      "    #00  0000000000002000  0706050403020100\n"
+      "         0000000000002008  0f0e0d0c0b0a0908\n"
+      "         0000000000002010  1716151413121110\n"
+      "         0000000000002018  1f1e1d1c1b1a1918\n"
+      "         0000000000002020  2726252423222120\n"
+      "         0000000000002028  2f2e2d2c2b2a2928\n"
+      "         0000000000002030  3736353433323130\n"
+      "         0000000000002038  3f3e3d3c3b3a3938\n"
+      "         0000000000002040  4746454443424140\n"
+      "         0000000000002048  4f4e4d4c4b4a4948\n"
+      "         0000000000002050  5756555453525150\n"
+      "         0000000000002058  5f5e5d5c5b5a5958\n"
+      "         0000000000002060  6766656463626160\n"
+      "         0000000000002068  6f6e6d6c6b6a6968\n"
+      "         0000000000002070  7776757473727170\n"
+      "         0000000000002078  7f7e7d7c7b7a7978\n";
+#else
+      "         00001fc0  03020100\n"
+      "         00001fc4  07060504\n"
+      "         00001fc8  0b0a0908\n"
+      "         00001fcc  0f0e0d0c\n"
+      "         00001fd0  13121110\n"
+      "         00001fd4  17161514\n"
+      "         00001fd8  1b1a1918\n"
+      "         00001fdc  1f1e1d1c\n"
+      "         00001fe0  23222120\n"
+      "         00001fe4  27262524\n"
+      "         00001fe8  2b2a2928\n"
+      "         00001fec  2f2e2d2c\n"
+      "         00001ff0  33323130\n"
+      "         00001ff4  37363534\n"
+      "         00001ff8  3b3a3938\n"
+      "         00001ffc  3f3e3d3c\n"
+      "    #00  00002000  03020100\n"
+      "         00002004  07060504\n"
+      "         00002008  0b0a0908\n"
+      "         0000200c  0f0e0d0c\n"
+      "         00002010  13121110\n"
+      "         00002014  17161514\n"
+      "         00002018  1b1a1918\n"
+      "         0000201c  1f1e1d1c\n"
+      "         00002020  23222120\n"
+      "         00002024  27262524\n"
+      "         00002028  2b2a2928\n"
+      "         0000202c  2f2e2d2c\n"
+      "         00002030  33323130\n"
+      "         00002034  37363534\n"
+      "         00002038  3b3a3938\n"
+      "         0000203c  3f3e3d3c\n";
+#endif
+  EXPECT_EQ(expected, contents);
+}
+
+TEST_F(TombstoneTest, dump_stack_multiple_frames_same_sp) {
+  std::vector<unwindstack::FrameData> frames;
+  unwindstack::Maps maps;
+  MemoryPattern memory;
+
+  frames.push_back(
+      unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
+  frames.push_back(
+      unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2000});
+  dump_stack(&log_, frames, &maps, &memory);
+
+  std::string contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
+
+  std::string expected =
+#if defined(__LP64__)
+      "         0000000000001f80  0706050403020100\n"
+      "         0000000000001f88  0f0e0d0c0b0a0908\n"
+      "         0000000000001f90  1716151413121110\n"
+      "         0000000000001f98  1f1e1d1c1b1a1918\n"
+      "         0000000000001fa0  2726252423222120\n"
+      "         0000000000001fa8  2f2e2d2c2b2a2928\n"
+      "         0000000000001fb0  3736353433323130\n"
+      "         0000000000001fb8  3f3e3d3c3b3a3938\n"
+      "         0000000000001fc0  4746454443424140\n"
+      "         0000000000001fc8  4f4e4d4c4b4a4948\n"
+      "         0000000000001fd0  5756555453525150\n"
+      "         0000000000001fd8  5f5e5d5c5b5a5958\n"
+      "         0000000000001fe0  6766656463626160\n"
+      "         0000000000001fe8  6f6e6d6c6b6a6968\n"
+      "         0000000000001ff0  7776757473727170\n"
+      "         0000000000001ff8  7f7e7d7c7b7a7978\n"
+      "    #00  0000000000002000  0706050403020100\n"
+      "         ................  ................\n"
+      "    #01  0000000000002000  0706050403020100\n"
+      "         0000000000002008  0f0e0d0c0b0a0908\n"
+      "         0000000000002010  1716151413121110\n"
+      "         0000000000002018  1f1e1d1c1b1a1918\n"
+      "         0000000000002020  2726252423222120\n"
+      "         0000000000002028  2f2e2d2c2b2a2928\n"
+      "         0000000000002030  3736353433323130\n"
+      "         0000000000002038  3f3e3d3c3b3a3938\n"
+      "         0000000000002040  4746454443424140\n"
+      "         0000000000002048  4f4e4d4c4b4a4948\n"
+      "         0000000000002050  5756555453525150\n"
+      "         0000000000002058  5f5e5d5c5b5a5958\n"
+      "         0000000000002060  6766656463626160\n"
+      "         0000000000002068  6f6e6d6c6b6a6968\n"
+      "         0000000000002070  7776757473727170\n"
+      "         0000000000002078  7f7e7d7c7b7a7978\n";
+#else
+      "         00001fc0  03020100\n"
+      "         00001fc4  07060504\n"
+      "         00001fc8  0b0a0908\n"
+      "         00001fcc  0f0e0d0c\n"
+      "         00001fd0  13121110\n"
+      "         00001fd4  17161514\n"
+      "         00001fd8  1b1a1918\n"
+      "         00001fdc  1f1e1d1c\n"
+      "         00001fe0  23222120\n"
+      "         00001fe4  27262524\n"
+      "         00001fe8  2b2a2928\n"
+      "         00001fec  2f2e2d2c\n"
+      "         00001ff0  33323130\n"
+      "         00001ff4  37363534\n"
+      "         00001ff8  3b3a3938\n"
+      "         00001ffc  3f3e3d3c\n"
+      "    #00  00002000  03020100\n"
+      "         ........  ........\n"
+      "    #01  00002000  03020100\n"
+      "         00002004  07060504\n"
+      "         00002008  0b0a0908\n"
+      "         0000200c  0f0e0d0c\n"
+      "         00002010  13121110\n"
+      "         00002014  17161514\n"
+      "         00002018  1b1a1918\n"
+      "         0000201c  1f1e1d1c\n"
+      "         00002020  23222120\n"
+      "         00002024  27262524\n"
+      "         00002028  2b2a2928\n"
+      "         0000202c  2f2e2d2c\n"
+      "         00002030  33323130\n"
+      "         00002034  37363534\n"
+      "         00002038  3b3a3938\n"
+      "         0000203c  3f3e3d3c\n";
+#endif
+  EXPECT_EQ(expected, contents);
+}
+
+TEST_F(TombstoneTest, dump_stack_multiple_frames) {
+  std::vector<unwindstack::FrameData> frames;
+  unwindstack::Maps maps;
+  MemoryPattern memory;
+
+  frames.push_back(
+      unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
+  frames.push_back(
+      unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2010});
+  frames.push_back(
+      unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2100});
+  dump_stack(&log_, frames, &maps, &memory);
+
+  std::string contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
+
+  std::string expected =
+#if defined(__LP64__)
+      "         0000000000001f80  0706050403020100\n"
+      "         0000000000001f88  0f0e0d0c0b0a0908\n"
+      "         0000000000001f90  1716151413121110\n"
+      "         0000000000001f98  1f1e1d1c1b1a1918\n"
+      "         0000000000001fa0  2726252423222120\n"
+      "         0000000000001fa8  2f2e2d2c2b2a2928\n"
+      "         0000000000001fb0  3736353433323130\n"
+      "         0000000000001fb8  3f3e3d3c3b3a3938\n"
+      "         0000000000001fc0  4746454443424140\n"
+      "         0000000000001fc8  4f4e4d4c4b4a4948\n"
+      "         0000000000001fd0  5756555453525150\n"
+      "         0000000000001fd8  5f5e5d5c5b5a5958\n"
+      "         0000000000001fe0  6766656463626160\n"
+      "         0000000000001fe8  6f6e6d6c6b6a6968\n"
+      "         0000000000001ff0  7776757473727170\n"
+      "         0000000000001ff8  7f7e7d7c7b7a7978\n"
+      "    #00  0000000000002000  0706050403020100\n"
+      "         0000000000002008  0f0e0d0c0b0a0908\n"
+      "    #01  0000000000002010  0706050403020100\n"
+      "         0000000000002018  0f0e0d0c0b0a0908\n"
+      "         0000000000002020  1716151413121110\n"
+      "         0000000000002028  1f1e1d1c1b1a1918\n"
+      "         0000000000002030  2726252423222120\n"
+      "         0000000000002038  2f2e2d2c2b2a2928\n"
+      "         0000000000002040  3736353433323130\n"
+      "         0000000000002048  3f3e3d3c3b3a3938\n"
+      "         0000000000002050  4746454443424140\n"
+      "         0000000000002058  4f4e4d4c4b4a4948\n"
+      "         0000000000002060  5756555453525150\n"
+      "         0000000000002068  5f5e5d5c5b5a5958\n"
+      "         0000000000002070  6766656463626160\n"
+      "         0000000000002078  6f6e6d6c6b6a6968\n"
+      "         0000000000002080  7776757473727170\n"
+      "         0000000000002088  7f7e7d7c7b7a7978\n"
+      "         ................  ................\n"
+      "    #02  0000000000002100  0706050403020100\n"
+      "         0000000000002108  0f0e0d0c0b0a0908\n"
+      "         0000000000002110  1716151413121110\n"
+      "         0000000000002118  1f1e1d1c1b1a1918\n"
+      "         0000000000002120  2726252423222120\n"
+      "         0000000000002128  2f2e2d2c2b2a2928\n"
+      "         0000000000002130  3736353433323130\n"
+      "         0000000000002138  3f3e3d3c3b3a3938\n"
+      "         0000000000002140  4746454443424140\n"
+      "         0000000000002148  4f4e4d4c4b4a4948\n"
+      "         0000000000002150  5756555453525150\n"
+      "         0000000000002158  5f5e5d5c5b5a5958\n"
+      "         0000000000002160  6766656463626160\n"
+      "         0000000000002168  6f6e6d6c6b6a6968\n"
+      "         0000000000002170  7776757473727170\n"
+      "         0000000000002178  7f7e7d7c7b7a7978\n";
+#else
+      "         00001fc0  03020100\n"
+      "         00001fc4  07060504\n"
+      "         00001fc8  0b0a0908\n"
+      "         00001fcc  0f0e0d0c\n"
+      "         00001fd0  13121110\n"
+      "         00001fd4  17161514\n"
+      "         00001fd8  1b1a1918\n"
+      "         00001fdc  1f1e1d1c\n"
+      "         00001fe0  23222120\n"
+      "         00001fe4  27262524\n"
+      "         00001fe8  2b2a2928\n"
+      "         00001fec  2f2e2d2c\n"
+      "         00001ff0  33323130\n"
+      "         00001ff4  37363534\n"
+      "         00001ff8  3b3a3938\n"
+      "         00001ffc  3f3e3d3c\n"
+      "    #00  00002000  03020100\n"
+      "         00002004  07060504\n"
+      "         00002008  0b0a0908\n"
+      "         0000200c  0f0e0d0c\n"
+      "    #01  00002010  03020100\n"
+      "         00002014  07060504\n"
+      "         00002018  0b0a0908\n"
+      "         0000201c  0f0e0d0c\n"
+      "         00002020  13121110\n"
+      "         00002024  17161514\n"
+      "         00002028  1b1a1918\n"
+      "         0000202c  1f1e1d1c\n"
+      "         00002030  23222120\n"
+      "         00002034  27262524\n"
+      "         00002038  2b2a2928\n"
+      "         0000203c  2f2e2d2c\n"
+      "         00002040  33323130\n"
+      "         00002044  37363534\n"
+      "         00002048  3b3a3938\n"
+      "         0000204c  3f3e3d3c\n"
+      "         ........  ........\n"
+      "    #02  00002100  03020100\n"
+      "         00002104  07060504\n"
+      "         00002108  0b0a0908\n"
+      "         0000210c  0f0e0d0c\n"
+      "         00002110  13121110\n"
+      "         00002114  17161514\n"
+      "         00002118  1b1a1918\n"
+      "         0000211c  1f1e1d1c\n"
+      "         00002120  23222120\n"
+      "         00002124  27262524\n"
+      "         00002128  2b2a2928\n"
+      "         0000212c  2f2e2d2c\n"
+      "         00002130  33323130\n"
+      "         00002134  37363534\n"
+      "         00002138  3b3a3938\n"
+      "         0000213c  3f3e3d3c\n";
+#endif
+  EXPECT_EQ(expected, contents);
+}
+
+TEST_F(TombstoneTest, dump_stack_multiple_frames_disjoint_frames) {
+  std::vector<unwindstack::FrameData> frames;
+  unwindstack::Maps maps;
+  MemoryPattern memory;
+
+  frames.push_back(
+      unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
+  frames.push_back(
+      unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2010});
+  frames.push_back(
+      unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x1000});
+  frames.push_back(
+      unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x1030});
+  dump_stack(&log_, frames, &maps, &memory);
+
+  std::string contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
+
+  std::string expected =
+#if defined(__LP64__)
+      "         0000000000001f80  0706050403020100\n"
+      "         0000000000001f88  0f0e0d0c0b0a0908\n"
+      "         0000000000001f90  1716151413121110\n"
+      "         0000000000001f98  1f1e1d1c1b1a1918\n"
+      "         0000000000001fa0  2726252423222120\n"
+      "         0000000000001fa8  2f2e2d2c2b2a2928\n"
+      "         0000000000001fb0  3736353433323130\n"
+      "         0000000000001fb8  3f3e3d3c3b3a3938\n"
+      "         0000000000001fc0  4746454443424140\n"
+      "         0000000000001fc8  4f4e4d4c4b4a4948\n"
+      "         0000000000001fd0  5756555453525150\n"
+      "         0000000000001fd8  5f5e5d5c5b5a5958\n"
+      "         0000000000001fe0  6766656463626160\n"
+      "         0000000000001fe8  6f6e6d6c6b6a6968\n"
+      "         0000000000001ff0  7776757473727170\n"
+      "         0000000000001ff8  7f7e7d7c7b7a7978\n"
+      "    #00  0000000000002000  0706050403020100\n"
+      "         0000000000002008  0f0e0d0c0b0a0908\n"
+      "    #01  0000000000002010  0706050403020100\n"
+      "         0000000000002018  0f0e0d0c0b0a0908\n"
+      "         0000000000002020  1716151413121110\n"
+      "         0000000000002028  1f1e1d1c1b1a1918\n"
+      "         0000000000002030  2726252423222120\n"
+      "         0000000000002038  2f2e2d2c2b2a2928\n"
+      "         0000000000002040  3736353433323130\n"
+      "         0000000000002048  3f3e3d3c3b3a3938\n"
+      "         0000000000002050  4746454443424140\n"
+      "         0000000000002058  4f4e4d4c4b4a4948\n"
+      "         0000000000002060  5756555453525150\n"
+      "         0000000000002068  5f5e5d5c5b5a5958\n"
+      "         0000000000002070  6766656463626160\n"
+      "         0000000000002078  6f6e6d6c6b6a6968\n"
+      "         0000000000002080  7776757473727170\n"
+      "         0000000000002088  7f7e7d7c7b7a7978\n"
+      "         ................  ................\n"
+      "    #02  0000000000001000  0706050403020100\n"
+      "         0000000000001008  0f0e0d0c0b0a0908\n"
+      "         0000000000001010  1716151413121110\n"
+      "         0000000000001018  1f1e1d1c1b1a1918\n"
+      "         0000000000001020  2726252423222120\n"
+      "         0000000000001028  2f2e2d2c2b2a2928\n"
+      "    #03  0000000000001030  0706050403020100\n"
+      "         0000000000001038  0f0e0d0c0b0a0908\n"
+      "         0000000000001040  1716151413121110\n"
+      "         0000000000001048  1f1e1d1c1b1a1918\n"
+      "         0000000000001050  2726252423222120\n"
+      "         0000000000001058  2f2e2d2c2b2a2928\n"
+      "         0000000000001060  3736353433323130\n"
+      "         0000000000001068  3f3e3d3c3b3a3938\n"
+      "         0000000000001070  4746454443424140\n"
+      "         0000000000001078  4f4e4d4c4b4a4948\n"
+      "         0000000000001080  5756555453525150\n"
+      "         0000000000001088  5f5e5d5c5b5a5958\n"
+      "         0000000000001090  6766656463626160\n"
+      "         0000000000001098  6f6e6d6c6b6a6968\n"
+      "         00000000000010a0  7776757473727170\n"
+      "         00000000000010a8  7f7e7d7c7b7a7978\n";
+#else
+      "         00001fc0  03020100\n"
+      "         00001fc4  07060504\n"
+      "         00001fc8  0b0a0908\n"
+      "         00001fcc  0f0e0d0c\n"
+      "         00001fd0  13121110\n"
+      "         00001fd4  17161514\n"
+      "         00001fd8  1b1a1918\n"
+      "         00001fdc  1f1e1d1c\n"
+      "         00001fe0  23222120\n"
+      "         00001fe4  27262524\n"
+      "         00001fe8  2b2a2928\n"
+      "         00001fec  2f2e2d2c\n"
+      "         00001ff0  33323130\n"
+      "         00001ff4  37363534\n"
+      "         00001ff8  3b3a3938\n"
+      "         00001ffc  3f3e3d3c\n"
+      "    #00  00002000  03020100\n"
+      "         00002004  07060504\n"
+      "         00002008  0b0a0908\n"
+      "         0000200c  0f0e0d0c\n"
+      "    #01  00002010  03020100\n"
+      "         00002014  07060504\n"
+      "         00002018  0b0a0908\n"
+      "         0000201c  0f0e0d0c\n"
+      "         00002020  13121110\n"
+      "         00002024  17161514\n"
+      "         00002028  1b1a1918\n"
+      "         0000202c  1f1e1d1c\n"
+      "         00002030  23222120\n"
+      "         00002034  27262524\n"
+      "         00002038  2b2a2928\n"
+      "         0000203c  2f2e2d2c\n"
+      "         00002040  33323130\n"
+      "         00002044  37363534\n"
+      "         00002048  3b3a3938\n"
+      "         0000204c  3f3e3d3c\n"
+      "         ........  ........\n"
+      "    #02  00001000  03020100\n"
+      "         00001004  07060504\n"
+      "         00001008  0b0a0908\n"
+      "         0000100c  0f0e0d0c\n"
+      "         00001010  13121110\n"
+      "         00001014  17161514\n"
+      "         00001018  1b1a1918\n"
+      "         0000101c  1f1e1d1c\n"
+      "         00001020  23222120\n"
+      "         00001024  27262524\n"
+      "         00001028  2b2a2928\n"
+      "         0000102c  2f2e2d2c\n"
+      "    #03  00001030  03020100\n"
+      "         00001034  07060504\n"
+      "         00001038  0b0a0908\n"
+      "         0000103c  0f0e0d0c\n"
+      "         00001040  13121110\n"
+      "         00001044  17161514\n"
+      "         00001048  1b1a1918\n"
+      "         0000104c  1f1e1d1c\n"
+      "         00001050  23222120\n"
+      "         00001054  27262524\n"
+      "         00001058  2b2a2928\n"
+      "         0000105c  2f2e2d2c\n"
+      "         00001060  33323130\n"
+      "         00001064  37363534\n"
+      "         00001068  3b3a3938\n"
+      "         0000106c  3f3e3d3c\n";
+#endif
+  EXPECT_EQ(expected, contents);
+}
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 933a597..d246722 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -27,6 +27,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/mman.h>
 #include <sys/ptrace.h>
 #include <sys/stat.h>
 #include <time.h>
@@ -41,19 +42,20 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <android/log.h>
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
 #include <log/log.h>
 #include <log/logprint.h>
 #include <private/android_filesystem_config.h>
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
 
 // Needed to get DEBUGGER_SIGNAL.
 #include "debuggerd/handler.h"
 
 #include "libdebuggerd/backtrace.h"
-#include "libdebuggerd/elf_utils.h"
 #include "libdebuggerd/open_files_list.h"
 #include "libdebuggerd/utility.h"
 
@@ -62,9 +64,6 @@
 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
@@ -78,7 +77,16 @@
   _LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING);
 }
 
-static void dump_probable_cause(log_t* log, const siginfo_t* si) {
+static void dump_timestamp(log_t* log, time_t time) {
+  struct tm tm;
+  localtime_r(&time, &tm);
+
+  char buf[strlen("1970-01-01 00:00:00+0830") + 1];
+  strftime(buf, sizeof(buf), "%F %T%z", &tm);
+  _LOG(log, logtype::HEADER, "Timestamp: %s\n", buf);
+}
+
+static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Maps* maps) {
   std::string cause;
   if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) {
     if (si->si_addr < reinterpret_cast<void*>(4096)) {
@@ -94,6 +102,11 @@
     } else if (si->si_addr == reinterpret_cast<void*>(0xffff0f60)) {
       cause = "call to kuser_cmpxchg64";
     }
+  } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
+    unwindstack::MapInfo* map_info = maps->Find(reinterpret_cast<uint64_t>(si->si_addr));
+    if (map_info != nullptr && map_info->flags == PROT_EXEC) {
+      cause = "execute-only (no-read) memory access error; likely due to data in .text.";
+    }
   } 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);
@@ -102,18 +115,30 @@
   if (!cause.empty()) _LOG(log, logtype::HEADER, "Cause: %s\n", cause.c_str());
 }
 
-static void dump_signal_info(log_t* log, const siginfo_t* si) {
-  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,
+                             unwindstack::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);
+  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_probable_cause(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);
 }
 
 static void dump_thread_info(log_t* log, const ThreadInfo& thread_info) {
@@ -126,16 +151,17 @@
 
   _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());
+  _LOG(log, logtype::HEADER, "uid: %d\n", thread_info.uid);
 }
 
-static void dump_stack_segment(log_t* log, BacktraceMap* backtrace_map, Memory* process_memory,
+static void dump_stack_segment(log_t* log, unwindstack::Maps* maps, unwindstack::Memory* memory,
                                uint64_t* sp, size_t words, int label) {
   // Read the data all at once.
   word_t stack_data[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);
+  size_t bytes_read = 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++) {
@@ -148,17 +174,15 @@
     }
     line += StringPrintf("%" PRIPTR "  %" PRIPTR, *sp, static_cast<uint64_t>(stack_data[i]));
 
-    backtrace_map_t 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()) {
+    unwindstack::MapInfo* map_info = maps->Find(stack_data[i]);
+    if (map_info != nullptr && !map_info->name.empty()) {
+      line += "  " + map_info->name;
+      std::string func_name;
+      uint64_t func_offset = 0;
+      if (map_info->GetFunctionName(stack_data[i], &func_name, &func_offset)) {
         line += " (" + func_name;
-        if (offset) {
-          line += StringPrintf("+%" PRIu64, offset);
+        if (func_offset) {
+          line += StringPrintf("+%" PRIu64, func_offset);
         }
         line += ')';
       }
@@ -169,12 +193,11 @@
   }
 }
 
-static void dump_stack(log_t* log, BacktraceMap* backtrace_map, Memory* process_memory,
-                       std::vector<backtrace_frame_data_t>& frames) {
+static void dump_stack(log_t* log, const std::vector<unwindstack::FrameData>& frames,
+                       unwindstack::Maps* maps, unwindstack::Memory* memory) {
   size_t first = 0, last;
   for (size_t i = 0; i < frames.size(); i++) {
-    const backtrace_frame_data_t& frame = frames[i];
-    if (frame.sp) {
+    if (frames[i].sp) {
       if (!first) {
         first = i+1;
       }
@@ -189,29 +212,44 @@
 
   // Dump a few words before the first frame.
   uint64_t sp = frames[first].sp - STACK_WORDS * sizeof(word_t);
-  dump_stack_segment(log, backtrace_map, process_memory, &sp, STACK_WORDS, -1);
+  dump_stack_segment(log, maps, memory, &sp, STACK_WORDS, -1);
+
+#if defined(__LP64__)
+  static constexpr const char delimiter[] = "         ................  ................\n";
+#else
+  static constexpr const char delimiter[] = "         ........  ........\n";
+#endif
 
   // 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 = &frames[i];
+    auto* frame = &frames[i];
     if (sp != frame->sp) {
-      _LOG(log, logtype::STACK, "         ........  ........\n");
+      _LOG(log, logtype::STACK, delimiter);
       sp = frame->sp;
     }
-    if (i == last) {
-      dump_stack_segment(log, backtrace_map, process_memory, &sp, STACK_WORDS, i);
-      if (sp < frame->sp + frame->stack_size) {
-        _LOG(log, logtype::STACK, "         ........  ........\n");
-      }
-    } else {
-      size_t words = frame->stack_size / sizeof(word_t);
-      if (words == 0) {
-        words = 1;
-      } else if (words > STACK_WORDS) {
+    if (i != last) {
+      // Print stack data up to the stack from the next frame.
+      size_t words;
+      uint64_t next_sp = frames[i + 1].sp;
+      if (next_sp < sp) {
+        // The next frame is probably using a completely different stack,
+        // so dump the max from this stack.
         words = STACK_WORDS;
+      } else {
+        words = (next_sp - sp) / sizeof(word_t);
+        if (words == 0) {
+          // The sp is the same as the next frame, print at least
+          // one line for this frame.
+          words = 1;
+        } else if (words > STACK_WORDS) {
+          words = STACK_WORDS;
+        }
       }
-      dump_stack_segment(log, backtrace_map, process_memory, &sp, words, i);
+      dump_stack_segment(log, maps, memory, &sp, words, i);
+    } else {
+      // Print some number of words past the last stack frame since we
+      // don't know how large the stack is.
+      dump_stack_segment(log, maps, memory, &sp, STACK_WORDS, i);
     }
   }
 }
@@ -228,7 +266,7 @@
   return addr_str;
 }
 
-static void dump_abort_message(log_t* log, Memory* process_memory, uint64_t address) {
+static void dump_abort_message(log_t* log, unwindstack::Memory* process_memory, uint64_t address) {
   if (address == 0) {
     return;
   }
@@ -239,31 +277,34 @@
     return;
   }
 
-  char msg[512];
-  if (length >= sizeof(msg)) {
-    _LOG(log, logtype::HEADER, "Abort message too long: claimed length = %zd\n", length);
+  // 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;
   }
 
-  if (!process_memory->ReadFully(address + sizeof(length), msg, length)) {
+  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;
   }
 
-  msg[length] = '\0';
-  _LOG(log, logtype::HEADER, "Abort message: '%s'\n", msg);
+  _LOG(log, logtype::HEADER, "Abort message: '%s'\n", &msg[0]);
 }
 
-static void dump_all_maps(log_t* log, BacktraceMap* map, Memory* process_memory, uint64_t addr) {
+static void dump_all_maps(log_t* log, unwindstack::Unwinder* unwinder, uint64_t addr) {
   bool print_fault_address_marker = addr;
 
-  ScopedBacktraceMapIteratorLock lock(map);
+  unwindstack::Maps* maps = unwinder->GetMaps();
   _LOG(log, logtype::MAPS,
        "\n"
        "memory map (%zu entr%s):",
-       map->size(), map->size() == 1 ? "y" : "ies");
+       maps->Total(), maps->Total() == 1 ? "y" : "ies");
   if (print_fault_address_marker) {
-    if (map->begin() != map->end() && addr < (*map->begin())->start) {
+    if (maps->Total() != 0 && addr < maps->Get(0)->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;
@@ -274,51 +315,54 @@
     _LOG(log, logtype::MAPS, "\n");
   }
 
+  std::shared_ptr<unwindstack::Memory>& process_memory = unwinder->GetProcessMemory();
+
   std::string line;
-  for (auto it = map->begin(); it != map->end(); ++it) {
-    const backtrace_map_t* entry = *it;
+  for (auto const& map_info : *maps) {
     line = "    ";
     if (print_fault_address_marker) {
-      if (addr < entry->start) {
+      if (addr < map_info->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 >= entry->start && addr < entry->end) {
+      } else if (addr >= map_info->start && addr < map_info->end) {
         line = "--->";
         print_fault_address_marker = false;
       }
     }
-    line += get_addr_string(entry->start) + '-' + get_addr_string(entry->end - 1) + ' ';
-    if (entry->flags & PROT_READ) {
+    line += get_addr_string(map_info->start) + '-' + get_addr_string(map_info->end - 1) + ' ';
+    if (map_info->flags & PROT_READ) {
       line += 'r';
     } else {
       line += '-';
     }
-    if (entry->flags & PROT_WRITE) {
+    if (map_info->flags & PROT_WRITE) {
       line += 'w';
     } else {
       line += '-';
     }
-    if (entry->flags & PROT_EXEC) {
+    if (map_info->flags & PROT_EXEC) {
       line += 'x';
     } else {
       line += '-';
     }
-    line += StringPrintf("  %8" PRIx64 "  %8" PRIx64, entry->offset, entry->end - entry->start);
+    line += StringPrintf("  %8" PRIx64 "  %8" PRIx64, map_info->offset,
+                         map_info->end - map_info->start);
     bool space_needed = true;
-    if (entry->name.length() > 0) {
+    if (!map_info->name.empty()) {
       space_needed = false;
-      line += "  " + entry->name;
-      std::string build_id;
-      if ((entry->flags & PROT_READ) && elf_get_build_id(process_memory, entry->start, &build_id)) {
+      line += "  " + map_info->name;
+      std::string build_id = map_info->GetPrintableBuildID();
+      if (!build_id.empty()) {
         line += " (BuildId: " + build_id + ")";
       }
     }
-    if (entry->load_bias != 0) {
+    uint64_t load_bias = map_info->GetLoadBias(process_memory);
+    if (load_bias != 0) {
       if (space_needed) {
         line += ' ';
       }
-      line += StringPrintf(" (load bias 0x%" PRIx64 ")", entry->load_bias);
+      line += StringPrintf(" (load bias 0x%" PRIx64 ")", load_bias);
     }
     _LOG(log, logtype::MAPS, "%s\n", line.c_str());
   }
@@ -328,12 +372,6 @@
   }
 }
 
-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());
-  }
-}
-
 static void print_register_row(log_t* log,
                                const std::vector<std::pair<std::string, uint64_t>>& registers) {
   std::string output;
@@ -346,7 +384,7 @@
   _LOG(log, logtype::REGISTERS, "  %s\n", output.c_str());
 }
 
-void dump_registers(log_t* log, Regs* regs) {
+void dump_registers(log_t* log, unwindstack::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;
@@ -385,23 +423,22 @@
   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) {
+void dump_memory_and_code(log_t* log, unwindstack::Maps* maps, unwindstack::Memory* memory,
+                          unwindstack::Regs* regs) {
+  regs->IterateRegisters([log, maps, 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() + ")";
+    if (maps) {
+      unwindstack::MapInfo* map_info = maps->Find(reg_value);
+      if (map_info != nullptr && !map_info->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);
+static bool dump_thread(log_t* log, unwindstack::Unwinder* unwinder, const ThreadInfo& thread_info,
+                        uint64_t abort_msg_address, bool primary_thread) {
   log->current_tid = thread_info.tid;
   if (!primary_thread) {
     _LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
@@ -409,40 +446,41 @@
   dump_thread_info(log, thread_info);
 
   if (thread_info.siginfo) {
-    dump_signal_info(log, thread_info.siginfo);
+    dump_signal_info(log, thread_info, unwinder->GetProcessMemory().get());
+    dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps());
   }
 
   if (primary_thread) {
-    dump_abort_message(log, process_memory, abort_msg_address);
+    dump_abort_message(log, unwinder->GetProcessMemory().get(), 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)) {
+  std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
+  unwinder->SetRegs(regs_copy.get());
+  unwinder->Unwind();
+  if (unwinder->NumFrames() == 0) {
     _LOG(log, logtype::THREAD, "Failed to unwind");
-    return false;
-  }
-
-  if (!frames.empty()) {
+  } else {
     _LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
-    dump_backtrace(log, frames, "    ");
+    log_backtrace(log, unwinder, "    ");
 
     _LOG(log, logtype::STACK, "\nstack:\n");
-    dump_stack(log, map, process_memory, frames);
+    dump_stack(log, unwinder->frames(), unwinder->GetMaps(), unwinder->GetProcessMemory().get());
   }
 
   if (primary_thread) {
-    dump_memory_and_code(log, map, process_memory, thread_info.registers.get());
-    if (map) {
+    unwindstack::Maps* maps = unwinder->GetMaps();
+    dump_memory_and_code(log, maps, unwinder->GetProcessMemory().get(),
+                         thread_info.registers.get());
+    if (maps != nullptr) {
       uint64_t addr = 0;
       siginfo_t* si = thread_info.siginfo;
-      if (signal_has_si_addr(si->si_signo, si->si_code)) {
+      if (signal_has_si_addr(si)) {
         addr = reinterpret_cast<uint64_t>(si->si_addr);
       }
-      dump_all_maps(log, map, process_memory, addr);
+      dump_all_maps(log, unwinder, addr);
     }
   }
 
@@ -458,7 +496,7 @@
 
 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;
@@ -472,11 +510,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) {
@@ -499,8 +535,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);
@@ -511,19 +545,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);
@@ -531,17 +554,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;
@@ -552,20 +581,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));
   }
 
@@ -586,6 +616,7 @@
 
 void engrave_tombstone_ucontext(int tombstone_fd, uint64_t abort_msg_address, siginfo_t* siginfo,
                                 ucontext_t* ucontext) {
+  pid_t uid = getuid();
   pid_t pid = getpid();
   pid_t tid = gettid();
 
@@ -601,11 +632,13 @@
   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::unique_ptr<unwindstack::Regs> regs(
+      unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
 
   std::map<pid_t, ThreadInfo> threads;
   threads[gettid()] = ThreadInfo{
       .registers = std::move(regs),
+      .uid = uid,
       .tid = tid,
       .thread_name = thread_name,
       .pid = pid,
@@ -613,18 +646,16 @@
       .siginfo = siginfo,
   };
 
-  std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(getpid(), false));
-  if (!backtrace_map) {
-    ALOGE("failed to create backtrace map");
-    _exit(1);
+  unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid);
+  if (!unwinder.Init(unwindstack::Regs::CurrentArch())) {
+    LOG(FATAL) << "Failed to init unwinder object.";
   }
 
-  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);
+  engrave_tombstone(unique_fd(dup(tombstone_fd)), &unwinder, threads, tid, abort_msg_address,
+                    nullptr, nullptr);
 }
 
-void engrave_tombstone(unique_fd output_fd, BacktraceMap* map, Memory* process_memory,
+void engrave_tombstone(unique_fd output_fd, unwindstack::Unwinder* unwinder,
                        const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
                        uint64_t abort_msg_address, OpenFilesList* open_files,
                        std::string* amfd_data) {
@@ -639,12 +670,13 @@
 
   _LOG(&log, logtype::HEADER, "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
   dump_header_info(&log);
+  dump_timestamp(&log, time(nullptr));
 
   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);
+  dump_thread(&log, unwinder, it->second, abort_msg_address, true);
 
   if (want_logs) {
     dump_logs(&log, it->second.pid, 50);
@@ -655,7 +687,7 @@
       continue;
     }
 
-    dump_thread(&log, map, process_memory, thread_info, 0, false);
+    dump_thread(&log, unwinder, thread_info, 0, false);
   }
 
   if (open_files) {
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index d153865..9b2779a 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -35,10 +35,10 @@
 #include <android-base/stringprintf.h>
 #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>
+#include <unwindstack/Unwinder.h>
 
 using android::base::unique_fd;
 
@@ -74,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) {
@@ -100,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];
@@ -257,13 +254,13 @@
   }
 }
 
-bool signal_has_si_addr(int si_signo, int si_code) {
+bool signal_has_si_addr(const siginfo_t* si) {
   // Manually sent signals won't have si_addr.
-  if (si_code == SI_USER || si_code == SI_QUEUE || si_code == SI_TKILL) {
+  if (si->si_code == SI_USER || si->si_code == SI_QUEUE || si->si_code == SI_TKILL) {
     return false;
   }
 
-  switch (si_signo) {
+  switch (si->si_signo) {
     case SIGBUS:
     case SIGFPE:
     case SIGILL:
@@ -275,16 +272,22 @@
   }
 }
 
-const char* get_signame(int sig) {
-  switch (sig) {
+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";
-#if defined(SIGSTKFLT)
     case SIGSTKFLT: return "SIGSTKFLT";
-#endif
     case SIGSTOP: return "SIGSTOP";
     case SIGSYS: return "SIGSYS";
     case SIGTRAP: return "SIGTRAP";
@@ -293,11 +296,11 @@
   }
 }
 
-const char* get_sigcode(int signo, int code) {
+const char* get_sigcode(const siginfo_t* si) {
   // Try the signal-specific codes...
-  switch (signo) {
+  switch (si->si_signo) {
     case SIGILL:
-      switch (code) {
+      switch (si->si_code) {
         case ILL_ILLOPC: return "ILL_ILLOPC";
         case ILL_ILLOPN: return "ILL_ILLOPN";
         case ILL_ILLADR: return "ILL_ILLADR";
@@ -306,11 +309,17 @@
         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_BADSTK, "missing ILL_* si_code");
+      static_assert(NSIGILL == __ILL_BNDMOD, "missing ILL_* si_code");
       break;
     case SIGBUS:
-      switch (code) {
+      switch (si->si_code) {
         case BUS_ADRALN: return "BUS_ADRALN";
         case BUS_ADRERR: return "BUS_ADRERR";
         case BUS_OBJERR: return "BUS_OBJERR";
@@ -320,7 +329,7 @@
       static_assert(NSIGBUS == BUS_MCEERR_AO, "missing BUS_* si_code");
       break;
     case SIGFPE:
-      switch (code) {
+      switch (si->si_code) {
         case FPE_INTDIV: return "FPE_INTDIV";
         case FPE_INTOVF: return "FPE_INTOVF";
         case FPE_FLTDIV: return "FPE_FLTDIV";
@@ -329,45 +338,55 @@
         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_FLTSUB, "missing FPE_* si_code");
+      static_assert(NSIGFPE == FPE_CONDTRAP, "missing FPE_* si_code");
       break;
     case SIGSEGV:
-      switch (code) {
+      switch (si->si_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
+        case SEGV_ACCADI:
+          return "SEGV_ACCADI";
+        case SEGV_ADIDERR:
+          return "SEGV_ADIDERR";
+        case SEGV_ADIPERR:
+          return "SEGV_ADIPERR";
       }
-#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
+      static_assert(NSIGSEGV == SEGV_ADIPERR, "missing SEGV_* si_code");
       break;
-#if defined(SYS_SECCOMP) // Our glibc is too old, and we build this for the host too.
     case SIGSYS:
-      switch (code) {
+      switch (si->si_code) {
         case SYS_SECCOMP: return "SYS_SECCOMP";
       }
       static_assert(NSIGSYS == SYS_SECCOMP, "missing SYS_* si_code");
       break;
-#endif
     case SIGTRAP:
-      switch (code) {
+      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";
+        case TRAP_UNK:
+          return "TRAP_UNDIAGNOSED";
       }
-      if ((code & 0xff) == SIGTRAP) {
-        switch ((code >> 8) & 0xff) {
+      if ((si->si_code & 0xff) == SIGTRAP) {
+        switch ((si->si_code >> 8) & 0xff) {
           case PTRACE_EVENT_FORK:
             return "PTRACE_EVENT_FORK";
           case PTRACE_EVENT_VFORK:
@@ -386,11 +405,11 @@
             return "PTRACE_EVENT_STOP";
         }
       }
-      static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code");
+      static_assert(NSIGTRAP == TRAP_UNK, "missing TRAP_* si_code");
       break;
   }
   // Then the other codes...
-  switch (code) {
+  switch (si->si_code) {
     case SI_USER: return "SI_USER";
     case SI_KERNEL: return "SI_KERNEL";
     case SI_QUEUE: return "SI_QUEUE";
@@ -404,3 +423,22 @@
   // Then give up...
   return "?";
 }
+
+void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix) {
+  if (unwinder->elf_from_memory_not_file()) {
+    _LOG(log, logtype::BACKTRACE,
+         "%sNOTE: Function names and BuildId information is missing for some frames due\n", prefix);
+    _LOG(log, logtype::BACKTRACE,
+         "%sNOTE: to unreadable libraries. For unwinds of apps, only shared libraries\n", prefix);
+    _LOG(log, logtype::BACKTRACE, "%sNOTE: found under the lib/ directory are readable.\n", prefix);
+#if defined(ROOT_POSSIBLE)
+    _LOG(log, logtype::BACKTRACE,
+         "%sNOTE: On this device, run setenforce 0 to make the libraries readable.\n", prefix);
+#endif
+  }
+
+  unwinder->SetDisplayBuildID(true);
+  for (size_t i = 0; i < unwinder->NumFrames(); i++) {
+    _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(i).c_str());
+  }
+}
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index 6903b0e..bfd0fbb 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -81,9 +81,24 @@
 };
 
 // Sent from handler to crash_dump via pipe.
-struct __attribute__((__packed__)) CrashInfo {
-  uint32_t version;  // must be 1.
+struct __attribute__((__packed__)) CrashInfoHeader {
+  uint32_t version;
+};
+
+struct __attribute__((__packed__)) CrashInfoDataV1 {
   siginfo_t siginfo;
   ucontext_t ucontext;
   uintptr_t abort_msg_address;
 };
+
+struct __attribute__((__packed__)) CrashInfoDataV2 : public CrashInfoDataV1 {
+  uintptr_t fdsan_table_address;
+};
+
+struct __attribute__((__packed__)) CrashInfo {
+  CrashInfoHeader header;
+  union {
+    CrashInfoDataV1 v1;
+    CrashInfoDataV2 v2;
+  } data;
+};
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index c446dbb..7d25c50 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -24,6 +24,7 @@
 #include <event2/event.h>
 #include <event2/listener.h>
 
+#include <android-base/cmsg.h>
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
@@ -31,6 +32,7 @@
 #include "protocol.h"
 #include "util.h"
 
+using android::base::ReceiveFileDescriptors;
 using android::base::unique_fd;
 
 static void intercept_close_cb(evutil_socket_t sockfd, short event, void* arg) {
@@ -96,7 +98,8 @@
   {
     unique_fd rcv_fd;
     InterceptRequest intercept_request;
-    ssize_t result = recv_fd(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
+    ssize_t result =
+        ReceiveFileDescriptors(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
 
     if (result == -1) {
       PLOG(WARNING) << "failed to read from intercept socket";
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 15ae406..bbeb181 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -31,6 +31,7 @@
 #include <event2/listener.h>
 #include <event2/thread.h>
 
+#include <android-base/cmsg.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -45,6 +46,7 @@
 #include "intercept_manager.h"
 
 using android::base::GetIntProperty;
+using android::base::SendFileDescriptors;
 using android::base::StringPrintf;
 using android::base::unique_fd;
 
@@ -212,14 +214,22 @@
   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()));
+    if (crash->crash_type == kDebuggerdNativeBacktrace) {
+      // Don't generate tombstones for native backtrace requests.
+      output_fd.reset(open("/dev/null", O_WRONLY | O_CLOEXEC));
+    } else {
+      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_socket_fd, &response, sizeof(response), std::move(output_fd));
+  ssize_t rc =
+      SendFileDescriptors(crash->crash_socket_fd, &response, sizeof(response), output_fd.get());
+  output_fd.reset();
+
   if (rc == -1) {
     PLOG(WARNING) << "failed to send response to CrashRequest";
     goto fail;
diff --git a/debuggerd/tombstoned/tombstoned_client.cpp b/debuggerd/tombstoned/tombstoned_client.cpp
index bdb4c1a..2c23c98 100644
--- a/debuggerd/tombstoned/tombstoned_client.cpp
+++ b/debuggerd/tombstoned/tombstoned_client.cpp
@@ -21,6 +21,7 @@
 
 #include <utility>
 
+#include <android-base/cmsg.h>
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
 #include <cutils/sockets.h>
@@ -28,6 +29,7 @@
 #include "protocol.h"
 #include "util.h"
 
+using android::base::ReceiveFileDescriptors;
 using android::base::unique_fd;
 
 bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd,
@@ -53,7 +55,7 @@
   }
 
   unique_fd tmp_output_fd;
-  ssize_t rc = recv_fd(sockfd, &packet, sizeof(packet), &tmp_output_fd);
+  ssize_t rc = ReceiveFileDescriptors(sockfd, &packet, sizeof(packet), &tmp_output_fd);
   if (rc == -1) {
     async_safe_format_log(ANDROID_LOG_ERROR, "libc",
                           "failed to read response to DumpRequest packet: %s", strerror(errno));
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index 50c5efc..a37b3b9 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -24,73 +24,9 @@
 #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"
 
-using android::base::unique_fd;
-
-ssize_t send_fd(int sockfd, const void* data, size_t len, unique_fd fd) {
-  char cmsg_buf[CMSG_SPACE(sizeof(int))];
-
-  iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
-  msghdr msg = {
-    .msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmsg_buf, .msg_controllen = sizeof(cmsg_buf),
-  };
-  auto cmsg = CMSG_FIRSTHDR(&msg);
-  cmsg->cmsg_level = SOL_SOCKET;
-  cmsg->cmsg_type = SCM_RIGHTS;
-  cmsg->cmsg_len = CMSG_LEN(sizeof(int));
-  *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = fd.get();
-
-  return TEMP_FAILURE_RETRY(sendmsg(sockfd, &msg, 0));
-}
-
-ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len, unique_fd* _Nullable out_fd) {
-  char cmsg_buf[CMSG_SPACE(sizeof(int))];
-
-  iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
-  msghdr msg = {
-    .msg_iov = &iov,
-    .msg_iovlen = 1,
-    .msg_control = cmsg_buf,
-    .msg_controllen = sizeof(cmsg_buf),
-    .msg_flags = 0,
-  };
-  auto cmsg = CMSG_FIRSTHDR(&msg);
-  cmsg->cmsg_level = SOL_SOCKET;
-  cmsg->cmsg_type = SCM_RIGHTS;
-  cmsg->cmsg_len = CMSG_LEN(sizeof(int));
-
-  ssize_t result = TEMP_FAILURE_RETRY(recvmsg(sockfd, &msg, 0));
-  if (result == -1) {
-    return -1;
-  }
-
-  unique_fd fd;
-  bool received_fd = msg.msg_controllen == sizeof(cmsg_buf);
-  if (received_fd) {
-    fd.reset(*reinterpret_cast<int*>(CMSG_DATA(cmsg)));
-  }
-
-  if ((msg.msg_flags & MSG_TRUNC) != 0) {
-    errno = EFBIG;
-    return -1;
-  } else if ((msg.msg_flags & MSG_CTRUNC) != 0) {
-    errno = ERANGE;
-    return -1;
-  }
-
-  if (out_fd) {
-    *out_fd = std::move(fd);
-  } else if (received_fd) {
-    errno = ERANGE;
-    return -1;
-  }
-
-  return result;
-}
-
 std::string get_process_name(pid_t pid) {
   std::string result = "<unknown>";
   android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid), &result);
diff --git a/debuggerd/util.h b/debuggerd/util.h
index 8260b44..e964423 100644
--- a/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -21,29 +21,5 @@
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
-#include <android-base/unique_fd.h>
-
-// *** WARNING ***
-// tombstoned's sockets are SOCK_SEQPACKET sockets.
-// Short reads are treated as errors and short writes are assumed to not happen.
-
-// Sends a packet with an attached fd.
-ssize_t send_fd(int sockfd, const void* _Nonnull data, size_t len, android::base::unique_fd fd);
-
-// Receives a packet and optionally, its attached fd.
-// If out_fd is non-null, packets can optionally have an attached fd.
-// If out_fd is null, received packets must not have an attached fd.
-//
-// Errors:
-//   EOVERFLOW: sockfd is SOCK_DGRAM or SOCK_SEQPACKET and buffer is too small.
-//              The first len bytes of the packet are stored in data, but the
-//              rest of the packet is dropped.
-//   ERANGE:    too many file descriptors were attached to the packet.
-//   ENOMSG:    not enough file descriptors were attached to the packet.
-//
-//   plus any errors returned by the underlying recvmsg.
-ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len,
-                android::base::unique_fd* _Nullable out_fd);
-
 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 8d5b135..fd79cf8 100644
--- a/demangle/Android.bp
+++ b/demangle/Android.bp
@@ -36,6 +36,7 @@
     name: "libdemangle",
     defaults: ["libdemangle_defaults"],
     vendor_available: true,
+    recovery_available: true,
 
     srcs: [
         "Demangler.cpp",
@@ -78,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/Demangler.cpp b/demangle/Demangler.cpp
index 7a3aa81..7bae356 100644
--- a/demangle/Demangler.cpp
+++ b/demangle/Demangler.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <assert.h>
+#include <string.h>
 
 #include <cctype>
 #include <stack>
diff --git a/adf/Android.bp b/deprecated-adf/Android.bp
similarity index 100%
rename from adf/Android.bp
rename to deprecated-adf/Android.bp
diff --git a/adf/OWNERS b/deprecated-adf/OWNERS
similarity index 100%
rename from adf/OWNERS
rename to deprecated-adf/OWNERS
diff --git a/deprecated-adf/libadf/Android.bp b/deprecated-adf/libadf/Android.bp
new file mode 100644
index 0000000..49e3721
--- /dev/null
+++ b/deprecated-adf/libadf/Android.bp
@@ -0,0 +1,28 @@
+// 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.
+
+cc_library {
+    name: "libadf",
+    recovery_available: true,
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
+    srcs: ["adf.cpp"],
+    cflags: ["-Werror"],
+    local_include_dirs: ["include"],
+    export_include_dirs: ["include"],
+}
+
+subdirs = ["tests"]
diff --git a/adf/libadf/adf.cpp b/deprecated-adf/libadf/adf.cpp
similarity index 100%
rename from adf/libadf/adf.cpp
rename to deprecated-adf/libadf/adf.cpp
diff --git a/adf/libadf/include/adf/adf.h b/deprecated-adf/libadf/include/adf/adf.h
similarity index 100%
rename from adf/libadf/include/adf/adf.h
rename to deprecated-adf/libadf/include/adf/adf.h
diff --git a/adf/libadf/include/video/adf.h b/deprecated-adf/libadf/include/video/adf.h
similarity index 100%
rename from adf/libadf/include/video/adf.h
rename to deprecated-adf/libadf/include/video/adf.h
diff --git a/adf/libadf/original-kernel-headers/video/adf.h b/deprecated-adf/libadf/original-kernel-headers/video/adf.h
similarity index 100%
rename from adf/libadf/original-kernel-headers/video/adf.h
rename to deprecated-adf/libadf/original-kernel-headers/video/adf.h
diff --git a/adf/libadf/tests/Android.bp b/deprecated-adf/libadf/tests/Android.bp
similarity index 100%
rename from adf/libadf/tests/Android.bp
rename to deprecated-adf/libadf/tests/Android.bp
diff --git a/adf/libadf/tests/adf_test.cpp b/deprecated-adf/libadf/tests/adf_test.cpp
similarity index 100%
rename from adf/libadf/tests/adf_test.cpp
rename to deprecated-adf/libadf/tests/adf_test.cpp
diff --git a/adf/libadfhwc/Android.bp b/deprecated-adf/libadfhwc/Android.bp
similarity index 100%
rename from adf/libadfhwc/Android.bp
rename to deprecated-adf/libadfhwc/Android.bp
diff --git a/adf/libadfhwc/adfhwc.cpp b/deprecated-adf/libadfhwc/adfhwc.cpp
similarity index 100%
rename from adf/libadfhwc/adfhwc.cpp
rename to deprecated-adf/libadfhwc/adfhwc.cpp
diff --git a/adf/libadfhwc/include/adfhwc/adfhwc.h b/deprecated-adf/libadfhwc/include/adfhwc/adfhwc.h
similarity index 100%
rename from adf/libadfhwc/include/adfhwc/adfhwc.h
rename to deprecated-adf/libadfhwc/include/adfhwc/adfhwc.h
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..716fe95
--- /dev/null
+++ b/fastboot/Android.bp
@@ -0,0 +1,311 @@
+// 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.
+
+// This is required because no Android.bp can include a library defined in an
+// Android.mk. Eventually should kill libfastboot (defined in Android.mk)
+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",
+        "liblp",
+    ],
+
+    header_libs: [
+        "bootimg_headers",
+    ],
+
+    export_header_lib_headers: [
+        "bootimg_headers",
+    ],
+
+    target: {
+        linux: {
+            srcs: ["usb_linux.cpp"],
+        },
+
+        darwin: {
+            srcs: ["usb_osx.cpp"],
+
+            host_ldlibs: [
+                "-framework CoreFoundation",
+                "-framework IOKit",
+            ],
+        },
+
+        windows: {
+            srcs: ["usb_windows.cpp"],
+
+            host_ldlibs: [
+                "-lws2_32",
+            ],
+        },
+    },
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wunreachable-code",
+    ],
+
+    export_include_dirs: ["."],
+
+}
+
+cc_defaults {
+    name: "fastboot_defaults",
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wvla",
+    ],
+    rtti: true,
+
+    clang_cflags: [
+        "-Wthread-safety",
+    ],
+}
+
+cc_binary {
+    name: "fastbootd",
+    defaults: ["fastboot_defaults"],
+
+    recovery: true,
+
+    srcs: [
+        "device/commands.cpp",
+        "device/fastboot_device.cpp",
+        "device/flashing.cpp",
+        "device/main.cpp",
+        "device/usb_client.cpp",
+        "device/utility.cpp",
+        "device/variables.cpp",
+    ],
+
+    shared_libs: [
+        "android.hardware.boot@1.0",
+        "android.hardware.fastboot@1.0",
+        "android.hardware.health@2.0",
+        "libadbd",
+        "libasyncio",
+        "libbase",
+        "libbootloader_message",
+        "libcutils",
+        "libext2_uuid",
+        "libext4_utils",
+        "libfs_mgr",
+        "libgsi",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "liblog",
+        "liblp",
+        "libsparse",
+        "libutils",
+    ],
+
+    static_libs: [
+        "libhealthhalutils",
+    ],
+}
+
+cc_defaults {
+    name: "fastboot_host_defaults",
+
+    use_version_lib: true,
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wunreachable-code",
+    ],
+
+    target: {
+        darwin: {
+            cflags: ["-Wno-unused-parameter"],
+            host_ldlibs: [
+                "-lpthread",
+                "-framework CoreFoundation",
+                "-framework IOKit",
+            ],
+        },
+        windows: {
+            enabled: true,
+
+            host_ldlibs: ["-lws2_32"],
+        },
+        not_windows: {
+            static_libs: [
+                "libext4_utils",
+            ],
+        },
+    },
+
+    stl: "libc++_static",
+
+    // 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.
+    shared_libs: [],
+
+    header_libs: ["bootimg_headers"],
+    static_libs: [
+        "libziparchive",
+        "libsparse",
+        "libutils",
+        "liblog",
+        "libz",
+        "libdiagnose_usb",
+        "libbase",
+        "libcutils",
+        "libgtest_host",
+        "liblp",
+        "libcrypto",
+    ],
+}
+
+//
+// Build host libfastboot.
+//
+
+cc_library_host_static {
+    name: "libfastboot",
+    defaults: ["fastboot_host_defaults"],
+
+    srcs: [
+        "bootimg_utils.cpp",
+        "fastboot.cpp",
+        "fs.cpp",
+        "socket.cpp",
+        "tcp.cpp",
+        "udp.cpp",
+        "util.cpp",
+        "fastboot_driver.cpp",
+    ],
+
+    // Only version the final binaries
+    use_version_lib: false,
+    static_libs: ["libbuildversion"],
+
+    generated_headers: ["platform_tools_version"],
+
+    target: {
+        windows: {
+            srcs: ["usb_windows.cpp"],
+
+            include_dirs: ["development/host/windows/usb/api"],
+        },
+        darwin: {
+            srcs: ["usb_osx.cpp"],
+        },
+        linux_glibc: {
+            srcs: ["usb_linux.cpp"],
+        },
+    },
+}
+
+//
+// Build host fastboot / fastboot.exe
+//
+
+cc_binary_host {
+    name: "fastboot",
+    defaults: ["fastboot_host_defaults"],
+
+    srcs: ["main.cpp"],
+    static_libs: ["libfastboot"],
+
+    required: [
+        "mke2fs",
+        "make_f2fs",
+    ],
+    dist: {
+        targets: [
+            "dist_files",
+            "sdk",
+            "win_sdk",
+        ],
+    },
+
+    target: {
+        not_windows: {
+            required: [
+                "e2fsdroid",
+                "mke2fs.conf",
+                "sload_f2fs",
+            ],
+        },
+        windows: {
+            required: ["AdbWinUsbApi"],
+            shared_libs: ["AdbWinApi"],
+        },
+    },
+}
+
+//
+// Build host fastboot_test.
+//
+
+cc_test_host {
+    name: "fastboot_test",
+    defaults: ["fastboot_host_defaults"],
+
+    srcs: [
+        "fastboot_test.cpp",
+        "socket_mock.cpp",
+        "socket_test.cpp",
+        "tcp_test.cpp",
+        "udp_test.cpp",
+    ],
+
+    static_libs: ["libfastboot"],
+
+    target: {
+        windows: {
+            shared_libs: ["AdbWinApi"],
+        },
+        windows_x86_64: {
+            // Avoid trying to build for win64
+            enabled: false,
+        },
+    },
+}
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 944b00b..17ec392 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -14,114 +14,13 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-include $(LOCAL_PATH)/../platform_tools_tool_version.mk
+#
+# Package fastboot-related executables.
+#
 
-include $(CLEAR_VARS)
-
-LOCAL_CFLAGS += -DFASTBOOT_VERSION="\"$(tool_version)\""
-
-LOCAL_C_INCLUDES := \
-  $(LOCAL_PATH)/../adb \
-
-LOCAL_HEADER_LIBRARIES := bootimg_headers
-
-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_REQUIRED_MODULES := mke2fs make_f2fs
-
-LOCAL_SRC_FILES_linux := usb_linux.cpp
-LOCAL_STATIC_LIBRARIES_linux := libselinux
-LOCAL_REQUIRED_MODULES_linux := e2fsdroid mke2fs.conf sload_f2fs
-
-LOCAL_SRC_FILES_darwin := usb_osx.cpp
-LOCAL_STATIC_LIBRARIES_darwin := libselinux
-LOCAL_REQUIRED_MODULES_darwin := e2fsdroid mke2fs.conf sload_f2fs
-LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
-LOCAL_CFLAGS_darwin := -Wno-unused-parameter
-
-LOCAL_SRC_FILES_windows := usb_windows.cpp
-LOCAL_SHARED_LIBRARIES_windows := AdbWinApi
-LOCAL_REQUIRED_MODULES_windows := AdbWinUsbApi
-LOCAL_LDLIBS_windows := -lws2_32
-LOCAL_C_INCLUDES_windows := development/host/windows/usb/api
-
-LOCAL_STATIC_LIBRARIES := \
-    libziparchive \
-    libsparse \
-    libutils \
-    liblog \
-    libz \
-    libdiagnose_usb \
-    libbase \
-    libcutils \
-    libgtest_host \
-
-LOCAL_CXX_STL := libc++_static
-
-# 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)
-my_dist_files += $(HOST_OUT_EXECUTABLES)/mke2fs$(HOST_EXECUTABLE_SUFFIX)
-my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid$(HOST_EXECUTABLE_SUFFIX)
-my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs$(HOST_EXECUTABLE_SUFFIX)
-my_dist_files += $(HOST_OUT_EXECUTABLES)/sload_f2fs$(HOST_EXECUTABLE_SUFFIX)
+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
-# 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_HOST_OS := darwin linux windows
-
-LOCAL_SRC_FILES := \
-    socket.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
-
-include $(BUILD_HOST_NATIVE_TEST)
diff --git a/fastboot/OWNERS b/fastboot/OWNERS
index 2d12d50..2088ae3 100644
--- a/fastboot/OWNERS
+++ b/fastboot/OWNERS
@@ -1,3 +1,4 @@
 dpursell@google.com
 enh@google.com
 jmgao@google.com
+tomcherry@google.com
diff --git a/fastboot/README.md b/fastboot/README.md
index ec7dcb4..c224448 100644
--- a/fastboot/README.md
+++ b/fastboot/README.md
@@ -136,10 +136,6 @@
                        should not support "upload" unless it supports an
                        oem command that requires "upload" capabilities.
 
-    verify:%08x        Send a digital signature to verify the downloaded
-                       data.  Required if the bootloader is "secure"
-                       otherwise "flash" and "boot" will be ignored.
-
     flash:%s           Write the previously downloaded image to the
                        named partition (if possible).
 
@@ -159,8 +155,6 @@
                        the bootloader and then upgrading other partitions
                        using the new bootloader.
 
-    powerdown          Power off the device.
-
 
 
 ## Client Variables
@@ -186,10 +180,45 @@
                         bootloader requiring a signature before
                         it will install or boot images.
 
+    is-userspace        If the value is "yes", the device is running
+                        fastbootd. Otherwise, it is running fastboot
+                        in the bootloader.
+
 Names starting with a lowercase character are reserved by this
 specification.  OEM-specific names should not start with lowercase
 characters.
 
+## Logical Partitions
+
+There are a number of commands to interact with logical partitions:
+
+    update-super:%s:%s  Write the previously downloaded image to a super
+                        partition. Unlike the "flash" command, this has
+                        special rules. The image must have been created by
+                        the lpmake command, and must not be a sparse image.
+                        If the last argument is "wipe", then all existing
+                        logical partitions are deleted. If no final argument
+                        is specified, the partition tables are merged. Any
+                        partition in the new image that does not exist in the
+                        old image is created with a zero size.
+
+                        In all cases, this will cause the temporary "scratch"
+                        partition to be deleted if it exists.
+
+    create-logical-partition:%s:%d
+                        Create a logical partition with the given name and
+                        size, in the super partition.
+
+    delete-logical-partition:%s
+                        Delete a logical partition with the given name.
+
+    resize-logical-partition:%s:%d
+                        Change the size of the named logical partition.
+
+In addition, there is a variable to test whether a partition is logical:
+
+    is-logical:%s       If the value is "yes", the partition is logical.
+                        Otherwise the partition is physical.
 
 ## TCP Protocol v1
 
diff --git a/fastboot/bootimg_utils.cpp b/fastboot/bootimg_utils.cpp
index 2e8c334..46d4bd3 100644
--- a/fastboot/bootimg_utils.cpp
+++ b/fastboot/bootimg_utils.cpp
@@ -28,55 +28,59 @@
 
 #include "bootimg_utils.h"
 
-#include "fastboot.h"
+#include "util.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
-void bootimg_set_cmdline(boot_img_hdr_v1* h, const char* cmdline) {
-    if (strlen(cmdline) >= sizeof(h->cmdline)) die("command line too large: %zu", strlen(cmdline));
-    strcpy(reinterpret_cast<char*>(h->cmdline), cmdline);
+void bootimg_set_cmdline(boot_img_hdr_v2* 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_v1* 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, uint32_t header_version, int64_t* bootimg_size) {
-    size_t page_mask = page_size - 1;
+boot_img_hdr_v2* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
+                           const std::vector<char>& second, const std::vector<char>& dtb,
+                           size_t base, const boot_img_hdr_v2& src, std::vector<char>* out) {
+    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);
+    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);
+    int64_t dtb_actual = (dtb.size() + page_mask) & (~page_mask);
 
-    *bootimg_size = header_actual + kernel_actual + ramdisk_actual + second_actual;
+    int64_t bootimg_size =
+            header_actual + kernel_actual + ramdisk_actual + second_actual + dtb_actual;
+    out->resize(bootimg_size);
 
-    boot_img_hdr_v1* hdr = reinterpret_cast<boot_img_hdr_v1*>(calloc(*bootimg_size, 1));
-    if (hdr == nullptr) {
-        return hdr;
-    }
+    boot_img_hdr_v2* hdr = reinterpret_cast<boot_img_hdr_v2*>(out->data());
 
+    *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_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 (header_version) {
-        hdr->header_version = header_version;
+    if (hdr->header_version == 1) {
         hdr->header_size = sizeof(boot_img_hdr_v1);
+    } else if (hdr->header_version == 2) {
+        hdr->header_size = sizeof(boot_img_hdr_v2);
+        hdr->dtb_size = dtb.size();
+        hdr->dtb_addr += base;
     }
 
-    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.data(), kernel.size());
+    memcpy(hdr->magic + hdr->page_size + kernel_actual, ramdisk.data(), ramdisk.size());
+    memcpy(hdr->magic + hdr->page_size + kernel_actual + ramdisk_actual, second.data(),
+           second.size());
+    memcpy(hdr->magic + hdr->page_size + kernel_actual + ramdisk_actual + second_actual, dtb.data(),
+           dtb.size());
     return hdr;
 }
diff --git a/fastboot/bootimg_utils.h b/fastboot/bootimg_utils.h
index d3993f5..b7cf9bd 100644
--- a/fastboot/bootimg_utils.h
+++ b/fastboot/bootimg_utils.h
@@ -26,17 +26,16 @@
  * 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_v1* h, const char* cmdline);
-boot_img_hdr_v1* 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, uint32_t header_version, int64_t* bootimg_size);
+#include <string>
+#include <vector>
 
-#endif
+boot_img_hdr_v2* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
+                           const std::vector<char>& second, const std::vector<char>& dtb,
+                           size_t base, const boot_img_hdr_v2& src, std::vector<char>* out);
+void bootimg_set_cmdline(boot_img_hdr_v2* h, const std::string& cmdline);
diff --git a/fastboot/constants.h b/fastboot/constants.h
new file mode 100644
index 0000000..8a72627
--- /dev/null
+++ b/fastboot/constants.h
@@ -0,0 +1,68 @@
+/*
+ * 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_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_REBOOT_RECOVERY "reboot-recovery"
+#define FB_CMD_REBOOT_FASTBOOT "reboot-fastboot"
+#define FB_CMD_CREATE_PARTITION "create-logical-partition"
+#define FB_CMD_DELETE_PARTITION "delete-logical-partition"
+#define FB_CMD_RESIZE_PARTITION "resize-logical-partition"
+#define FB_CMD_UPDATE_SUPER "update-super"
+#define FB_CMD_OEM "oem"
+#define FB_CMD_GSI "gsi"
+
+#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"
+#define FB_VAR_PARTITION_TYPE "partition-type"
+#define FB_VAR_SLOT_SUCCESSFUL "slot-successful"
+#define FB_VAR_SLOT_UNBOOTABLE "slot-unbootable"
+#define FB_VAR_IS_LOGICAL "is-logical"
+#define FB_VAR_IS_USERSPACE "is-userspace"
+#define FB_VAR_HW_REVISION "hw-revision"
+#define FB_VAR_VARIANT "variant"
+#define FB_VAR_OFF_MODE_CHARGE_STATE "off-mode-charge"
+#define FB_VAR_BATTERY_VOLTAGE "battery-voltage"
+#define FB_VAR_BATTERY_SOC_OK "battery-soc-ok"
+#define FB_VAR_SUPER_PARTITION_NAME "super-partition-name"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
new file mode 100644
index 0000000..409ef70
--- /dev/null
+++ b/fastboot/device/commands.cpp
@@ -0,0 +1,525 @@
+/*
+ * 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 "commands.h"
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#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 <cutils/android_reboot.h>
+#include <ext4_utils/wipe.h>
+#include <fs_mgr.h>
+#include <fs_mgr/roots.h>
+#include <libgsi/libgsi.h>
+#include <liblp/builder.h>
+#include <liblp/liblp.h>
+#include <uuid/uuid.h>
+
+#include "constants.h"
+#include "fastboot_device.h"
+#include "flashing.h"
+#include "utility.h"
+
+using android::fs_mgr::MetadataBuilder;
+using ::android::hardware::hidl_string;
+using ::android::hardware::boot::V1_0::BoolResult;
+using ::android::hardware::boot::V1_0::CommandResult;
+using ::android::hardware::boot::V1_0::Slot;
+using ::android::hardware::fastboot::V1_0::Result;
+using ::android::hardware::fastboot::V1_0::Status;
+
+struct VariableHandlers {
+    // Callback to retrieve the value of a single variable.
+    std::function<bool(FastbootDevice*, const std::vector<std::string>&, std::string*)> get;
+    // Callback to retrieve all possible argument combinations, for getvar all.
+    std::function<std::vector<std::vector<std::string>>(FastbootDevice*)> get_all_args;
+};
+
+static void GetAllVars(FastbootDevice* device, const std::string& name,
+                       const VariableHandlers& handlers) {
+    if (!handlers.get_all_args) {
+        std::string message;
+        if (!handlers.get(device, std::vector<std::string>(), &message)) {
+            return;
+        }
+        device->WriteInfo(android::base::StringPrintf("%s:%s", name.c_str(), message.c_str()));
+        return;
+    }
+
+    auto all_args = handlers.get_all_args(device);
+    for (const auto& args : all_args) {
+        std::string message;
+        if (!handlers.get(device, args, &message)) {
+            continue;
+        }
+        std::string arg_string = android::base::Join(args, ":");
+        device->WriteInfo(android::base::StringPrintf("%s:%s:%s", name.c_str(), arg_string.c_str(),
+                                                      message.c_str()));
+    }
+}
+
+bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    const std::unordered_map<std::string, VariableHandlers> kVariableMap = {
+            {FB_VAR_VERSION, {GetVersion, nullptr}},
+            {FB_VAR_VERSION_BOOTLOADER, {GetBootloaderVersion, nullptr}},
+            {FB_VAR_VERSION_BASEBAND, {GetBasebandVersion, nullptr}},
+            {FB_VAR_PRODUCT, {GetProduct, nullptr}},
+            {FB_VAR_SERIALNO, {GetSerial, nullptr}},
+            {FB_VAR_VARIANT, {GetVariant, nullptr}},
+            {FB_VAR_SECURE, {GetSecure, nullptr}},
+            {FB_VAR_UNLOCKED, {GetUnlocked, nullptr}},
+            {FB_VAR_MAX_DOWNLOAD_SIZE, {GetMaxDownloadSize, nullptr}},
+            {FB_VAR_CURRENT_SLOT, {::GetCurrentSlot, nullptr}},
+            {FB_VAR_SLOT_COUNT, {GetSlotCount, nullptr}},
+            {FB_VAR_HAS_SLOT, {GetHasSlot, GetAllPartitionArgsNoSlot}},
+            {FB_VAR_SLOT_SUCCESSFUL, {GetSlotSuccessful, nullptr}},
+            {FB_VAR_SLOT_UNBOOTABLE, {GetSlotUnbootable, nullptr}},
+            {FB_VAR_PARTITION_SIZE, {GetPartitionSize, GetAllPartitionArgsWithSlot}},
+            {FB_VAR_PARTITION_TYPE, {GetPartitionType, GetAllPartitionArgsWithSlot}},
+            {FB_VAR_IS_LOGICAL, {GetPartitionIsLogical, GetAllPartitionArgsWithSlot}},
+            {FB_VAR_IS_USERSPACE, {GetIsUserspace, nullptr}},
+            {FB_VAR_OFF_MODE_CHARGE_STATE, {GetOffModeChargeState, nullptr}},
+            {FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
+            {FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
+            {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
+            {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}}};
+
+    if (args.size() < 2) {
+        return device->WriteFail("Missing argument");
+    }
+
+    // Special case: return all variables that we can.
+    if (args[1] == "all") {
+        for (const auto& [name, handlers] : kVariableMap) {
+            GetAllVars(device, name, handlers);
+        }
+        return device->WriteOkay("");
+    }
+
+    // args[0] is command name, args[1] is variable.
+    auto found_variable = kVariableMap.find(args[1]);
+    if (found_variable == kVariableMap.end()) {
+        return device->WriteFail("Unknown variable");
+    }
+
+    std::string message;
+    std::vector<std::string> getvar_args(args.begin() + 2, args.end());
+    if (!found_variable->second.get(device, getvar_args, &message)) {
+        return device->WriteFail(message);
+    }
+    return device->WriteOkay(message);
+}
+
+bool EraseHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 2) {
+        return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments");
+    }
+
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL, "Erase is not allowed on locked devices");
+    }
+
+    PartitionHandle handle;
+    if (!OpenPartition(device, args[1], &handle)) {
+        return device->WriteStatus(FastbootResult::FAIL, "Partition doesn't exist");
+    }
+    if (wipe_block_device(handle.fd(), get_block_device_size(handle.fd())) == 0) {
+        return device->WriteStatus(FastbootResult::OKAY, "Erasing succeeded");
+    }
+    return device->WriteStatus(FastbootResult::FAIL, "Erasing failed");
+}
+
+bool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    auto fastboot_hal = device->fastboot_hal();
+    if (!fastboot_hal) {
+        return device->WriteStatus(FastbootResult::FAIL, "Unable to open fastboot HAL");
+    }
+
+    Result ret;
+    auto ret_val = fastboot_hal->doOemCommand(args[0], [&](Result result) { ret = result; });
+    if (!ret_val.isOk()) {
+        return device->WriteStatus(FastbootResult::FAIL, "Unable to do OEM command");
+    }
+    if (ret.status != Status::SUCCESS) {
+        return device->WriteStatus(FastbootResult::FAIL, ret.message);
+    }
+
+    return device->WriteStatus(FastbootResult::OKAY, ret.message);
+}
+
+bool DownloadHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 2) {
+        return device->WriteStatus(FastbootResult::FAIL, "size argument unspecified");
+    }
+
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL,
+                                   "Download is not allowed on locked devices");
+    }
+
+    // arg[0] is the command name, arg[1] contains size of data to be downloaded
+    unsigned int size;
+    if (!android::base::ParseUint("0x" + args[1], &size, kMaxDownloadSizeDefault)) {
+        return device->WriteStatus(FastbootResult::FAIL, "Invalid size");
+    }
+    device->download_data().resize(size);
+    if (!device->WriteStatus(FastbootResult::DATA, android::base::StringPrintf("%08x", size))) {
+        return false;
+    }
+
+    if (device->HandleData(true, &device->download_data())) {
+        return device->WriteStatus(FastbootResult::OKAY, "");
+    }
+
+    PLOG(ERROR) << "Couldn't download data";
+    return device->WriteStatus(FastbootResult::FAIL, "Couldn't download data");
+}
+
+bool FlashHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 2) {
+        return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments");
+    }
+
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL,
+                                   "Flashing is not allowed on locked devices");
+    }
+
+    int ret = Flash(device, args[1]);
+    if (ret < 0) {
+        return device->WriteStatus(FastbootResult::FAIL, strerror(-ret));
+    }
+    return device->WriteStatus(FastbootResult::OKAY, "Flashing succeeded");
+}
+
+bool SetActiveHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 2) {
+        return device->WriteStatus(FastbootResult::FAIL, "Missing slot argument");
+    }
+
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL,
+                                   "set_active command is not allowed on locked devices");
+    }
+
+    // Slot suffix needs to be between 'a' and 'z'.
+    Slot slot;
+    if (!GetSlotNumber(args[1], &slot)) {
+        return device->WriteStatus(FastbootResult::FAIL, "Bad slot suffix");
+    }
+
+    // Non-A/B devices will not have a boot control HAL.
+    auto boot_control_hal = device->boot_control_hal();
+    if (!boot_control_hal) {
+        return device->WriteStatus(FastbootResult::FAIL,
+                                   "Cannot set slot: boot control HAL absent");
+    }
+    if (slot >= boot_control_hal->getNumberSlots()) {
+        return device->WriteStatus(FastbootResult::FAIL, "Slot out of range");
+    }
+    CommandResult ret;
+    auto cb = [&ret](CommandResult result) { ret = result; };
+    auto result = boot_control_hal->setActiveBootSlot(slot, cb);
+    if (result.isOk() && ret.success) {
+        // Save as slot suffix to match the suffix format as returned from
+        // the boot control HAL.
+        auto current_slot = "_" + args[1];
+        device->set_active_slot(current_slot);
+        return device->WriteStatus(FastbootResult::OKAY, "");
+    }
+    return device->WriteStatus(FastbootResult::FAIL, "Unable to set slot");
+}
+
+bool ShutDownHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto result = device->WriteStatus(FastbootResult::OKAY, "Shutting down");
+    android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,fastboot");
+    device->CloseDevice();
+    TEMP_FAILURE_RETRY(pause());
+    return result;
+}
+
+bool RebootHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto result = device->WriteStatus(FastbootResult::OKAY, "Rebooting");
+    android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,from_fastboot");
+    device->CloseDevice();
+    TEMP_FAILURE_RETRY(pause());
+    return result;
+}
+
+bool RebootBootloaderHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto result = device->WriteStatus(FastbootResult::OKAY, "Rebooting bootloader");
+    android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader");
+    device->CloseDevice();
+    TEMP_FAILURE_RETRY(pause());
+    return result;
+}
+
+bool RebootFastbootHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto result = device->WriteStatus(FastbootResult::OKAY, "Rebooting fastboot");
+    android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,fastboot");
+    device->CloseDevice();
+    TEMP_FAILURE_RETRY(pause());
+    return result;
+}
+
+static bool EnterRecovery() {
+    const char msg_switch_to_recovery = 'r';
+
+    android::base::unique_fd sock(socket(AF_UNIX, SOCK_STREAM, 0));
+    if (sock < 0) {
+        PLOG(ERROR) << "Couldn't create sock";
+        return false;
+    }
+
+    struct sockaddr_un addr = {.sun_family = AF_UNIX};
+    strncpy(addr.sun_path, "/dev/socket/recovery", sizeof(addr.sun_path) - 1);
+    if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+        PLOG(ERROR) << "Couldn't connect to recovery";
+        return false;
+    }
+    // Switch to recovery will not update the boot reason since it does not
+    // require a reboot.
+    auto ret = write(sock, &msg_switch_to_recovery, sizeof(msg_switch_to_recovery));
+    if (ret != sizeof(msg_switch_to_recovery)) {
+        PLOG(ERROR) << "Couldn't write message to switch to recovery";
+        return false;
+    }
+
+    return true;
+}
+
+bool RebootRecoveryHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto status = true;
+    if (EnterRecovery()) {
+        status = device->WriteStatus(FastbootResult::OKAY, "Rebooting to recovery");
+    } else {
+        status = device->WriteStatus(FastbootResult::FAIL, "Unable to reboot to recovery");
+    }
+    device->CloseDevice();
+    TEMP_FAILURE_RETRY(pause());
+    return status;
+}
+
+// Helper class for opening a handle to a MetadataBuilder and writing the new
+// partition table to the same place it was read.
+class PartitionBuilder {
+  public:
+    explicit PartitionBuilder(FastbootDevice* device, const std::string& partition_name);
+
+    bool Write();
+    bool Valid() const { return !!builder_; }
+    MetadataBuilder* operator->() const { return builder_.get(); }
+
+  private:
+    FastbootDevice* device_;
+    std::string super_device_;
+    uint32_t slot_number_;
+    std::unique_ptr<MetadataBuilder> builder_;
+};
+
+PartitionBuilder::PartitionBuilder(FastbootDevice* device, const std::string& partition_name)
+    : device_(device) {
+    std::string slot_suffix = GetSuperSlotSuffix(device, partition_name);
+    slot_number_ = android::fs_mgr::SlotNumberForSlotSuffix(slot_suffix);
+    auto super_device = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number_));
+    if (!super_device) {
+        return;
+    }
+    super_device_ = *super_device;
+    builder_ = MetadataBuilder::New(super_device_, slot_number_);
+}
+
+bool PartitionBuilder::Write() {
+    auto metadata = builder_->Export();
+    if (!metadata) {
+        return false;
+    }
+    return UpdateAllPartitionMetadata(device_, super_device_, *metadata.get());
+}
+
+bool CreatePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 3) {
+        return device->WriteFail("Invalid partition name and size");
+    }
+
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL, "Command not available on locked devices");
+    }
+
+    uint64_t partition_size;
+    std::string partition_name = args[1];
+    if (!android::base::ParseUint(args[2].c_str(), &partition_size)) {
+        return device->WriteFail("Invalid partition size");
+    }
+
+    PartitionBuilder builder(device, partition_name);
+    if (!builder.Valid()) {
+        return device->WriteFail("Could not open super partition");
+    }
+    // TODO(112433293) Disallow if the name is in the physical table as well.
+    if (builder->FindPartition(partition_name)) {
+        return device->WriteFail("Partition already exists");
+    }
+
+    auto partition = builder->AddPartition(partition_name, 0);
+    if (!partition) {
+        return device->WriteFail("Failed to add partition");
+    }
+    if (!builder->ResizePartition(partition, partition_size)) {
+        builder->RemovePartition(partition_name);
+        return device->WriteFail("Not enough space for partition");
+    }
+    if (!builder.Write()) {
+        return device->WriteFail("Failed to write partition table");
+    }
+    return device->WriteOkay("Partition created");
+}
+
+bool DeletePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 2) {
+        return device->WriteFail("Invalid partition name and size");
+    }
+
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL, "Command not available on locked devices");
+    }
+
+    std::string partition_name = args[1];
+
+    PartitionBuilder builder(device, partition_name);
+    if (!builder.Valid()) {
+        return device->WriteFail("Could not open super partition");
+    }
+    builder->RemovePartition(partition_name);
+    if (!builder.Write()) {
+        return device->WriteFail("Failed to write partition table");
+    }
+    return device->WriteOkay("Partition deleted");
+}
+
+bool ResizePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 3) {
+        return device->WriteFail("Invalid partition name and size");
+    }
+
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL, "Command not available on locked devices");
+    }
+
+    uint64_t partition_size;
+    std::string partition_name = args[1];
+    if (!android::base::ParseUint(args[2].c_str(), &partition_size)) {
+        return device->WriteFail("Invalid partition size");
+    }
+
+    PartitionBuilder builder(device, partition_name);
+    if (!builder.Valid()) {
+        return device->WriteFail("Could not open super partition");
+    }
+
+    auto partition = builder->FindPartition(partition_name);
+    if (!partition) {
+        return device->WriteFail("Partition does not exist");
+    }
+    if (!builder->ResizePartition(partition, partition_size)) {
+        return device->WriteFail("Not enough space to resize partition");
+    }
+    if (!builder.Write()) {
+        return device->WriteFail("Failed to write partition table");
+    }
+    return device->WriteOkay("Partition resized");
+}
+
+bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 2) {
+        return device->WriteFail("Invalid arguments");
+    }
+
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL, "Command not available on locked devices");
+    }
+
+    bool wipe = (args.size() >= 3 && args[2] == "wipe");
+    return UpdateSuper(device, args[1], wipe);
+}
+
+class AutoMountMetadata {
+  public:
+    AutoMountMetadata() {
+        android::fs_mgr::Fstab proc_mounts;
+        if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
+            LOG(ERROR) << "Could not read /proc/mounts";
+            return;
+        }
+
+        auto iter = std::find_if(proc_mounts.begin(), proc_mounts.end(),
+                [](const auto& entry) { return entry.mount_point == "/metadata"; });
+        if (iter != proc_mounts.end()) {
+            mounted_ = true;
+            return;
+        }
+
+        if (!ReadDefaultFstab(&fstab_)) {
+            LOG(ERROR) << "Could not read default fstab";
+            return;
+        }
+        mounted_ = EnsurePathMounted(&fstab_, "/metadata");
+        should_unmount_ = true;
+    }
+    ~AutoMountMetadata() {
+        if (mounted_ && should_unmount_) {
+            EnsurePathUnmounted(&fstab_, "/metadata");
+        }
+    }
+    explicit operator bool() const { return mounted_; }
+
+  private:
+    android::fs_mgr::Fstab fstab_;
+    bool mounted_ = false;
+    bool should_unmount_ = false;
+};
+
+bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() != 2) {
+        return device->WriteFail("Invalid arguments");
+    }
+
+    AutoMountMetadata mount_metadata;
+    if (!mount_metadata) {
+        return device->WriteFail("Could not find GSI install");
+    }
+
+    if (!android::gsi::IsGsiInstalled()) {
+        return device->WriteStatus(FastbootResult::FAIL, "No GSI is installed");
+    }
+
+    if (args[1] == "wipe") {
+        if (!android::gsi::UninstallGsi()) {
+            return device->WriteStatus(FastbootResult::FAIL, strerror(errno));
+        }
+    } else if (args[1] == "disable") {
+        if (!android::gsi::DisableGsi()) {
+            return device->WriteStatus(FastbootResult::FAIL, strerror(errno));
+        }
+    }
+    return device->WriteStatus(FastbootResult::OKAY, "Success");
+}
diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h
new file mode 100644
index 0000000..afd6d08
--- /dev/null
+++ b/fastboot/device/commands.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
+
+#include <functional>
+#include <string>
+#include <vector>
+
+constexpr unsigned int kMaxDownloadSizeDefault = 0x20000000;
+
+class FastbootDevice;
+
+enum class FastbootResult {
+    OKAY,
+    FAIL,
+    INFO,
+    DATA,
+};
+
+// Execute a command with the given arguments (possibly empty).
+using CommandHandler = std::function<bool(FastbootDevice*, const std::vector<std::string>&)>;
+
+bool DownloadHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool SetActiveHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool ShutDownHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool RebootHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool RebootBootloaderHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool RebootFastbootHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool RebootRecoveryHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool EraseHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool FlashHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool CreatePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool DeletePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool ResizePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args);
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
new file mode 100644
index 0000000..56fafab
--- /dev/null
+++ b/fastboot/device/fastboot_device.cpp
@@ -0,0 +1,168 @@
+/*
+ * 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 "fastboot_device.h"
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android/hardware/boot/1.0/IBootControl.h>
+#include <android/hardware/fastboot/1.0/IFastboot.h>
+#include <healthhalutils/HealthHalUtils.h>
+
+#include <algorithm>
+
+#include "constants.h"
+#include "flashing.h"
+#include "usb_client.h"
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::boot::V1_0::IBootControl;
+using ::android::hardware::boot::V1_0::Slot;
+using ::android::hardware::fastboot::V1_0::IFastboot;
+using ::android::hardware::health::V2_0::get_health_service;
+
+namespace sph = std::placeholders;
+
+FastbootDevice::FastbootDevice()
+    : kCommandMap({
+              {FB_CMD_SET_ACTIVE, SetActiveHandler},
+              {FB_CMD_DOWNLOAD, DownloadHandler},
+              {FB_CMD_GETVAR, GetVarHandler},
+              {FB_CMD_SHUTDOWN, ShutDownHandler},
+              {FB_CMD_REBOOT, RebootHandler},
+              {FB_CMD_REBOOT_BOOTLOADER, RebootBootloaderHandler},
+              {FB_CMD_REBOOT_FASTBOOT, RebootFastbootHandler},
+              {FB_CMD_REBOOT_RECOVERY, RebootRecoveryHandler},
+              {FB_CMD_ERASE, EraseHandler},
+              {FB_CMD_FLASH, FlashHandler},
+              {FB_CMD_CREATE_PARTITION, CreatePartitionHandler},
+              {FB_CMD_DELETE_PARTITION, DeletePartitionHandler},
+              {FB_CMD_RESIZE_PARTITION, ResizePartitionHandler},
+              {FB_CMD_UPDATE_SUPER, UpdateSuperHandler},
+              {FB_CMD_OEM, OemCmdHandler},
+              {FB_CMD_GSI, GsiHandler},
+      }),
+      transport_(std::make_unique<ClientUsbTransport>()),
+      boot_control_hal_(IBootControl::getService()),
+      health_hal_(get_health_service()),
+      fastboot_hal_(IFastboot::getService()),
+      active_slot_("") {}
+
+FastbootDevice::~FastbootDevice() {
+    CloseDevice();
+}
+
+void FastbootDevice::CloseDevice() {
+    transport_->Close();
+}
+
+std::string FastbootDevice::GetCurrentSlot() {
+    // Check if a set_active ccommand was issued earlier since the boot control HAL
+    // returns the slot that is currently booted into.
+    if (!active_slot_.empty()) {
+        return active_slot_;
+    }
+    // Non-A/B devices must not have boot control HALs.
+    if (!boot_control_hal_) {
+        return "";
+    }
+    std::string suffix;
+    auto cb = [&suffix](hidl_string s) { suffix = s; };
+    boot_control_hal_->getSuffix(boot_control_hal_->getCurrentSlot(), cb);
+    return suffix;
+}
+
+bool FastbootDevice::WriteStatus(FastbootResult result, const std::string& message) {
+    constexpr size_t kResponseReasonSize = 4;
+    constexpr size_t kNumResponseTypes = 4;  // "FAIL", "OKAY", "INFO", "DATA"
+
+    char buf[FB_RESPONSE_SZ];
+    constexpr size_t kMaxMessageSize = sizeof(buf) - kResponseReasonSize;
+    size_t msg_len = std::min(kMaxMessageSize, message.size());
+
+    constexpr const char* kResultStrings[kNumResponseTypes] = {RESPONSE_OKAY, RESPONSE_FAIL,
+                                                               RESPONSE_INFO, RESPONSE_DATA};
+
+    if (static_cast<size_t>(result) >= kNumResponseTypes) {
+        return false;
+    }
+
+    memcpy(buf, kResultStrings[static_cast<size_t>(result)], kResponseReasonSize);
+    memcpy(buf + kResponseReasonSize, message.c_str(), msg_len);
+
+    size_t response_len = kResponseReasonSize + msg_len;
+    auto write_ret = this->get_transport()->Write(buf, response_len);
+    if (write_ret != static_cast<ssize_t>(response_len)) {
+        PLOG(ERROR) << "Failed to write " << message;
+        return false;
+    }
+
+    return true;
+}
+
+bool FastbootDevice::HandleData(bool read, std::vector<char>* data) {
+    auto read_write_data_size = read ? this->get_transport()->Read(data->data(), data->size())
+                                     : this->get_transport()->Write(data->data(), data->size());
+    if (read_write_data_size == -1 || static_cast<size_t>(read_write_data_size) != data->size()) {
+        return false;
+    }
+    return true;
+}
+
+void FastbootDevice::ExecuteCommands() {
+    char command[FB_RESPONSE_SZ + 1];
+    for (;;) {
+        auto bytes_read = transport_->Read(command, FB_RESPONSE_SZ);
+        if (bytes_read == -1) {
+            PLOG(ERROR) << "Couldn't read command";
+            return;
+        }
+        command[bytes_read] = '\0';
+
+        LOG(INFO) << "Fastboot command: " << command;
+
+        std::vector<std::string> args;
+        std::string cmd_name;
+        if (android::base::StartsWith(command, "oem ")) {
+            args = {command};
+            cmd_name = FB_CMD_OEM;
+        } else {
+            args = android::base::Split(command, ":");
+            cmd_name = args[0];
+        }
+
+        auto found_command = kCommandMap.find(cmd_name);
+        if (found_command == kCommandMap.end()) {
+            WriteStatus(FastbootResult::FAIL, "Unrecognized command " + args[0]);
+            continue;
+        }
+        if (!found_command->second(this, args)) {
+            return;
+        }
+    }
+}
+
+bool FastbootDevice::WriteOkay(const std::string& message) {
+    return WriteStatus(FastbootResult::OKAY, message);
+}
+
+bool FastbootDevice::WriteFail(const std::string& message) {
+    return WriteStatus(FastbootResult::FAIL, message);
+}
+
+bool FastbootDevice::WriteInfo(const std::string& message) {
+    return WriteStatus(FastbootResult::INFO, message);
+}
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
new file mode 100644
index 0000000..091aadf
--- /dev/null
+++ b/fastboot/device/fastboot_device.h
@@ -0,0 +1,70 @@
+/*
+ * 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 <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <android/hardware/boot/1.0/IBootControl.h>
+#include <android/hardware/fastboot/1.0/IFastboot.h>
+#include <android/hardware/health/2.0/IHealth.h>
+
+#include "commands.h"
+#include "transport.h"
+#include "variables.h"
+
+class FastbootDevice {
+  public:
+    FastbootDevice();
+    ~FastbootDevice();
+
+    void CloseDevice();
+    void ExecuteCommands();
+    bool WriteStatus(FastbootResult result, const std::string& message);
+    bool HandleData(bool read, std::vector<char>* data);
+    std::string GetCurrentSlot();
+
+    // Shortcuts for writing status results.
+    bool WriteOkay(const std::string& message);
+    bool WriteFail(const std::string& message);
+    bool WriteInfo(const std::string& message);
+
+    std::vector<char>& download_data() { return download_data_; }
+    Transport* get_transport() { return transport_.get(); }
+    android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal() {
+        return boot_control_hal_;
+    }
+    android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal() {
+        return fastboot_hal_;
+    }
+    android::sp<android::hardware::health::V2_0::IHealth> health_hal() { return health_hal_; }
+
+    void set_active_slot(const std::string& active_slot) { active_slot_ = active_slot; }
+
+  private:
+    const std::unordered_map<std::string, CommandHandler> kCommandMap;
+
+    std::unique_ptr<Transport> transport_;
+    android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal_;
+    android::sp<android::hardware::health::V2_0::IHealth> health_hal_;
+    android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal_;
+    std::vector<char> download_data_;
+    std::string active_slot_;
+};
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
new file mode 100644
index 0000000..99854c9
--- /dev/null
+++ b/fastboot/device/flashing.cpp
@@ -0,0 +1,199 @@
+/*
+ * 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 "flashing.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <set>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <ext4_utils/ext4_utils.h>
+#include <fs_mgr_overlayfs.h>
+#include <fstab/fstab.h>
+#include <liblp/builder.h>
+#include <liblp/liblp.h>
+#include <sparse/sparse.h>
+
+#include "fastboot_device.h"
+#include "utility.h"
+
+using namespace android::fs_mgr;
+using namespace std::literals;
+
+namespace {
+
+constexpr uint32_t SPARSE_HEADER_MAGIC = 0xed26ff3a;
+
+void WipeOverlayfsForPartition(FastbootDevice* device, const std::string& partition_name) {
+    // May be called, in the case of sparse data, multiple times so cache/skip.
+    static std::set<std::string> wiped;
+    if (wiped.find(partition_name) != wiped.end()) return;
+    wiped.insert(partition_name);
+    // Following appears to have a first time 2% impact on flashing speeds.
+
+    // Convert partition_name to a validated mount point and wipe.
+    Fstab fstab;
+    ReadDefaultFstab(&fstab);
+
+    for (const auto& entry : fstab) {
+        auto partition = android::base::Basename(entry.mount_point);
+        if ("/" == entry.mount_point) {
+            partition = "system";
+        }
+
+        if ((partition + device->GetCurrentSlot()) == partition_name) {
+            fs_mgr_overlayfs_teardown(entry.mount_point.c_str());
+        }
+    }
+}
+
+}  // namespace
+
+int FlashRawDataChunk(int fd, const char* data, size_t len) {
+    size_t ret = 0;
+    while (ret < len) {
+        int this_len = std::min(static_cast<size_t>(1048576UL * 8), len - ret);
+        int this_ret = write(fd, data, this_len);
+        if (this_ret < 0) {
+            PLOG(ERROR) << "Failed to flash data of len " << len;
+            return -1;
+        }
+        data += this_ret;
+        ret += this_ret;
+    }
+    return 0;
+}
+
+int FlashRawData(int fd, const std::vector<char>& downloaded_data) {
+    int ret = FlashRawDataChunk(fd, downloaded_data.data(), downloaded_data.size());
+    if (ret < 0) {
+        return -errno;
+    }
+    return ret;
+}
+
+int WriteCallback(void* priv, const void* data, size_t len) {
+    int fd = reinterpret_cast<long long>(priv);
+    if (!data) {
+        return lseek64(fd, len, SEEK_CUR) >= 0 ? 0 : -errno;
+    }
+    return FlashRawDataChunk(fd, reinterpret_cast<const char*>(data), len);
+}
+
+int FlashSparseData(int fd, std::vector<char>& downloaded_data) {
+    struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(), true, false);
+    if (!file) {
+        return -ENOENT;
+    }
+    return sparse_file_callback(file, false, false, WriteCallback, reinterpret_cast<void*>(fd));
+}
+
+int FlashBlockDevice(int fd, std::vector<char>& downloaded_data) {
+    lseek64(fd, 0, SEEK_SET);
+    if (downloaded_data.size() >= sizeof(SPARSE_HEADER_MAGIC) &&
+        *reinterpret_cast<uint32_t*>(downloaded_data.data()) == SPARSE_HEADER_MAGIC) {
+        return FlashSparseData(fd, downloaded_data);
+    } else {
+        return FlashRawData(fd, downloaded_data);
+    }
+}
+
+int Flash(FastbootDevice* device, const std::string& partition_name) {
+    PartitionHandle handle;
+    if (!OpenPartition(device, partition_name, &handle)) {
+        return -ENOENT;
+    }
+
+    std::vector<char> data = std::move(device->download_data());
+    if (data.size() == 0) {
+        return -EINVAL;
+    } else if (data.size() > get_block_device_size(handle.fd())) {
+        return -EOVERFLOW;
+    }
+    WipeOverlayfsForPartition(device, partition_name);
+    return FlashBlockDevice(handle.fd(), data);
+}
+
+bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wipe) {
+    std::vector<char> data = std::move(device->download_data());
+    if (data.empty()) {
+        return device->WriteFail("No data available");
+    }
+
+    std::unique_ptr<LpMetadata> new_metadata = ReadFromImageBlob(data.data(), data.size());
+    if (!new_metadata) {
+        return device->WriteFail("Data is not a valid logical partition metadata image");
+    }
+
+    if (!FindPhysicalPartition(super_name)) {
+        return device->WriteFail("Cannot find " + super_name +
+                                 ", build may be missing broken or missing boot_devices");
+    }
+
+    // If we are unable to read the existing metadata, then the super partition
+    // is corrupt. In this case we reflash the whole thing using the provided
+    // image.
+    std::string slot_suffix = device->GetCurrentSlot();
+    uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
+    std::unique_ptr<LpMetadata> old_metadata = ReadMetadata(super_name, slot_number);
+    if (wipe || !old_metadata) {
+        if (!FlashPartitionTable(super_name, *new_metadata.get())) {
+            return device->WriteFail("Unable to flash new partition table");
+        }
+        fs_mgr_overlayfs_teardown();
+        return device->WriteOkay("Successfully flashed partition table");
+    }
+
+    std::set<std::string> partitions_to_keep;
+    for (const auto& partition : old_metadata->partitions) {
+        // Preserve partitions in the other slot, but not the current slot.
+        std::string partition_name = GetPartitionName(partition);
+        if (!slot_suffix.empty() && GetPartitionSlotSuffix(partition_name) == slot_suffix) {
+            continue;
+        }
+        partitions_to_keep.emplace(partition_name);
+    }
+
+    // Do not preserve the scratch partition.
+    partitions_to_keep.erase("scratch");
+
+    if (!partitions_to_keep.empty()) {
+        std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(*new_metadata.get());
+        if (!builder->ImportPartitions(*old_metadata.get(), partitions_to_keep)) {
+            return device->WriteFail(
+                    "Old partitions are not compatible with the new super layout; wipe needed");
+        }
+
+        new_metadata = builder->Export();
+        if (!new_metadata) {
+            return device->WriteFail("Unable to build new partition table; wipe needed");
+        }
+    }
+
+    // Write the new table to every metadata slot.
+    if (!UpdateAllPartitionMetadata(device, super_name, *new_metadata.get())) {
+        return device->WriteFail("Unable to write new partition table");
+    }
+    fs_mgr_overlayfs_teardown();
+    return device->WriteOkay("Successfully updated partition table");
+}
diff --git a/fastboot/device/flashing.h b/fastboot/device/flashing.h
new file mode 100644
index 0000000..b15f28b
--- /dev/null
+++ b/fastboot/device/flashing.h
@@ -0,0 +1,25 @@
+/*
+ * 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>
+
+class FastbootDevice;
+
+int Flash(FastbootDevice* device, const std::string& partition_name);
+bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wipe);
diff --git a/fastboot/device/main.cpp b/fastboot/device/main.cpp
new file mode 100644
index 0000000..df9c900
--- /dev/null
+++ b/fastboot/device/main.cpp
@@ -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 <android-base/logging.h>
+
+#include "fastboot_device.h"
+
+int main(int /*argc*/, char* argv[]) {
+    android::base::InitLogging(argv, &android::base::KernelLogger);
+
+    while (true) {
+        FastbootDevice device;
+        device.ExecuteCommands();
+    }
+}
diff --git a/fastboot/device/usb_client.cpp b/fastboot/device/usb_client.cpp
new file mode 100644
index 0000000..5066046
--- /dev/null
+++ b/fastboot/device/usb_client.cpp
@@ -0,0 +1,299 @@
+/*
+ * 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 "usb_client.h"
+
+#include <endian.h>
+#include <fcntl.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/functionfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+constexpr int kMaxPacketSizeFs = 64;
+constexpr int kMaxPacketSizeHs = 512;
+constexpr int kMaxPacketsizeSs = 1024;
+
+constexpr size_t kFbFfsNumBufs = 16;
+constexpr size_t kFbFfsBufSize = 16384;
+
+constexpr const char* kUsbFfsFastbootEp0 = "/dev/usb-ffs/fastboot/ep0";
+constexpr const char* kUsbFfsFastbootOut = "/dev/usb-ffs/fastboot/ep1";
+constexpr const char* kUsbFfsFastbootIn = "/dev/usb-ffs/fastboot/ep2";
+
+struct FuncDesc {
+    struct usb_interface_descriptor intf;
+    struct usb_endpoint_descriptor_no_audio source;
+    struct usb_endpoint_descriptor_no_audio sink;
+} __attribute__((packed));
+
+struct SsFuncDesc {
+    struct usb_interface_descriptor intf;
+    struct usb_endpoint_descriptor_no_audio source;
+    struct usb_ss_ep_comp_descriptor source_comp;
+    struct usb_endpoint_descriptor_no_audio sink;
+    struct usb_ss_ep_comp_descriptor sink_comp;
+} __attribute__((packed));
+
+struct DescV2 {
+    struct usb_functionfs_descs_head_v2 header;
+    // The rest of the structure depends on the flags in the header.
+    __le32 fs_count;
+    __le32 hs_count;
+    __le32 ss_count;
+    struct FuncDesc fs_descs, hs_descs;
+    struct SsFuncDesc ss_descs;
+} __attribute__((packed));
+
+struct usb_interface_descriptor fastboot_interface = {
+        .bLength = USB_DT_INTERFACE_SIZE,
+        .bDescriptorType = USB_DT_INTERFACE,
+        .bInterfaceNumber = 0,
+        .bNumEndpoints = 2,
+        .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+        .bInterfaceSubClass = 66,
+        .bInterfaceProtocol = 3,
+        .iInterface = 1, /* first string from the provided table */
+};
+
+static struct FuncDesc fs_descriptors = {
+        .intf = fastboot_interface,
+        .source =
+                {
+                        .bLength = sizeof(fs_descriptors.source),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_OUT,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketSizeFs,
+                },
+        .sink =
+                {
+                        .bLength = sizeof(fs_descriptors.sink),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_IN,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketSizeFs,
+                },
+};
+
+static struct FuncDesc hs_descriptors = {
+        .intf = fastboot_interface,
+        .source =
+                {
+                        .bLength = sizeof(hs_descriptors.source),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_OUT,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketSizeHs,
+                },
+        .sink =
+                {
+                        .bLength = sizeof(hs_descriptors.sink),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_IN,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketSizeHs,
+                },
+};
+
+static struct SsFuncDesc ss_descriptors = {
+        .intf = fastboot_interface,
+        .source =
+                {
+                        .bLength = sizeof(ss_descriptors.source),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_OUT,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketsizeSs,
+                },
+        .source_comp =
+                {
+                        .bLength = sizeof(ss_descriptors.source_comp),
+                        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+                        .bMaxBurst = 15,
+                },
+        .sink =
+                {
+                        .bLength = sizeof(ss_descriptors.sink),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_IN,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketsizeSs,
+                },
+        .sink_comp =
+                {
+                        .bLength = sizeof(ss_descriptors.sink_comp),
+                        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+                        .bMaxBurst = 15,
+                },
+};
+
+#define STR_INTERFACE_ "fastboot"
+
+static const struct {
+    struct usb_functionfs_strings_head header;
+    struct {
+        __le16 code;
+        const char str1[sizeof(STR_INTERFACE_)];
+    } __attribute__((packed)) lang0;
+} __attribute__((packed)) strings = {
+        .header =
+                {
+                        .magic = htole32(FUNCTIONFS_STRINGS_MAGIC),
+                        .length = htole32(sizeof(strings)),
+                        .str_count = htole32(1),
+                        .lang_count = htole32(1),
+                },
+        .lang0 =
+                {
+                        htole16(0x0409), /* en-us */
+                        STR_INTERFACE_,
+                },
+};
+
+static struct DescV2 v2_descriptor = {
+        .header =
+                {
+                        .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
+                        .length = htole32(sizeof(v2_descriptor)),
+                        .flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
+                                 FUNCTIONFS_HAS_SS_DESC,
+                },
+        .fs_count = 3,
+        .hs_count = 3,
+        .ss_count = 5,
+        .fs_descs = fs_descriptors,
+        .hs_descs = hs_descriptors,
+        .ss_descs = ss_descriptors,
+};
+
+// Reimplementing since usb_ffs_close() does not close the control FD.
+static void CloseFunctionFs(usb_handle* h) {
+    h->bulk_in.reset();
+    h->bulk_out.reset();
+    h->control.reset();
+}
+
+static bool InitFunctionFs(usb_handle* h) {
+    LOG(INFO) << "initializing functionfs";
+
+    if (h->control < 0) {  // might have already done this before
+        LOG(INFO) << "opening control endpoint " << kUsbFfsFastbootEp0;
+        h->control.reset(open(kUsbFfsFastbootEp0, O_RDWR));
+        if (h->control < 0) {
+            PLOG(ERROR) << "cannot open control endpoint " << kUsbFfsFastbootEp0;
+            goto err;
+        }
+
+        auto ret = write(h->control.get(), &v2_descriptor, sizeof(v2_descriptor));
+        if (ret < 0) {
+            PLOG(ERROR) << "cannot write descriptors " << kUsbFfsFastbootEp0;
+            goto err;
+        }
+
+        ret = write(h->control.get(), &strings, sizeof(strings));
+        if (ret < 0) {
+            PLOG(ERROR) << "cannot write strings " << kUsbFfsFastbootEp0;
+            goto err;
+        }
+        // Signal only when writing the descriptors to ffs
+        android::base::SetProperty("sys.usb.ffs.ready", "1");
+    }
+
+    h->bulk_out.reset(open(kUsbFfsFastbootOut, O_RDONLY));
+    if (h->bulk_out < 0) {
+        PLOG(ERROR) << "cannot open bulk-out endpoint " << kUsbFfsFastbootOut;
+        goto err;
+    }
+
+    h->bulk_in.reset(open(kUsbFfsFastbootIn, O_WRONLY));
+    if (h->bulk_in < 0) {
+        PLOG(ERROR) << "cannot open bulk-in endpoint " << kUsbFfsFastbootIn;
+        goto err;
+    }
+
+    h->read_aiob.fd = h->bulk_out.get();
+    h->write_aiob.fd = h->bulk_in.get();
+    h->reads_zero_packets = false;
+    return true;
+
+err:
+    CloseFunctionFs(h);
+    return false;
+}
+
+ClientUsbTransport::ClientUsbTransport()
+    : handle_(std::unique_ptr<usb_handle>(create_usb_handle(kFbFfsNumBufs, kFbFfsBufSize))) {
+    if (!InitFunctionFs(handle_.get())) {
+        handle_.reset(nullptr);
+    }
+}
+
+ssize_t ClientUsbTransport::Read(void* data, size_t len) {
+    if (handle_ == nullptr || len > SSIZE_MAX) {
+        return -1;
+    }
+    char* char_data = static_cast<char*>(data);
+    size_t bytes_read_total = 0;
+    while (bytes_read_total < len) {
+        auto bytes_to_read = std::min(len - bytes_read_total, kFbFfsNumBufs * kFbFfsBufSize);
+        auto bytes_read_now =
+                handle_->read(handle_.get(), char_data, bytes_to_read, true /* allow_partial */);
+        if (bytes_read_now < 0) {
+            return bytes_read_total == 0 ? -1 : bytes_read_total;
+        }
+        bytes_read_total += bytes_read_now;
+        char_data += bytes_read_now;
+        if (static_cast<size_t>(bytes_read_now) < bytes_to_read) {
+            break;
+        }
+    }
+    return bytes_read_total;
+}
+
+ssize_t ClientUsbTransport::Write(const void* data, size_t len) {
+    if (handle_ == nullptr || len > SSIZE_MAX) {
+        return -1;
+    }
+    const char* char_data = reinterpret_cast<const char*>(data);
+    size_t bytes_written_total = 0;
+    while (bytes_written_total < len) {
+        auto bytes_to_write = std::min(len - bytes_written_total, kFbFfsNumBufs * kFbFfsBufSize);
+        auto bytes_written_now = handle_->write(handle_.get(), data, bytes_to_write);
+        if (bytes_written_now < 0) {
+            return bytes_written_total == 0 ? -1 : bytes_written_total;
+        }
+        bytes_written_total += bytes_written_now;
+        char_data += bytes_written_now;
+        if (static_cast<size_t>(bytes_written_now) < bytes_to_write) {
+            break;
+        }
+    }
+    return bytes_written_total;
+}
+
+int ClientUsbTransport::Close() {
+    if (handle_ == nullptr) {
+        return -1;
+    }
+    CloseFunctionFs(handle_.get());
+    return 0;
+}
diff --git a/fastboot/device/usb_client.h b/fastboot/device/usb_client.h
new file mode 100644
index 0000000..3694f9a
--- /dev/null
+++ b/fastboot/device/usb_client.h
@@ -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.
+ */
+#pragma once
+
+#include <memory>
+
+#include <adbd/usb.h>
+
+#include "transport.h"
+
+class ClientUsbTransport : public Transport {
+  public:
+    ClientUsbTransport();
+    ~ClientUsbTransport() override = default;
+
+    ssize_t Read(void* data, size_t len) override;
+    ssize_t Write(const void* data, size_t len) override;
+    int Close() override;
+
+  private:
+    std::unique_ptr<usb_handle> handle_;
+
+    DISALLOW_COPY_AND_ASSIGN(ClientUsbTransport);
+};
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
new file mode 100644
index 0000000..2ebd57d
--- /dev/null
+++ b/fastboot/device/utility.cpp
@@ -0,0 +1,234 @@
+/*
+ * 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 "utility.h"
+
+#include <dirent.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/properties.h>
+#include <android-base/strings.h>
+#include <fs_mgr.h>
+#include <fs_mgr_dm_linear.h>
+#include <liblp/builder.h>
+#include <liblp/liblp.h>
+
+#include "fastboot_device.h"
+
+using namespace android::fs_mgr;
+using namespace std::chrono_literals;
+using android::base::unique_fd;
+using android::hardware::boot::V1_0::Slot;
+
+namespace {
+
+bool OpenPhysicalPartition(const std::string& name, PartitionHandle* handle) {
+    std::optional<std::string> path = FindPhysicalPartition(name);
+    if (!path) {
+        return false;
+    }
+    *handle = PartitionHandle(*path);
+    return true;
+}
+
+bool OpenLogicalPartition(FastbootDevice* device, const std::string& partition_name,
+                          PartitionHandle* handle) {
+    std::string slot_suffix = GetSuperSlotSuffix(device, partition_name);
+    uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
+    auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number));
+    if (!path) {
+        return false;
+    }
+    std::string dm_path;
+    if (!CreateLogicalPartition(path->c_str(), slot_number, partition_name, true, 5s, &dm_path)) {
+        LOG(ERROR) << "Could not map partition: " << partition_name;
+        return false;
+    }
+    auto closer = [partition_name]() -> void { DestroyLogicalPartition(partition_name, 5s); };
+    *handle = PartitionHandle(dm_path, std::move(closer));
+    return true;
+}
+
+}  // namespace
+
+bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle) {
+    // We prioritize logical partitions over physical ones, and do this
+    // consistently for other partition operations (like getvar:partition-size).
+    if (LogicalPartitionExists(device, name)) {
+        if (!OpenLogicalPartition(device, name, handle)) {
+            return false;
+        }
+    } else if (!OpenPhysicalPartition(name, handle)) {
+        LOG(ERROR) << "No such partition: " << name;
+        return false;
+    }
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(handle->path().c_str(), O_WRONLY | O_EXCL)));
+    if (fd < 0) {
+        PLOG(ERROR) << "Failed to open block device: " << handle->path();
+        return false;
+    }
+    handle->set_fd(std::move(fd));
+    return true;
+}
+
+std::optional<std::string> FindPhysicalPartition(const std::string& name) {
+    // Check for an invalid file name
+    if (android::base::StartsWith(name, "../") || name.find("/../") != std::string::npos) {
+        return {};
+    }
+    std::string path = "/dev/block/by-name/" + name;
+    if (access(path.c_str(), W_OK) < 0) {
+        return {};
+    }
+    return path;
+}
+
+static const LpMetadataPartition* FindLogicalPartition(const LpMetadata& metadata,
+                                                       const std::string& name) {
+    for (const auto& partition : metadata.partitions) {
+        if (GetPartitionName(partition) == name) {
+            return &partition;
+        }
+    }
+    return nullptr;
+}
+
+bool LogicalPartitionExists(FastbootDevice* device, const std::string& name, bool* is_zero_length) {
+    std::string slot_suffix = GetSuperSlotSuffix(device, name);
+    uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
+    auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number));
+    if (!path) {
+        return false;
+    }
+
+    std::unique_ptr<LpMetadata> metadata = ReadMetadata(path->c_str(), slot_number);
+    if (!metadata) {
+        return false;
+    }
+    const LpMetadataPartition* partition = FindLogicalPartition(*metadata.get(), name);
+    if (!partition) {
+        return false;
+    }
+    if (is_zero_length) {
+        *is_zero_length = (partition->num_extents == 0);
+    }
+    return true;
+}
+
+bool GetSlotNumber(const std::string& slot, Slot* number) {
+    if (slot.size() != 1) {
+        return false;
+    }
+    if (slot[0] < 'a' || slot[0] > 'z') {
+        return false;
+    }
+    *number = slot[0] - 'a';
+    return true;
+}
+
+std::vector<std::string> ListPartitions(FastbootDevice* device) {
+    std::vector<std::string> partitions;
+
+    // First get physical partitions.
+    struct dirent* de;
+    std::unique_ptr<DIR, decltype(&closedir)> by_name(opendir("/dev/block/by-name"), closedir);
+    while ((de = readdir(by_name.get())) != nullptr) {
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
+            continue;
+        }
+        struct stat s;
+        std::string path = "/dev/block/by-name/" + std::string(de->d_name);
+        if (!stat(path.c_str(), &s) && S_ISBLK(s.st_mode)) {
+            partitions.emplace_back(de->d_name);
+        }
+    }
+
+    // Find metadata in each super partition (on retrofit devices, there will
+    // be two).
+    std::vector<std::unique_ptr<LpMetadata>> metadata_list;
+
+    uint32_t current_slot = SlotNumberForSlotSuffix(device->GetCurrentSlot());
+    std::string super_name = fs_mgr_get_super_partition_name(current_slot);
+    if (auto metadata = ReadMetadata(super_name, current_slot)) {
+        metadata_list.emplace_back(std::move(metadata));
+    }
+
+    uint32_t other_slot = (current_slot == 0) ? 1 : 0;
+    std::string other_super = fs_mgr_get_super_partition_name(other_slot);
+    if (super_name != other_super) {
+        if (auto metadata = ReadMetadata(other_super, other_slot)) {
+            metadata_list.emplace_back(std::move(metadata));
+        }
+    }
+
+    for (const auto& metadata : metadata_list) {
+        for (const auto& partition : metadata->partitions) {
+            std::string partition_name = GetPartitionName(partition);
+            if (std::find(partitions.begin(), partitions.end(), partition_name) ==
+                partitions.end()) {
+                partitions.emplace_back(partition_name);
+            }
+        }
+    }
+    return partitions;
+}
+
+bool GetDeviceLockStatus() {
+    std::string cmdline;
+    // Return lock status true if unable to read kernel command line.
+    if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
+        return true;
+    }
+    return cmdline.find("androidboot.verifiedbootstate=orange") == std::string::npos;
+}
+
+bool UpdateAllPartitionMetadata(FastbootDevice* device, const std::string& super_name,
+                                const android::fs_mgr::LpMetadata& metadata) {
+    size_t num_slots = 1;
+    auto boot_control_hal = device->boot_control_hal();
+    if (boot_control_hal) {
+        num_slots = boot_control_hal->getNumberSlots();
+    }
+
+    bool ok = true;
+    for (size_t i = 0; i < num_slots; i++) {
+        ok &= UpdatePartitionTable(super_name, metadata, i);
+    }
+    return ok;
+}
+
+std::string GetSuperSlotSuffix(FastbootDevice* device, const std::string& partition_name) {
+    // If the super partition does not have a slot suffix, this is not a
+    // retrofit device, and we should take the current slot.
+    std::string current_slot_suffix = device->GetCurrentSlot();
+    uint32_t current_slot_number = SlotNumberForSlotSuffix(current_slot_suffix);
+    std::string super_partition = fs_mgr_get_super_partition_name(current_slot_number);
+    if (GetPartitionSlotSuffix(super_partition).empty()) {
+        return current_slot_suffix;
+    }
+
+    // Otherwise, infer the slot from the partition name.
+    std::string slot_suffix = GetPartitionSlotSuffix(partition_name);
+    if (!slot_suffix.empty()) {
+        return slot_suffix;
+    }
+    return current_slot_suffix;
+}
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
new file mode 100644
index 0000000..bfeeb74
--- /dev/null
+++ b/fastboot/device/utility.h
@@ -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.
+ */
+#pragma once
+
+#include <optional>
+#include <string>
+
+#include <android-base/unique_fd.h>
+#include <android/hardware/boot/1.0/IBootControl.h>
+#include <liblp/liblp.h>
+
+// Logical partitions are only mapped to a block device as needed, and
+// immediately unmapped when no longer needed. In order to enforce this we
+// require accessing partitions through a Handle abstraction, which may perform
+// additional operations after closing its file descriptor.
+class PartitionHandle {
+  public:
+    PartitionHandle() {}
+    explicit PartitionHandle(const std::string& path) : path_(path) {}
+    PartitionHandle(const std::string& path, std::function<void()>&& closer)
+        : path_(path), closer_(std::move(closer)) {}
+    PartitionHandle(PartitionHandle&& other) = default;
+    PartitionHandle& operator=(PartitionHandle&& other) = default;
+    ~PartitionHandle() {
+        if (closer_) {
+            // Make sure the device is closed first.
+            fd_ = {};
+            closer_();
+        }
+    }
+    const std::string& path() const { return path_; }
+    int fd() const { return fd_.get(); }
+    void set_fd(android::base::unique_fd&& fd) { fd_ = std::move(fd); }
+
+  private:
+    std::string path_;
+    android::base::unique_fd fd_;
+    std::function<void()> closer_;
+};
+
+class FastbootDevice;
+
+// On normal devices, the super partition is always named "super". On retrofit
+// devices, the name must be derived from the partition name or current slot.
+// This helper assists in choosing the correct super for a given partition
+// name.
+std::string GetSuperSlotSuffix(FastbootDevice* device, const std::string& partition_name);
+
+std::optional<std::string> FindPhysicalPartition(const std::string& name);
+bool LogicalPartitionExists(FastbootDevice* device, const std::string& name,
+                            bool* is_zero_length = nullptr);
+bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle);
+bool GetSlotNumber(const std::string& slot, android::hardware::boot::V1_0::Slot* number);
+std::vector<std::string> ListPartitions(FastbootDevice* device);
+bool GetDeviceLockStatus();
+
+// Update all copies of metadata.
+bool UpdateAllPartitionMetadata(FastbootDevice* device, const std::string& super_name,
+                                const android::fs_mgr::LpMetadata& metadata);
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
new file mode 100644
index 0000000..130a3cf
--- /dev/null
+++ b/fastboot/device/variables.cpp
@@ -0,0 +1,426 @@
+/*
+ * 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 "variables.h"
+
+#include <inttypes.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 <ext4_utils/ext4_utils.h>
+#include <fs_mgr.h>
+#include <healthhalutils/HealthHalUtils.h>
+#include <liblp/liblp.h>
+
+#include "fastboot_device.h"
+#include "flashing.h"
+#include "utility.h"
+
+using ::android::hardware::boot::V1_0::BoolResult;
+using ::android::hardware::boot::V1_0::Slot;
+using ::android::hardware::fastboot::V1_0::FileSystemType;
+using ::android::hardware::fastboot::V1_0::Result;
+using ::android::hardware::fastboot::V1_0::Status;
+using namespace android::fs_mgr;
+
+constexpr char kFastbootProtocolVersion[] = "0.4";
+
+bool GetVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                std::string* message) {
+    *message = kFastbootProtocolVersion;
+    return true;
+}
+
+bool GetBootloaderVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                          std::string* message) {
+    *message = android::base::GetProperty("ro.bootloader", "");
+    return true;
+}
+
+bool GetBasebandVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                        std::string* message) {
+    *message = android::base::GetProperty("ro.build.expect.baseband", "");
+    return true;
+}
+
+bool GetProduct(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                std::string* message) {
+    *message = android::base::GetProperty("ro.product.device", "");
+    return true;
+}
+
+bool GetSerial(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+               std::string* message) {
+    *message = android::base::GetProperty("ro.serialno", "");
+    return true;
+}
+
+bool GetSecure(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+               std::string* message) {
+    *message = android::base::GetBoolProperty("ro.secure", "") ? "yes" : "no";
+    return true;
+}
+
+bool GetVariant(FastbootDevice* device, const std::vector<std::string>& /* args */,
+                std::string* message) {
+    auto fastboot_hal = device->fastboot_hal();
+    if (!fastboot_hal) {
+        *message = "Fastboot HAL not found";
+        return false;
+    }
+
+    Result ret;
+    auto ret_val = fastboot_hal->getVariant([&](std::string device_variant, Result result) {
+        *message = device_variant;
+        ret = result;
+    });
+    if (!ret_val.isOk() || ret.status != Status::SUCCESS) {
+        *message = "Unable to get device variant";
+        return false;
+    }
+
+    return true;
+}
+
+bool GetBatteryVoltageHelper(FastbootDevice* device, int32_t* battery_voltage) {
+    using android::hardware::health::V2_0::HealthInfo;
+    using android::hardware::health::V2_0::Result;
+
+    auto health_hal = device->health_hal();
+    if (!health_hal) {
+        return false;
+    }
+
+    Result ret;
+    auto ret_val = health_hal->getHealthInfo([&](Result result, HealthInfo info) {
+        *battery_voltage = info.legacy.batteryVoltage;
+        ret = result;
+    });
+    if (!ret_val.isOk() || (ret != Result::SUCCESS)) {
+        return false;
+    }
+
+    return true;
+}
+
+bool GetBatterySoCOk(FastbootDevice* device, const std::vector<std::string>& /* args */,
+                     std::string* message) {
+    int32_t battery_voltage = 0;
+    if (!GetBatteryVoltageHelper(device, &battery_voltage)) {
+        *message = "Unable to read battery voltage";
+        return false;
+    }
+
+    auto fastboot_hal = device->fastboot_hal();
+    if (!fastboot_hal) {
+        *message = "Fastboot HAL not found";
+        return false;
+    }
+
+    Result ret;
+    auto ret_val = fastboot_hal->getBatteryVoltageFlashingThreshold(
+            [&](int32_t voltage_threshold, Result result) {
+                *message = battery_voltage >= voltage_threshold ? "yes" : "no";
+                ret = result;
+            });
+
+    if (!ret_val.isOk() || ret.status != Status::SUCCESS) {
+        *message = "Unable to get battery voltage flashing threshold";
+        return false;
+    }
+
+    return true;
+}
+
+bool GetOffModeChargeState(FastbootDevice* device, const std::vector<std::string>& /* args */,
+                           std::string* message) {
+    auto fastboot_hal = device->fastboot_hal();
+    if (!fastboot_hal) {
+        *message = "Fastboot HAL not found";
+        return false;
+    }
+
+    Result ret;
+    auto ret_val =
+            fastboot_hal->getOffModeChargeState([&](bool off_mode_charging_state, Result result) {
+                *message = off_mode_charging_state ? "1" : "0";
+                ret = result;
+            });
+    if (!ret_val.isOk() || (ret.status != Status::SUCCESS)) {
+        *message = "Unable to get off mode charge state";
+        return false;
+    }
+
+    return true;
+}
+
+bool GetBatteryVoltage(FastbootDevice* device, const std::vector<std::string>& /* args */,
+                       std::string* message) {
+    int32_t battery_voltage = 0;
+    if (GetBatteryVoltageHelper(device, &battery_voltage)) {
+        *message = std::to_string(battery_voltage);
+        return true;
+    }
+    *message = "Unable to get battery voltage";
+    return false;
+}
+
+bool GetCurrentSlot(FastbootDevice* device, const std::vector<std::string>& /* args */,
+                    std::string* message) {
+    std::string suffix = device->GetCurrentSlot();
+    *message = suffix.size() == 2 ? suffix.substr(1) : suffix;
+    return true;
+}
+
+bool GetSlotCount(FastbootDevice* device, const std::vector<std::string>& /* args */,
+                  std::string* message) {
+    auto boot_control_hal = device->boot_control_hal();
+    if (!boot_control_hal) {
+        *message = "0";
+    } else {
+        *message = std::to_string(boot_control_hal->getNumberSlots());
+    }
+    return true;
+}
+
+bool GetSlotSuccessful(FastbootDevice* device, const std::vector<std::string>& args,
+                       std::string* message) {
+    if (args.empty()) {
+        *message = "Missing argument";
+        return false;
+    }
+    Slot slot;
+    if (!GetSlotNumber(args[0], &slot)) {
+        *message = "Invalid slot";
+        return false;
+    }
+    auto boot_control_hal = device->boot_control_hal();
+    if (!boot_control_hal) {
+        *message = "Device has no slots";
+        return false;
+    }
+    if (boot_control_hal->isSlotMarkedSuccessful(slot) != BoolResult::TRUE) {
+        *message = "no";
+    } else {
+        *message = "yes";
+    }
+    return true;
+}
+
+bool GetSlotUnbootable(FastbootDevice* device, const std::vector<std::string>& args,
+                       std::string* message) {
+    if (args.empty()) {
+        *message = "Missing argument";
+        return false;
+    }
+    Slot slot;
+    if (!GetSlotNumber(args[0], &slot)) {
+        *message = "Invalid slot";
+        return false;
+    }
+    auto boot_control_hal = device->boot_control_hal();
+    if (!boot_control_hal) {
+        *message = "Device has no slots";
+        return false;
+    }
+    if (boot_control_hal->isSlotBootable(slot) != BoolResult::TRUE) {
+        *message = "yes";
+    } else {
+        *message = "no";
+    }
+    return true;
+}
+
+bool GetMaxDownloadSize(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                        std::string* message) {
+    *message = android::base::StringPrintf("0x%X", kMaxDownloadSizeDefault);
+    return true;
+}
+
+bool GetUnlocked(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                 std::string* message) {
+    *message = GetDeviceLockStatus() ? "no" : "yes";
+    return true;
+}
+
+bool GetHasSlot(FastbootDevice* device, const std::vector<std::string>& args,
+                std::string* message) {
+    if (args.empty()) {
+        *message = "Missing argument";
+        return false;
+    }
+    std::string slot_suffix = device->GetCurrentSlot();
+    if (slot_suffix.empty()) {
+        *message = "no";
+        return true;
+    }
+    std::string partition_name = args[0] + slot_suffix;
+    if (FindPhysicalPartition(partition_name) || LogicalPartitionExists(device, partition_name)) {
+        *message = "yes";
+    } else {
+        *message = "no";
+    }
+    return true;
+}
+
+bool GetPartitionSize(FastbootDevice* device, const std::vector<std::string>& args,
+                      std::string* message) {
+    if (args.size() < 1) {
+        *message = "Missing argument";
+        return false;
+    }
+    // Zero-length partitions cannot be created through device-mapper, so we
+    // special case them here.
+    bool is_zero_length;
+    if (LogicalPartitionExists(device, args[0], &is_zero_length) && is_zero_length) {
+        *message = "0x0";
+        return true;
+    }
+    // Otherwise, open the partition as normal.
+    PartitionHandle handle;
+    if (!OpenPartition(device, args[0], &handle)) {
+        *message = "Could not open partition";
+        return false;
+    }
+    uint64_t size = get_block_device_size(handle.fd());
+    *message = android::base::StringPrintf("0x%" PRIX64, size);
+    return true;
+}
+
+bool GetPartitionType(FastbootDevice* device, const std::vector<std::string>& args,
+                      std::string* message) {
+    if (args.size() < 1) {
+        *message = "Missing argument";
+        return false;
+    }
+
+    std::string partition_name = args[0];
+    if (!FindPhysicalPartition(partition_name) && !LogicalPartitionExists(device, partition_name)) {
+        *message = "Invalid partition";
+        return false;
+    }
+
+    auto fastboot_hal = device->fastboot_hal();
+    if (!fastboot_hal) {
+        *message = "Fastboot HAL not found";
+        return false;
+    }
+
+    FileSystemType type;
+    Result ret;
+    auto ret_val =
+            fastboot_hal->getPartitionType(args[0], [&](FileSystemType fs_type, Result result) {
+                type = fs_type;
+                ret = result;
+            });
+    if (!ret_val.isOk() || (ret.status != Status::SUCCESS)) {
+        *message = "Unable to retrieve partition type";
+    } else {
+        switch (type) {
+            case FileSystemType::RAW:
+                *message = "raw";
+                return true;
+            case FileSystemType::EXT4:
+                *message = "ext4";
+                return true;
+            case FileSystemType::F2FS:
+                *message = "f2fs";
+                return true;
+            default:
+                *message = "Unknown file system type";
+        }
+    }
+
+    return false;
+}
+
+bool GetPartitionIsLogical(FastbootDevice* device, const std::vector<std::string>& args,
+                           std::string* message) {
+    if (args.size() < 1) {
+        *message = "Missing argument";
+        return false;
+    }
+    // Note: if a partition name is in both the GPT and the super partition, we
+    // return "true", to be consistent with prefering to flash logical partitions
+    // over physical ones.
+    std::string partition_name = args[0];
+    if (LogicalPartitionExists(device, partition_name)) {
+        *message = "yes";
+        return true;
+    }
+    if (FindPhysicalPartition(partition_name)) {
+        *message = "no";
+        return true;
+    }
+    *message = "Partition not found";
+    return false;
+}
+
+bool GetIsUserspace(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                    std::string* message) {
+    *message = "yes";
+    return true;
+}
+
+std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device) {
+    std::vector<std::vector<std::string>> args;
+    auto partitions = ListPartitions(device);
+    for (const auto& partition : partitions) {
+        args.emplace_back(std::initializer_list<std::string>{partition});
+    }
+    return args;
+}
+
+std::vector<std::vector<std::string>> GetAllPartitionArgsNoSlot(FastbootDevice* device) {
+    auto partitions = ListPartitions(device);
+
+    std::string slot_suffix = device->GetCurrentSlot();
+    if (!slot_suffix.empty()) {
+        auto names = std::move(partitions);
+        for (const auto& name : names) {
+            std::string slotless_name = name;
+            if (android::base::EndsWith(name, "_a") || android::base::EndsWith(name, "_b")) {
+                slotless_name = name.substr(0, name.rfind("_"));
+            }
+            if (std::find(partitions.begin(), partitions.end(), slotless_name) ==
+                partitions.end()) {
+                partitions.emplace_back(slotless_name);
+            }
+        }
+    }
+
+    std::vector<std::vector<std::string>> args;
+    for (const auto& partition : partitions) {
+        args.emplace_back(std::initializer_list<std::string>{partition});
+    }
+    return args;
+}
+
+bool GetHardwareRevision(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                         std::string* message) {
+    *message = android::base::GetProperty("ro.revision", "");
+    return true;
+}
+
+bool GetSuperPartitionName(FastbootDevice* device, const std::vector<std::string>& /* args */,
+                           std::string* message) {
+    uint32_t slot_number = SlotNumberForSlotSuffix(device->GetCurrentSlot());
+    *message = fs_mgr_get_super_partition_name(slot_number);
+    return true;
+}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
new file mode 100644
index 0000000..015a4c5
--- /dev/null
+++ b/fastboot/device/variables.h
@@ -0,0 +1,67 @@
+/*
+ * 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 <functional>
+#include <string>
+#include <vector>
+
+class FastbootDevice;
+
+bool GetVersion(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetBootloaderVersion(FastbootDevice* device, const std::vector<std::string>& args,
+                          std::string* message);
+bool GetBasebandVersion(FastbootDevice* device, const std::vector<std::string>& args,
+                        std::string* message);
+bool GetProduct(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetSerial(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetSecure(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetCurrentSlot(FastbootDevice* device, const std::vector<std::string>& args,
+                    std::string* message);
+bool GetSlotCount(FastbootDevice* device, const std::vector<std::string>& args,
+                  std::string* message);
+bool GetSlotSuccessful(FastbootDevice* device, const std::vector<std::string>& args,
+                       std::string* message);
+bool GetSlotUnbootable(FastbootDevice* device, const std::vector<std::string>& args,
+                       std::string* message);
+bool GetMaxDownloadSize(FastbootDevice* device, const std::vector<std::string>& args,
+                        std::string* message);
+bool GetUnlocked(FastbootDevice* device, const std::vector<std::string>& args,
+                 std::string* message);
+bool GetHasSlot(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetPartitionSize(FastbootDevice* device, const std::vector<std::string>& args,
+                      std::string* message);
+bool GetPartitionType(FastbootDevice* device, const std::vector<std::string>& args,
+                      std::string* message);
+bool GetPartitionIsLogical(FastbootDevice* device, const std::vector<std::string>& args,
+                           std::string* message);
+bool GetIsUserspace(FastbootDevice* device, const std::vector<std::string>& args,
+                    std::string* message);
+bool GetHardwareRevision(FastbootDevice* device, const std::vector<std::string>& args,
+                         std::string* message);
+bool GetVariant(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetOffModeChargeState(FastbootDevice* device, const std::vector<std::string>& args,
+                           std::string* message);
+bool GetBatteryVoltage(FastbootDevice* device, const std::vector<std::string>& args,
+                       std::string* message);
+bool GetBatterySoCOk(FastbootDevice* device, const std::vector<std::string>& args,
+                     std::string* message);
+bool GetSuperPartitionName(FastbootDevice* device, const std::vector<std::string>& args,
+                           std::string* message);
+
+// Helpers for getvar all.
+std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
+std::vector<std::vector<std::string>> GetAllPartitionArgsNoSlot(FastbootDevice* device);
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
deleted file mode 100644
index 60b7124..0000000
--- a/fastboot/engine.cpp
+++ /dev/null
@@ -1,351 +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 "fastboot.h"
-
-#include <errno.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <memory>
-#include <vector>
-
-#include <android-base/stringprintf.h>
-
-enum Op {
-    OP_DOWNLOAD,
-    OP_COMMAND,
-    OP_QUERY,
-    OP_NOTICE,
-    OP_DOWNLOAD_SPARSE,
-    OP_WAIT_FOR_DISCONNECT,
-    OP_DOWNLOAD_FD,
-    OP_UPLOAD,
-};
-
-struct Action {
-    Action(Op op, const std::string& cmd) : op(op), cmd(cmd) {}
-
-    Op op;
-    std::string cmd;
-    std::string msg;
-
-    std::string product;
-
-    void* data = nullptr;
-    // The protocol only supports 32-bit sizes, so you'll have to break
-    // anything larger into multiple chunks.
-    uint32_t size = 0;
-
-    int fd = -1;
-
-    int (*func)(Action& a, int status, const char* resp) = nullptr;
-
-    double start = -1;
-};
-
-static std::vector<std::unique_ptr<Action>> action_list;
-
-bool fb_getvar(Transport* transport, const std::string& key, std::string* value) {
-    std::string cmd = "getvar:" + key;
-
-    char buf[FB_RESPONSE_SZ + 1];
-    memset(buf, 0, sizeof(buf));
-    if (fb_command_response(transport, cmd, buf)) {
-        return false;
-    }
-    *value = buf;
-    return true;
-}
-
-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;
-    }
-    return status;
-}
-
-static Action& queue_action(Op op, const std::string& cmd) {
-    std::unique_ptr<Action> a{new Action(op, cmd)};
-    a->func = cb_default;
-
-    action_list.push_back(std::move(a));
-    return *action_list.back();
-}
-
-void fb_set_active(const std::string& slot) {
-    Action& a = queue_action(OP_COMMAND, "set_active:" + slot);
-    a.msg = "Setting current slot to '" + slot + "'...";
-}
-
-void fb_queue_erase(const std::string& partition) {
-    Action& a = queue_action(OP_COMMAND, "erase:" + partition);
-    a.msg = "Erasing '" + partition + "'...";
-}
-
-void fb_queue_flash_fd(const std::string& partition, int fd, uint32_t sz) {
-    Action& a = queue_action(OP_DOWNLOAD_FD, "");
-    a.fd = fd;
-    a.size = sz;
-    a.msg = android::base::StringPrintf("Sending '%s' (%d KB)...", partition.c_str(), sz / 1024);
-
-    Action& b = queue_action(OP_COMMAND, "flash:" + partition);
-    b.msg = "Writing '" + partition + "'...";
-}
-
-void fb_queue_flash(const std::string& partition, void* data, uint32_t sz) {
-    Action& a = queue_action(OP_DOWNLOAD, "");
-    a.data = data;
-    a.size = sz;
-    a.msg = android::base::StringPrintf("Sending '%s' (%d KB)...", partition.c_str(), sz / 1024);
-
-    Action& b = queue_action(OP_COMMAND, "flash:" + partition);
-    b.msg = "Writing '" + partition + "'...";
-}
-
-void fb_queue_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
-                           size_t current, size_t total) {
-    Action& a = queue_action(OP_DOWNLOAD_SPARSE, "");
-    a.data = s;
-    a.size = 0;
-    a.msg = android::base::StringPrintf("Sending sparse '%s' %zu/%zu (%d KB)...", partition.c_str(),
-                                        current, total, sz / 1024);
-
-    Action& b = queue_action(OP_COMMAND, "flash:" + partition);
-    b.msg =
-        android::base::StringPrintf("Writing '%s' %zu/%zu...", partition.c_str(), current, total);
-}
-
-static int match(const char* str, const char** value, unsigned count) {
-    unsigned n;
-
-    for (n = 0; n < count; n++) {
-        const char *val = value[n];
-        int len = strlen(val);
-        int match;
-
-        if ((len > 1) && (val[len-1] == '*')) {
-            len--;
-            match = !strncmp(val, str, len);
-        } else {
-            match = !strcmp(val, str);
-        }
-
-        if (match) return 1;
-    }
-
-    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;
-    unsigned n;
-
-    if (status) {
-        fprintf(stderr,"FAILED (%s)\n", resp);
-        return status;
-    }
-
-    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.product.c_str(), (split - a.start));
-            a.start = split;
-            return 0;
-        }
-    }
-
-    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;
-        return 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, ".\n\n");
-    return -1;
-}
-
-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) {
-    return cb_check(a, status, resp, 1);
-}
-
-void fb_queue_require(const std::string& product, const std::string& var, bool invert,
-                      size_t nvalues, const char** values) {
-    Action& a = queue_action(OP_QUERY, "getvar:" + var);
-    a.product = product;
-    a.data = values;
-    a.size = nvalues;
-    a.msg = "Checking " + var;
-    a.func = invert ? cb_reject : cb_require;
-    if (a.data == nullptr) die("out of memory");
-}
-
-static int cb_display(Action& a, int status, const char* resp) {
-    if (status) {
-        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));
-    return 0;
-}
-
-void fb_queue_display(const std::string& label, const std::string& var) {
-    Action& a = queue_action(OP_QUERY, "getvar:" + var);
-    a.data = xstrdup(label.c_str());
-    a.func = cb_display;
-}
-
-static int cb_save(Action& a, int status, const char* resp) {
-    if (status) {
-        fprintf(stderr, "%s FAILED (%s)\n", a.cmd.c_str(), resp);
-        return status;
-    }
-    strncpy(reinterpret_cast<char*>(a.data), resp, a.size);
-    return 0;
-}
-
-void fb_queue_query_save(const std::string& var, char* dest, uint32_t dest_size) {
-    Action& a = queue_action(OP_QUERY, "getvar:" + var);
-    a.data = dest;
-    a.size = dest_size;
-    a.func = cb_save;
-}
-
-static int cb_do_nothing(Action&, int, const char*) {
-    fprintf(stderr, "\n");
-    return 0;
-}
-
-void fb_queue_reboot() {
-    Action& a = queue_action(OP_COMMAND, "reboot");
-    a.func = cb_do_nothing;
-    a.msg = "Rebooting...";
-}
-
-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 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 std::string& name, int fd, uint32_t sz) {
-    Action& a = queue_action(OP_DOWNLOAD_FD, "");
-    a.fd = fd;
-    a.size = sz;
-    a.msg = android::base::StringPrintf("Sending '%s' (%d KB)", name.c_str(), sz / 1024);
-}
-
-void fb_queue_upload(const std::string& outfile) {
-    Action& a = queue_action(OP_UPLOAD, "");
-    a.data = xstrdup(outfile.c_str());
-    a.msg = "Uploading '" + outfile + "'";
-}
-
-void fb_queue_notice(const std::string& notice) {
-    Action& a = queue_action(OP_NOTICE, "");
-    a.msg = notice;
-}
-
-void fb_queue_wait_for_disconnect() {
-    queue_action(OP_WAIT_FOR_DISCONNECT, "");
-}
-
-int64_t fb_execute_queue(Transport* transport) {
-    int64_t status = 0;
-    for (auto& a : action_list) {
-        a->start = now();
-        if (!a->msg.empty()) {
-            fprintf(stderr, "%s\n", a->msg.c_str());
-        }
-        if (a->op == OP_DOWNLOAD) {
-            status = fb_download_data(transport, a->data, a->size);
-            status = a->func(*a, status, status ? fb_get_error().c_str() : "");
-            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() : "");
-            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() : "");
-            if (status) break;
-        } else if (a->op == OP_QUERY) {
-            char resp[FB_RESPONSE_SZ + 1] = {};
-            status = fb_command_response(transport, a->cmd, resp);
-            status = a->func(*a, status, status ? fb_get_error().c_str() : resp);
-            if (status) break;
-        } else if (a->op == OP_NOTICE) {
-            // We already showed the notice because it's in `Action::msg`.
-        } else if (a->op == OP_DOWNLOAD_SPARSE) {
-            status = fb_download_data_sparse(transport, reinterpret_cast<sparse_file*>(a->data));
-            status = a->func(*a, status, status ? fb_get_error().c_str() : "");
-            if (status) break;
-        } else if (a->op == OP_WAIT_FOR_DISCONNECT) {
-            transport->WaitForDisconnect();
-        } else if (a->op == OP_UPLOAD) {
-            status = fb_upload_data(transport, reinterpret_cast<char*>(a->data));
-            status = a->func(*a, status, status ? fb_get_error().c_str() : "");
-        } else {
-            die("unknown action: %d", a->op);
-        }
-    }
-    action_list.clear();
-    return status;
-}
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 e89586f..8923f40 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -26,6 +26,8 @@
  * SUCH DAMAGE.
  */
 
+#include "fastboot.h"
+
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -43,6 +45,8 @@
 
 #include <chrono>
 #include <functional>
+#include <regex>
+#include <string>
 #include <thread>
 #include <utility>
 #include <vector>
@@ -53,51 +57,52 @@
 #include <android-base/parsenetaddress.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <android-base/test_utils.h>
 #include <android-base/unique_fd.h>
+#include <build/version.h>
+#include <liblp/liblp.h>
+#include <platform_tools_version.h>
 #include <sparse/sparse.h>
 #include <ziparchive/zip_archive.h>
 
 #include "bootimg_utils.h"
+#include "constants.h"
 #include "diagnose_usb.h"
-#include "fastboot.h"
+#include "fastboot_driver.h"
 #include "fs.h"
 #include "tcp.h"
 #include "transport.h"
 #include "udp.h"
 #include "usb.h"
+#include "util.h"
 
+using android::base::ReadFully;
+using android::base::Split;
+using android::base::Trim;
 using android::base::unique_fd;
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-char cur_product[FB_RESPONSE_SZ + 1];
+using namespace std::string_literals;
 
 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_v2 g_boot_img_hdr = {};
+static std::string g_cmdline;
+static std::string g_dtb_path;
 
 static bool g_disable_verity = false;
 static bool g_disable_verification = false;
 
 static const std::string convert_fbe_marker_filename("convert_fbe");
 
+fastboot::FastBootDriver* fb = nullptr;
+
 enum fb_buffer_type {
     FB_BUFFER_FD,
     FB_BUFFER_SPARSE,
@@ -108,38 +113,71 @@
     void* data;
     int64_t sz;
     int fd;
+    int64_t image_size;
 };
 
-static struct {
+enum class ImageType {
+    // Must be flashed for device to boot into the kernel.
+    BootCritical,
+    // Normal partition to be flashed during "flashall".
+    Normal,
+    // Partition that is never flashed during "flashall".
+    Extra
+};
+
+struct Image {
     const char* nickname;
     const char* img_name;
     const char* sig_name;
     const char* part_name;
-    bool is_optional;
-    bool is_secondary;
-} images[] = {
-    // 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 },
-    { "recovery", "recovery.img",     "recovery.sig", "recovery", true,  false },
-    { "system",   "system.img",       "system.sig",   "system",   false, false },
-    { nullptr,    "system_other.img", "system.sig",   "system",   true,  true  },
-    { "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
+    bool optional_if_no_image;
+    ImageType type;
+    bool IsSecondary() const { return nickname == nullptr; }
 };
 
-static std::string find_item_given_name(const char* img_name) {
+static Image images[] = {
+        // clang-format off
+    { "boot",     "boot.img",         "boot.sig",     "boot",     false, ImageType::BootCritical },
+    { nullptr,    "boot_other.img",   "boot.sig",     "boot",     true,  ImageType::Normal },
+    { "cache",    "cache.img",        "cache.sig",    "cache",    true,  ImageType::Extra },
+    { "dtbo",     "dtbo.img",         "dtbo.sig",     "dtbo",     true,  ImageType::BootCritical },
+    { "dts",      "dt.img",           "dt.sig",       "dts",      true,  ImageType::BootCritical },
+    { "odm",      "odm.img",          "odm.sig",      "odm",      true,  ImageType::Normal },
+    { "product",  "product.img",      "product.sig",  "product",  true,  ImageType::Normal },
+    { "recovery", "recovery.img",     "recovery.sig", "recovery", true,  ImageType::BootCritical },
+    { "super",    "super.img",        "super.sig",    "super",    true,  ImageType::Extra },
+    { "system",   "system.img",       "system.sig",   "system",   false, ImageType::Normal },
+    { "system_ext",
+                  "system_ext.img",   "system_ext.sig",
+                                                      "system_ext",
+                                                                  true,  ImageType::Normal },
+    { nullptr,    "system_other.img", "system.sig",   "system",   true,  ImageType::Normal },
+    { "userdata", "userdata.img",     "userdata.sig", "userdata", true,  ImageType::Extra },
+    { "vbmeta",   "vbmeta.img",       "vbmeta.sig",   "vbmeta",   true,  ImageType::BootCritical },
+    { "vbmeta_system",
+                  "vbmeta_system.img",
+                                      "vbmeta_system.sig",
+                                                      "vbmeta_system",
+                                                                  true,  ImageType::BootCritical },
+    { "vendor",   "vendor.img",       "vendor.sig",   "vendor",   true,  ImageType::Normal },
+    { nullptr,    "vendor_other.img", "vendor.sig",   "vendor",   true,  ImageType::Normal },
+        // clang-format on
+};
+
+static char* get_android_product_out() {
     char* dir = getenv("ANDROID_PRODUCT_OUT");
     if (dir == nullptr || dir[0] == '\0') {
+        return nullptr;
+    }
+    return dir;
+}
+
+static std::string find_item_given_name(const std::string& img_name) {
+    char* dir = get_android_product_out();
+    if (!dir) {
         die("ANDROID_PRODUCT_OUT not set");
     }
-    return android::base::StringPrintf("%s/%s", dir, img_name);
+    return std::string(dir) + "/" + img_name;
 }
 
 static std::string find_item(const std::string& item) {
@@ -149,55 +187,53 @@
         }
     }
 
-    if (item == "userdata") return find_item_given_name("userdata.img");
-    if (item == "cache") return find_item_given_name("cache.img");
-
     fprintf(stderr, "unknown partition '%s'\n", item.c_str());
     return "";
 }
 
+double last_start_time;
+
+static void Status(const std::string& message) {
+    static constexpr char kStatusFormat[] = "%-50s ";
+    fprintf(stderr, kStatusFormat, message.c_str());
+    last_start_time = now();
+}
+
+static void Epilog(int status) {
+    if (status) {
+        fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
+        die("Command failed");
+    } else {
+        double split = now();
+        fprintf(stderr, "OKAY [%7.3fs]\n", (split - last_start_time));
+    }
+}
+
+static void InfoMessage(const std::string& info) {
+    fprintf(stderr, "(bootloader) %s\n", info.c_str());
+}
+
 static int64_t get_file_size(int fd) {
     struct stat sb;
-    return fstat(fd, &sb) == -1 ? -1 : sb.st_size;
+    if (fstat(fd, &sb) == -1) {
+        die("could not get file size");
+    }
+    return sb.st_size;
 }
 
-static void* load_fd(int fd, int64_t* sz) {
-    int errno_tmp;
-    char* data = nullptr;
+bool ReadFileToVector(const std::string& file, std::vector<char>* out) {
+    out->clear();
 
-    *sz = get_file_size(fd);
-    if (*sz < 0) {
-        goto oops;
+    unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY)));
+    if (fd == -1) {
+        return false;
     }
 
-    data = (char*) malloc(*sz);
-    if (data == nullptr) goto oops;
-
-    if(read(fd, data, *sz) != *sz) goto oops;
-    close(fd);
-
-    return data;
-
-oops:
-    errno_tmp = errno;
-    close(fd);
-    if(data != 0) free(data);
-    errno = errno_tmp;
-    return 0;
-}
-
-static void* load_file(const std::string& path, int64_t* sz) {
-    int fd = open(path.c_str(), O_RDONLY | O_BINARY);
-    if (fd == -1) return nullptr;
-    return load_fd(fd, sz);
+    out->resize(get_file_size(fd));
+    return ReadFully(fd, out->data(), out->size());
 }
 
 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;
     }
@@ -223,7 +259,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());
@@ -238,19 +274,14 @@
 // 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
 // object, and the caller should not attempt to delete the returned Transport.
 static Transport* open_device() {
-    static Transport* transport = nullptr;
     bool announce = true;
 
-    if (transport != nullptr) {
-        return transport;
-    }
-
     Socket::Protocol protocol = Socket::Protocol::kTcp;
     std::string host;
     int port = 0;
@@ -270,13 +301,12 @@
         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());
             }
         }
     }
 
+    Transport* transport = nullptr;
     while (true) {
         if (!host.empty()) {
             std::string error;
@@ -327,190 +357,168 @@
 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"
+            " gsi wipe|disable           Wipe or disable a GSI installation (fastbootd only).\n"
+            " wipe-super [SUPER_EMPTY]   Wipe the super partition. This will reset it to\n"
+            "                            contain an empty set of default dynamic partitions.\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"
+            " --dtb DTB                  Specify path to DTB for boot image header version 2.\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"
+            " --dtb-offset               Set dtb offset (default: 0x01100000).\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"
-            "  --disable-verity                         Set the disable-verity flag in the\n"
-            "                                           the vbmeta image being flashed.\n"
-            "  --disable-verification                   Set the disable-verification flag in"
-            "                                           the vbmeta image being flashed.\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"
+            " --force                    Force a flash operation that may be unsafe.\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"
-            "  --header-version                         Set boot image header version while\n"
-            "                                           using flash:raw and boot commands to \n"
-            "                                           to create a boot image.\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, uint32_t header_version) {
-    int64_t ksize;
-    void* kdata = load_file(kernel.c_str(), &ksize);
-    if (kdata == nullptr) die("cannot load '%s': %s", kernel.c_str(), strerror(errno));
+static std::vector<char> LoadBootableImage(const std::string& kernel, const std::string& ramdisk,
+                                           const std::string& second_stage) {
+    std::vector<char> kernel_data;
+    if (!ReadFileToVector(kernel, &kernel_data)) {
+        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))) {
+    if (kernel_data.size() < sizeof(boot_img_hdr_v2)) {
         die("cannot load '%s': too short", kernel.c_str());
     }
-    if (!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
-        if (cmdline) bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v1*>(kdata), cmdline);
+    if (!memcmp(kernel_data.data(), BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
+        if (!g_cmdline.empty()) {
+            bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v2*>(kernel_data.data()), g_cmdline);
+        }
 
         if (!ramdisk.empty()) die("cannot boot a boot.img *and* ramdisk");
 
-        *sz = ksize;
-        return kdata;
+        return kernel_data;
     }
 
-    void* rdata = nullptr;
-    int64_t rsize = 0;
+    std::vector<char> ramdisk_data;
     if (!ramdisk.empty()) {
-        rdata = load_file(ramdisk.c_str(), &rsize);
-        if (rdata == nullptr) die("cannot load '%s': %s", ramdisk.c_str(), strerror(errno));
+        if (!ReadFileToVector(ramdisk, &ramdisk_data)) {
+            die("cannot load '%s': %s", ramdisk.c_str(), strerror(errno));
+        }
     }
 
-    void* sdata = nullptr;
-    int64_t ssize = 0;
+    std::vector<char> second_stage_data;
     if (!second_stage.empty()) {
-        sdata = load_file(second_stage.c_str(), &ssize);
-        if (sdata == nullptr) die("cannot load '%s': %s", second_stage.c_str(), strerror(errno));
+        if (!ReadFileToVector(second_stage, &second_stage_data)) {
+            die("cannot load '%s': %s", second_stage.c_str(), strerror(errno));
+        }
+    }
+
+    std::vector<char> dtb_data;
+    if (!g_dtb_path.empty()) {
+        if (g_boot_img_hdr.header_version < 2) {
+                    die("Argument dtb not supported for boot image header version %d\n",
+                        g_boot_img_hdr.header_version);
+        }
+        if (!ReadFileToVector(g_dtb_path, &dtb_data)) {
+            die("cannot load '%s': %s", g_dtb_path.c_str(), strerror(errno));
+        }
     }
 
     fprintf(stderr,"creating boot image...\n");
-    int64_t bsize = 0;
-    boot_img_hdr_v1* bdata = mkbootimg(kdata, ksize, kernel_offset,
-                      rdata, rsize, ramdisk_offset,
-                      sdata, ssize, second_offset,
-                      page_size, base_addr, tags_offset, header_version, &bsize);
-    if (bdata == nullptr) die("failed to create boot.img");
 
-    if (cmdline) bootimg_set_cmdline(bdata, cmdline);
-    fprintf(stderr, "creating boot image - %" PRId64 " bytes\n", bsize);
-    *sz = bsize;
+    std::vector<char> out;
+    boot_img_hdr_v2* boot_image_data = mkbootimg(kernel_data, ramdisk_data, second_stage_data,
+                                                 dtb_data, g_base_addr, g_boot_img_hdr, &out);
 
-    return bdata;
+    if (!g_cmdline.empty()) bootimg_set_cmdline(boot_image_data, g_cmdline);
+    fprintf(stderr, "creating boot image - %zu bytes\n", out.size());
+    return out;
 }
 
-static void* unzip_to_memory(ZipArchiveHandle zip, const char* entry_name, int64_t* sz) {
-    ZipString zip_entry_name(entry_name);
+static bool UnzipToMemory(ZipArchiveHandle zip, const std::string& entry_name,
+                          std::vector<char>* out) {
     ZipEntry zip_entry;
-    if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
-        fprintf(stderr, "archive does not contain '%s'\n", entry_name);
-        return nullptr;
+    if (FindEntry(zip, entry_name, &zip_entry) != 0) {
+        fprintf(stderr, "archive does not contain '%s'\n", entry_name.c_str());
+        return false;
     }
 
-    *sz = zip_entry.uncompressed_length;
+    out->resize(zip_entry.uncompressed_length);
 
-    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) die("failed to allocate %" PRId64 " bytes for '%s'", *sz, entry_name);
+    fprintf(stderr, "extracting %s (%zu MB) to RAM...\n", entry_name.c_str(),
+            out->size() / 1024 / 1024);
 
-    int error = ExtractToMemory(zip, &zip_entry, data, zip_entry.uncompressed_length);
-    if (error != 0) die("failed to extract '%s': %s", entry_name, ErrorCodeString(error));
+    int error = ExtractToMemory(zip, &zip_entry, reinterpret_cast<uint8_t*>(out->data()),
+                                out->size());
+    if (error != 0) die("failed to extract '%s': %s", entry_name.c_str(), ErrorCodeString(error));
 
-    return data;
+    return true;
 }
 
 #if defined(_WIN32)
@@ -542,7 +550,7 @@
     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());
 }
@@ -558,18 +566,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;
@@ -579,16 +587,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;
@@ -609,15 +612,12 @@
 }
 
 static int unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
-    unique_fd fd(make_temporary_fd());
-    if (fd == -1) {
-        die("failed to create temporary file for '%s': %s", entry_name, strerror(errno));
-    }
+    unique_fd fd(make_temporary_fd(entry_name));
 
-    ZipString zip_entry_name(entry_name);
     ZipEntry zip_entry;
-    if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
+    if (FindEntry(zip, entry_name, &zip_entry) != 0) {
         fprintf(stderr, "archive does not contain '%s'\n", entry_name);
+        errno = ENOENT;
         return -1;
     }
 
@@ -638,126 +638,178 @@
     return fd.release();
 }
 
-static char* strip(char* s) {
-    while (*s && isspace(*s)) s++;
+static void CheckRequirement(const std::string& cur_product, const std::string& var,
+                             const std::string& product, bool invert,
+                             const std::vector<std::string>& options) {
+    Status("Checking '" + var + "'");
 
-    int n = strlen(s);
-    while (n-- > 0) {
-        if (!isspace(s[n])) break;
-        s[n] = 0;
+    double start = now();
+
+    if (!product.empty()) {
+        if (product != cur_product) {
+            double split = now();
+            fprintf(stderr, "IGNORE, product is %s required only for %s [%7.3fs]\n",
+                    cur_product.c_str(), product.c_str(), (split - start));
+            return;
+        }
     }
-    return s;
+
+    std::string var_value;
+    if (fb->GetVar(var, &var_value) != fastboot::SUCCESS) {
+        fprintf(stderr, "FAILED\n\n");
+        fprintf(stderr, "Could not getvar for '%s' (%s)\n\n", var.c_str(),
+                fb->Error().c_str());
+        die("requirements not met!");
+    }
+
+    bool match = false;
+    for (const auto& option : options) {
+        if (option == var_value || (option.back() == '*' &&
+                                    !var_value.compare(0, option.length() - 1, option, 0,
+                                                       option.length() - 1))) {
+            match = true;
+            break;
+        }
+    }
+
+    if (invert) {
+        match = !match;
+    }
+
+    if (match) {
+        double split = now();
+        fprintf(stderr, "OKAY [%7.3fs]\n", (split - start));
+        return;
+    }
+
+    fprintf(stderr, "FAILED\n\n");
+    fprintf(stderr, "Device %s is '%s'.\n", var.c_str(), var_value.c_str());
+    fprintf(stderr, "Update %s '%s'", invert ? "rejects" : "requires", options[0].c_str());
+    for (auto it = std::next(options.begin()); it != options.end(); ++it) {
+        fprintf(stderr, " or '%s'", it->c_str());
+    }
+    fprintf(stderr, ".\n\n");
+    die("requirements not met!");
 }
 
-#define MAX_OPTIONS 32
-static void check_requirement(Transport* transport, char* line) {
-    char *val[MAX_OPTIONS];
-    unsigned count;
-    char *x;
-    int invert = 0;
-
+bool ParseRequirementLine(const std::string& line, std::string* name, std::string* product,
+                          bool* invert, std::vector<std::string>* options) {
     // "require product=alpha|beta|gamma"
     // "require version-bootloader=1234"
     // "require-for-product:gamma version-bootloader=istanbul|constantinople"
     // "require partition-exists=vendor"
+    *product = "";
+    *invert = false;
 
-    char* name = line;
-    const char* product = "";
-    if (!strncmp(name, "reject ", 7)) {
-        name += 7;
-        invert = 1;
-    } else if (!strncmp(name, "require ", 8)) {
-        name += 8;
-        invert = 0;
-    } else if (!strncmp(name, "require-for-product:", 20)) {
-        // Get the product and point name past it
-        product = name + 20;
-        name = strchr(name, ' ');
-        if (!name) die("android-info.txt syntax error: %s", line);
-        *name = 0;
-        name += 1;
-        invert = 0;
+    auto require_reject_regex = std::regex{"(require\\s+|reject\\s+)?\\s*(\\S+)\\s*=\\s*(.*)"};
+    auto require_product_regex =
+            std::regex{"require-for-product:\\s*(\\S+)\\s+(\\S+)\\s*=\\s*(.*)"};
+    std::smatch match_results;
+
+    if (std::regex_match(line, match_results, require_reject_regex)) {
+        *invert = Trim(match_results[1]) == "reject";
+    } else if (std::regex_match(line, match_results, require_product_regex)) {
+        *product = match_results[1];
+    } else {
+        return false;
     }
 
-    x = strchr(name, '=');
-    if (x == 0) return;
-    *x = 0;
-    val[0] = x + 1;
+    *name = match_results[2];
+    // Work around an unfortunate name mismatch.
+    if (*name == "board") {
+        *name = "product";
+    }
 
-    name = strip(name);
+    auto raw_options = Split(match_results[3], "|");
+    for (const auto& option : raw_options) {
+        auto trimmed_option = Trim(option);
+        options->emplace_back(trimmed_option);
+    }
 
-    // "require partition-exists=x" is a special case, added because of the trouble we had when
-    // Pixel 2 shipped with new partitions and users used old versions of fastboot to flash them,
-    // missing out new partitions. A device with new partitions can use "partition-exists" to
-    // override the `is_optional` field in the `images` array.
-    if (!strcmp(name, "partition-exists")) {
-        const char* partition_name = val[0];
-        std::string has_slot;
-        if (!fb_getvar(transport, std::string("has-slot:") + partition_name, &has_slot) ||
-            (has_slot != "yes" && has_slot != "no")) {
-            die("device doesn't have required partition %s!", partition_name);
+    return true;
+}
+
+// "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 fields `optional_if_no_image` in the `images` array.
+static void HandlePartitionExists(const std::vector<std::string>& options) {
+    const std::string& partition_name = options[0];
+    std::string has_slot;
+    if (fb->GetVar("has-slot:" + partition_name, &has_slot) != fastboot::SUCCESS ||
+        (has_slot != "yes" && has_slot != "no")) {
+        die("device doesn't have required partition %s!", partition_name.c_str());
+    }
+    bool known_partition = false;
+    for (size_t i = 0; i < arraysize(images); ++i) {
+        if (images[i].nickname && images[i].nickname == partition_name) {
+            images[i].optional_if_no_image = false;
+            known_partition = true;
         }
-        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.c_str());
+    }
+}
+
+static void CheckRequirements(const std::string& data) {
+    std::string cur_product;
+    if (fb->GetVar("product", &cur_product) != fastboot::SUCCESS) {
+        fprintf(stderr, "getvar:product FAILED (%s)\n", fb->Error().c_str());
+    }
+
+    auto lines = Split(data, "\n");
+    for (const auto& line : lines) {
+        if (line.empty()) {
+            continue;
         }
-        if (!known_partition) {
-            die("device requires partition %s which is not known to this version of fastboot",
-                partition_name);
+
+        std::string name;
+        std::string product;
+        bool invert;
+        std::vector<std::string> options;
+
+        if (!ParseRequirementLine(line, &name, &product, &invert, &options)) {
+            fprintf(stderr, "android-info.txt syntax error: %s\n", line.c_str());
+            continue;
         }
+        if (name == "partition-exists") {
+            HandlePartitionExists(options);
+        } else {
+            CheckRequirement(cur_product, name, product, invert, options);
+        }
+    }
+}
+
+static void DisplayVarOrError(const std::string& label, const std::string& var) {
+    std::string value;
+
+    if (fb->GetVar(var, &value) != fastboot::SUCCESS) {
+        Status("getvar:" + var);
+        fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
         return;
     }
-
-    for(count = 1; count < MAX_OPTIONS; count++) {
-        x = strchr(val[count - 1],'|');
-        if (x == 0) break;
-        *x = 0;
-        val[count] = x + 1;
-    }
-
-    // Work around an unfortunate name mismatch.
-    const char* var = name;
-    if (!strcmp(name, "board")) var = "product";
-
-    const char** out = reinterpret_cast<const char**>(malloc(sizeof(char*) * count));
-    if (out == nullptr) die("out of memory");
-
-    for (size_t i = 0; i < count; ++i) {
-        out[i] = xstrdup(strip(val[i]));
-    }
-
-    fb_queue_require(product, var, invert, count, out);
+    fprintf(stderr, "%s: %s\n", label.c_str(), value.c_str());
 }
 
-static void check_requirements(Transport* transport, char* data, int64_t sz) {
-    char* s = data;
-    while (sz-- > 0) {
-        if (*s == '\n') {
-            *s++ = 0;
-            check_requirement(transport, data);
-            data = s;
-        } else {
-            s++;
-        }
-    }
-    if (fb_execute_queue(transport)) die("requirements not met!");
+static void DumpInfo() {
+    fprintf(stderr, "--------------------------------------------\n");
+    DisplayVarOrError("Bootloader Version...", "version-bootloader");
+    DisplayVarOrError("Baseband Version.....", "version-baseband");
+    DisplayVarOrError("Serial Number........", "serialno");
+    fprintf(stderr, "--------------------------------------------\n");
+
 }
 
-static void queue_info_dump() {
-    fb_queue_notice("--------------------------------------------");
-    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");
 
+    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");
 
@@ -770,11 +822,11 @@
     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) != fastboot::SUCCESS ||
+        max_download_size.empty()) {
+        verbose("target didn't report max-download-size");
         return 0;
     }
 
@@ -786,22 +838,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;
@@ -817,25 +864,21 @@
     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);
+    if (sparse_file* s = sparse_file_import_auto(fd, false, false)) {
+        buf->image_size = sparse_file_len(s, false, false);
+        sparse_file_destroy(s);
+    } else {
+        buf->image_size = sz;
+    }
+
+    lseek(fd, 0, SEEK_SET);
+    int64_t limit = get_sparse_limit(sz);
     if (limit) {
         sparse_file** s = load_sparse_files(fd, limit);
         if (s == nullptr) {
@@ -853,7 +896,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) {
@@ -869,7 +912,7 @@
         return false;
     }
 
-    return load_buf_fd(transport, fd.release(), buf);
+    return load_buf_fd(fd.release(), buf);
 }
 
 static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf) {
@@ -879,10 +922,7 @@
         return;
     }
 
-    int fd = make_temporary_fd();
-    if (fd == -1) {
-        die("Failed to create temporary file for vbmeta rewriting");
-    }
+    int fd = make_temporary_fd("vbmeta rewriting");
 
     std::string data;
     if (!android::base::ReadFdToString(buf->fd, &data)) {
@@ -932,63 +972,36 @@
 
             for (size_t i = 0; i < sparse_files.size(); ++i) {
                 const auto& pair = sparse_files[i];
-                fb_queue_flash_sparse(partition, pair.first, pair.second, i + 1, sparse_files.size());
+                fb->FlashPartition(partition, pair.first, pair.second, i + 1, sparse_files.size());
             }
             break;
         }
         case FB_BUFFER_FD:
-            fb_queue_flash_fd(partition, buf->fd, buf->sz);
+            fb->FlashPartition(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) != fastboot::SUCCESS) 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) != fastboot::SUCCESS ||
+        !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.
@@ -999,27 +1012,25 @@
     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 {
@@ -1028,11 +1039,11 @@
         }
     }
 
-    int count = get_slot_count(transport);
+    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");
         }
@@ -1049,22 +1060,22 @@
     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) != fastboot::SUCCESS) {
         /* 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");
             }
@@ -1086,217 +1097,325 @@
  * 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)) {
+        if (fb->GetVar("has-slot:" + part, &has_slot) != fastboot::SUCCESS) {
             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 bool is_logical(const std::string& partition) {
+    std::string value;
+    return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
+}
+
+static bool is_retrofit_device() {
+    std::string value;
+    if (fb->GetVar("super-partition-name", &value) != fastboot::SUCCESS) {
+        return false;
+    }
+    return android::base::StartsWith(value, "system_");
+}
+
+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));
     }
+    if (is_logical(pname)) {
+        fb->ResizePartition(pname, std::to_string(buf.image_size));
+    }
     flash_buf(pname, &buf);
 }
 
-static void do_update_signature(ZipArchiveHandle zip, const char* filename) {
-    int64_t 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");
-}
-
 // 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);
+        fb->SetActive(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);
+            fb->SetActive(current_slot);
         }
     }
 }
 
-static void do_update(Transport* transport, const char* filename, const std::string& slot_override, bool erase_first, bool skip_secondary) {
-    queue_info_dump();
+static bool is_userspace_fastboot() {
+    std::string value;
+    return fb->GetVar("is-userspace", &value) == fastboot::SUCCESS && value == "yes";
+}
 
-    fb_queue_query_save("product", cur_product, sizeof(cur_product));
+static void reboot_to_userspace_fastboot() {
+    fb->RebootTo("fastboot");
 
+    auto* old_transport = fb->set_transport(nullptr);
+    delete old_transport;
+
+    // Give the current connection time to close.
+    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+
+    fb->set_transport(open_device());
+
+    if (!is_userspace_fastboot()) {
+        die("Failed to boot into userspace fastboot; one or more components might be unbootable.");
+    }
+
+    // Reset target_sparse_limit after reboot to userspace fastboot. Max
+    // download sizes may differ in bootloader and fastbootd.
+    target_sparse_limit = -1;
+}
+
+class ImageSource {
+  public:
+    virtual bool ReadFile(const std::string& name, std::vector<char>* out) const = 0;
+    virtual int OpenFile(const std::string& name) const = 0;
+};
+
+class FlashAllTool {
+  public:
+    FlashAllTool(const ImageSource& source, const std::string& slot_override, bool skip_secondary, bool wipe);
+
+    void Flash();
+
+  private:
+    void CheckRequirements();
+    void DetermineSecondarySlot();
+    void CollectImages();
+    void FlashImages(const std::vector<std::pair<const Image*, std::string>>& images);
+    void FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf);
+    void UpdateSuperPartition();
+
+    const ImageSource& source_;
+    std::string slot_override_;
+    bool skip_secondary_;
+    bool wipe_;
+    std::string secondary_slot_;
+    std::vector<std::pair<const Image*, std::string>> boot_images_;
+    std::vector<std::pair<const Image*, std::string>> os_images_;
+};
+
+FlashAllTool::FlashAllTool(const ImageSource& source, const std::string& slot_override, bool skip_secondary, bool wipe)
+   : source_(source),
+     slot_override_(slot_override),
+     skip_secondary_(skip_secondary),
+     wipe_(wipe)
+{
+}
+
+void FlashAllTool::Flash() {
+    DumpInfo();
+    CheckRequirements();
+
+    // Change the slot first, so we boot into the correct recovery image when
+    // using fastbootd.
+    if (slot_override_ == "all") {
+        set_active("a");
+    } else {
+        set_active(slot_override_);
+    }
+
+    DetermineSecondarySlot();
+    CollectImages();
+
+    // First flash boot partitions. We allow this to happen either in userspace
+    // or in bootloader fastboot.
+    FlashImages(boot_images_);
+
+    // Sync the super partition. This will reboot to userspace fastboot if needed.
+    UpdateSuperPartition();
+
+    // Resize any logical partition to 0, so each partition is reset to 0
+    // extents, and will achieve more optimal allocation.
+    for (const auto& [image, slot] : os_images_) {
+        auto resize_partition = [](const std::string& partition) -> void {
+            if (is_logical(partition)) {
+                fb->ResizePartition(partition, "0");
+            }
+        };
+        do_for_partitions(image->part_name, slot, resize_partition, false);
+    }
+
+    // Flash OS images, resizing logical partitions as needed.
+    FlashImages(os_images_);
+}
+
+void FlashAllTool::CheckRequirements() {
+    std::vector<char> contents;
+    if (!source_.ReadFile("android-info.txt", &contents)) {
+        die("could not read android-info.txt");
+    }
+    ::CheckRequirements({contents.data(), contents.size()});
+}
+
+void FlashAllTool::DetermineSecondarySlot() {
+    if (skip_secondary_) {
+        return;
+    }
+    if (slot_override_ != "" && slot_override_ != "all") {
+        secondary_slot_ = get_other_slot(slot_override_);
+    } else {
+        secondary_slot_ = get_other_slot();
+    }
+    if (secondary_slot_ == "") {
+        if (supports_AB()) {
+            fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
+        }
+        skip_secondary_ = true;
+    }
+}
+
+void FlashAllTool::CollectImages() {
+    for (size_t i = 0; i < arraysize(images); ++i) {
+        std::string slot = slot_override_;
+        if (images[i].IsSecondary()) {
+            if (skip_secondary_) {
+                continue;
+            }
+            slot = secondary_slot_;
+        }
+        if (images[i].type == ImageType::BootCritical) {
+            boot_images_.emplace_back(&images[i], slot);
+        } else if (images[i].type == ImageType::Normal) {
+            os_images_.emplace_back(&images[i], slot);
+        }
+    }
+}
+
+void FlashAllTool::FlashImages(const std::vector<std::pair<const Image*, std::string>>& images) {
+    for (const auto& [image, slot] : images) {
+        fastboot_buffer buf;
+        int fd = source_.OpenFile(image->img_name);
+        if (fd < 0 || !load_buf_fd(fd, &buf)) {
+            if (image->optional_if_no_image) {
+                continue;
+            }
+            die("could not load '%s': %s", image->img_name, strerror(errno));
+        }
+        FlashImage(*image, slot, &buf);
+    }
+}
+
+void FlashAllTool::FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf) {
+    auto flash = [&, this](const std::string& partition_name) {
+        std::vector<char> signature_data;
+        if (source_.ReadFile(image.sig_name, &signature_data)) {
+            fb->Download("signature", signature_data);
+            fb->RawCommand("signature", "installing signature");
+        }
+
+        if (is_logical(partition_name)) {
+            fb->ResizePartition(partition_name, std::to_string(buf->image_size));
+        }
+        flash_buf(partition_name.c_str(), buf);
+    };
+    do_for_partitions(image.part_name, slot, flash, false);
+}
+
+void FlashAllTool::UpdateSuperPartition() {
+    int fd = source_.OpenFile("super_empty.img");
+    if (fd < 0) {
+        return;
+    }
+    if (!is_userspace_fastboot()) {
+        reboot_to_userspace_fastboot();
+    }
+
+    std::string super_name;
+    if (fb->GetVar("super-partition-name", &super_name) != fastboot::RetCode::SUCCESS) {
+        super_name = "super";
+    }
+    fb->Download(super_name, fd, get_file_size(fd));
+
+    std::string command = "update-super:" + super_name;
+    if (wipe_) {
+        command += ":wipe";
+    }
+    fb->RawCommand(command, "Updating super partition");
+
+    // Retrofit devices have two super partitions, named super_a and super_b.
+    // On these devices, secondary slots must be flashed as physical
+    // partitions (otherwise they would not mount on first boot). To enforce
+    // this, we delete any logical partitions for the "other" slot.
+    if (is_retrofit_device()) {
+        for (const auto& [image, slot] : os_images_) {
+            std::string partition_name = image->part_name + "_"s + slot;
+            if (image->IsSecondary() && is_logical(partition_name)) {
+                fb->DeletePartition(partition_name);
+            }
+        }
+    }
+}
+
+class ZipImageSource final : public ImageSource {
+  public:
+    explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {}
+    bool ReadFile(const std::string& name, std::vector<char>* out) const override;
+    int OpenFile(const std::string& name) const override;
+
+  private:
+    ZipArchiveHandle zip_;
+};
+
+bool ZipImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {
+    return UnzipToMemory(zip_, name, out);
+}
+
+int ZipImageSource::OpenFile(const std::string& name) const {
+    return unzip_to_file(zip_, name.c_str());
+}
+
+static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary) {
     ZipArchiveHandle zip;
     int error = OpenArchive(filename, &zip);
     if (error != 0) {
         die("failed to open zip file '%s': %s", filename, ErrorCodeString(error));
     }
 
-    int64_t sz;
-    void* data = unzip_to_memory(zip, "android-info.txt", &sz);
-    if (data == nullptr) {
-        die("update package '%s' has no android-info.txt", filename);
-    }
-
-    check_requirements(transport, reinterpret_cast<char*>(data), sz);
-
-    std::string secondary;
-    if (!skip_secondary) {
-        if (slot_override != "") {
-            secondary = get_other_slot(transport, slot_override);
-        } else {
-            secondary = get_other_slot(transport);
-        }
-        if (secondary == "") {
-            if (supports_AB(transport)) {
-                fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
-            }
-            skip_secondary = true;
-        }
-    }
-    for (size_t i = 0; i < arraysize(images); ++i) {
-        const char* slot = slot_override.c_str();
-        if (images[i].is_secondary) {
-            if (!skip_secondary) {
-                slot = secondary.c_str();
-            } else {
-                continue;
-            }
-        }
-
-        int fd = unzip_to_file(zip, images[i].img_name);
-        if (fd == -1) {
-            if (images[i].is_optional) {
-                continue; // An optional file is missing, so ignore it.
-            }
-            die("non-optional file %s missing", images[i].img_name);
-        }
-
-        fastboot_buffer buf;
-        if (!load_buf_fd(transport, fd, &buf)) {
-            die("cannot load %s from flash: %s", images[i].img_name, strerror(errno));
-        }
-
-        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);
-            }
-            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);
-    }
-
-    if (slot_override == "all") {
-        set_active(transport, "a");
-    } else {
-        set_active(transport, slot_override);
-    }
+    FlashAllTool tool(ZipImageSource(zip), slot_override, skip_secondary, false);
+    tool.Flash();
 
     CloseArchive(zip);
 }
 
-static void do_send_signature(const std::string& fn) {
-    std::size_t extension_loc = fn.find(".img");
-    if (extension_loc == std::string::npos) return;
+class LocalImageSource final : public ImageSource {
+  public:
+    bool ReadFile(const std::string& name, std::vector<char>* out) const override;
+    int OpenFile(const std::string& name) const override;
+};
 
-    std::string fs_sig = fn.substr(0, extension_loc) + ".sig";
-
-    int64_t sz;
-    void* data = load_file(fs_sig.c_str(), &sz);
-    if (data == nullptr) return;
-
-    fb_queue_download("signature", data, sz);
-    fb_queue_command("signature", "installing signature");
+bool LocalImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {
+    auto path = find_item_given_name(name);
+    if (path.empty()) {
+        return false;
+    }
+    return ReadFileToVector(path, out);
 }
 
-static void do_flashall(Transport* transport, const std::string& slot_override, int erase_first, bool skip_secondary) {
-    std::string fname;
-    queue_info_dump();
+int LocalImageSource::OpenFile(const std::string& name) const {
+    auto path = find_item_given_name(name);
+    return open(path.c_str(), O_RDONLY | O_BINARY);
+}
 
-    fb_queue_query_save("product", cur_product, sizeof(cur_product));
-
-    fname = find_item_given_name("android-info.txt");
-    if (fname.empty()) die("cannot find android-info.txt");
-
-    int64_t sz;
-    void* data = load_file(fname.c_str(), &sz);
-    if (data == nullptr) die("could not load android-info.txt: %s", strerror(errno));
-
-    check_requirements(transport, reinterpret_cast<char*>(data), sz);
-
-    std::string secondary;
-    if (!skip_secondary) {
-        if (slot_override != "") {
-            secondary = get_other_slot(transport, slot_override);
-        } else {
-            secondary = get_other_slot(transport);
-        }
-        if (secondary == "") {
-            if (supports_AB(transport)) {
-                fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
-            }
-            skip_secondary = true;
-        }
-    }
-
-    for (size_t i = 0; i < arraysize(images); i++) {
-        const char* slot = NULL;
-        if (images[i].is_secondary) {
-            if (!skip_secondary) slot = secondary.c_str();
-        } else {
-            slot = slot_override.c_str();
-        }
-        if (!slot) continue;
-        fname = find_item_given_name(images[i].img_name);
-        fastboot_buffer buf;
-        if (!load_buf(transport, fname.c_str(), &buf)) {
-            if (images[i].is_optional) continue;
-            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);
-            }
-            flash_buf(partition.c_str(), &buf);
-        };
-        do_for_partitions(transport, images[i].part_name, slot, flashall, false);
-    }
-
-    if (slot_override == "all") {
-        set_active(transport, "a");
-    } else {
-        set_active(transport, slot_override);
-    }
+static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe) {
+    FlashAllTool tool(LocalImageSource(), slot_override, skip_secondary, wipe);
+    tool.Flash();
 }
 
 static std::string next_arg(std::vector<std::string>* args) {
@@ -1306,18 +1425,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");
 
@@ -1325,48 +1432,7 @@
     while (!args->empty()) {
         command += " " + next_arg(args);
     }
-    fb_queue_command(command, "");
-}
-
-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->RawCommand(command, "");
 }
 
 static std::string fb_fix_numeric_var(std::string var) {
@@ -1378,9 +1444,9 @@
     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, &sizeString) || sizeString.empty()) {
+    if (fb->GetVar(name, &sizeString) != fastboot::SUCCESS || sizeString.empty()) {
         // This device does not report flash block sizes, so return 0.
         return 0;
     }
@@ -1398,7 +1464,7 @@
     return size;
 }
 
-static void fb_perform_format(Transport* transport,
+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) {
@@ -1418,7 +1484,7 @@
         limit = sparse_limit;
     }
 
-    if (!fb_getvar(transport, "partition-type:" + partition, &partition_type)) {
+    if (fb->GetVar("partition-type:" + partition, &partition_type) != fastboot::SUCCESS) {
         errMsg = "Can't determine partition type.\n";
         goto failed;
     }
@@ -1430,7 +1496,7 @@
         partition_type = type_override;
     }
 
-    if (!fb_getvar(transport, "partition-size:" + partition, &partition_size)) {
+    if (fb->GetVar("partition-size:" + partition, &partition_size) != fastboot::SUCCESS) {
         errMsg = "Unable to get partition size\n";
         goto failed;
     }
@@ -1450,35 +1516,30 @@
             fprintf(stderr, "File system type %s not supported.\n", partition_type.c_str());
             return;
         }
-        fprintf(stderr, "Formatting is not supported for file system with type '%s'.\n",
-                partition_type.c_str());
-        return;
+        die("Formatting is not supported for file system with type '%s'.",
+            partition_type.c_str());
     }
 
     int64_t size;
     if (!android::base::ParseInt(partition_size, &size)) {
-        fprintf(stderr, "Couldn't parse partition size '%s'.\n", partition_size.c_str());
-        return;
+        die("Couldn't parse partition size '%s'.", partition_size.c_str());
     }
 
     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)) {
         die("Cannot generate image for %s", partition.c_str());
-        return;
     }
 
     fd.reset(open(output.path, O_RDONLY));
     if (fd == -1) {
-        fprintf(stderr, "Cannot open generated image: %s\n", strerror(errno));
-        return;
+        die("Cannot open generated image: %s", strerror(errno));
     }
-    if (!load_buf_fd(transport, fd.release(), &buf)) {
-        fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
-        return;
+    if (!load_buf_fd(fd.release(), &buf)) {
+        die("Cannot read image: %s", strerror(errno));
     }
     flash_buf(partition, &buf);
     return;
@@ -1488,48 +1549,155 @@
         fprintf(stderr, "Erase successful, but not automatically formatting.\n");
         if (errMsg) fprintf(stderr, "%s", errMsg);
     }
-    fprintf(stderr, "FAILED (%s)\n", fb_get_error().c_str());
+    fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
+    if (!skip_if_not_supported) {
+        die("Command failed");
+    }
 }
 
-int main(int argc, char **argv)
-{
+static bool should_flash_in_userspace(const std::string& partition_name) {
+    if (!get_android_product_out()) {
+        return false;
+    }
+    auto path = find_item_given_name("super_empty.img");
+    if (path.empty() || access(path.c_str(), R_OK)) {
+        return false;
+    }
+    auto metadata = android::fs_mgr::ReadFromImageFile(path);
+    if (!metadata) {
+        return false;
+    }
+    for (const auto& partition : metadata->partitions) {
+        auto candidate = android::fs_mgr::GetPartitionName(partition);
+        if (partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED) {
+            // On retrofit devices, we don't know if, or whether, the A or B
+            // slot has been flashed for dynamic partitions. Instead we add
+            // both names to the list as a conservative guess.
+            if (candidate + "_a" == partition_name || candidate + "_b" == partition_name) {
+                return true;
+            }
+        } else if (candidate == partition_name) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static bool wipe_super(const android::fs_mgr::LpMetadata& metadata, const std::string& slot,
+                       std::string* message) {
+    auto super_device = GetMetadataSuperBlockDevice(metadata);
+    auto block_size = metadata.geometry.logical_block_size;
+    auto super_bdev_name = android::fs_mgr::GetBlockDevicePartitionName(*super_device);
+
+    if (super_bdev_name != "super") {
+        // retrofit devices do not allow flashing to the retrofit partitions,
+        // so enable it if we can.
+        fb->RawCommand("oem allow-flash-super");
+    }
+
+    // Note: do not use die() in here, since we want TemporaryDir's destructor
+    // to be called.
+    TemporaryDir temp_dir;
+
+    bool ok;
+    if (metadata.block_devices.size() > 1) {
+        ok = WriteSplitImageFiles(temp_dir.path, metadata, block_size, {}, true);
+    } else {
+        auto image_path = temp_dir.path + "/"s + super_bdev_name + ".img";
+        ok = WriteToImageFile(image_path, metadata, block_size, {}, true);
+    }
+    if (!ok) {
+        *message = "Could not generate a flashable super image file";
+        return false;
+    }
+
+    for (const auto& block_device : metadata.block_devices) {
+        auto partition = android::fs_mgr::GetBlockDevicePartitionName(block_device);
+        bool force_slot = !!(block_device.flags & LP_BLOCK_DEVICE_SLOT_SUFFIXED);
+
+        std::string image_name;
+        if (metadata.block_devices.size() > 1) {
+            image_name = "super_" + partition + ".img";
+        } else {
+            image_name = partition + ".img";
+        }
+
+        auto image_path = temp_dir.path + "/"s + image_name;
+        auto flash = [&](const std::string& partition_name) {
+            do_flash(partition_name.c_str(), image_path.c_str());
+        };
+        do_for_partitions(partition, slot, flash, force_slot);
+
+        unlink(image_path.c_str());
+    }
+    return true;
+}
+
+static void do_wipe_super(const std::string& image, const std::string& slot_override) {
+    if (access(image.c_str(), R_OK) != 0) {
+        die("Could not read image: %s", image.c_str());
+    }
+    auto metadata = android::fs_mgr::ReadFromImageFile(image);
+    if (!metadata) {
+        die("Could not parse image: %s", image.c_str());
+    }
+
+    auto slot = slot_override;
+    if (slot.empty()) {
+        slot = get_current_slot();
+    }
+
+    std::string message;
+    if (!wipe_super(*metadata.get(), slot, &message)) {
+        die(message);
+    }
+}
+
+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 wants_reboot_recovery = false;
+    bool wants_reboot_fastboot = 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;
-    uint32_t header_version = 0;
-    int64_t sz;
+    bool force_flash = false;
     int longindex;
     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;
+    g_boot_img_hdr.dtb_addr = 0x01100000;
+
     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'},
-        {"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'},
-        {"set-active", optional_argument, 0, 'a'},
-        {"skip-secondary", no_argument, 0, 0},
-        {"skip-reboot", no_argument, 0, 0},
-        {"disable-verity", no_argument, 0, 0},
+        {"base", required_argument, 0, 0},
+        {"cmdline", required_argument, 0, 0},
         {"disable-verification", no_argument, 0, 0},
+        {"disable-verity", no_argument, 0, 0},
+        {"force", no_argument, 0, 0},
         {"header-version", required_argument, 0, 0},
+        {"help", no_argument, 0, 'h'},
+        {"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-reboot", no_argument, 0, 0},
+        {"skip-secondary", no_argument, 0, 0},
+        {"slot", required_argument, 0, 0},
+        {"tags-offset", required_argument, 0, 0},
+        {"dtb", required_argument, 0, 0},
+        {"dtb-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
@@ -1538,100 +1706,90 @@
 
     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 == "force") {
+                force_flash = true;
+            } else if (name == "header-version") {
+                g_boot_img_hdr.header_version = strtoul(optarg, nullptr, 0);
+            } else if (name == "dtb") {
+                g_dtb_path = optarg;
+            } 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 == "dtb-offset") {
+                g_boot_img_hdr.dtb_addr = strtoul(optarg, 0, 16);
+            } 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) {
-                fprintf(stdout, "fastboot version %s\n", FASTBOOT_VERSION);
+            } else if (name == "version") {
+                fprintf(stdout, "fastboot version %s-%s\n", PLATFORM_TOOLS_VERSION, android::build::GetBuildNumber().c_str());
                 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;
-            } else if (strcmp("disable-verity", longopts[longindex].name) == 0 ) {
-                g_disable_verity = true;
-            } else if (strcmp("disable-verification", longopts[longindex].name) == 0 ) {
-                g_disable_verification = 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 if (strcmp("header-version", longopts[longindex].name) == 0) {
-                header_version = strtoul(optarg, nullptr, 0);
             } 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();
+            }
         }
     }
 
@@ -1653,26 +1811,30 @@
     if (transport == nullptr) {
         return 1;
     }
+    fastboot::DriverCallbacks driver_callbacks = {
+        .prolog = Status,
+        .epilog = Epilog,
+        .info = InfoMessage,
+    };
+    fastboot::FastBootDriver fastboot_driver(transport, driver_callbacks, false);
+    fb = &fastboot_driver;
 
     const double start = now();
 
-    if (!supports_AB(transport) && supports_AB_obsolete(transport)) {
-        fprintf(stderr, "Warning: Device A/B support is outdated. Bootloader update required.\n");
-    }
-    if (slot_override != "") slot_override = verify_slot(transport, slot_override);
-    if (next_active != "") next_active = verify_slot(transport, next_active, false);
+    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) == fastboot::SUCCESS) {
+                    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);
             }
         }
     }
@@ -1681,23 +1843,22 @@
     while (!args.empty()) {
         std::string command = next_arg(&args);
 
-        if (command == "getvar") {
+        if (command == FB_CMD_GETVAR) {
             std::string variable = next_arg(&args);
-            fb_queue_display(variable, variable);
-        } else if (command == "erase") {
+            DisplayVarOrError(variable, variable);
+        } else if (command == FB_CMD_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,
-                              &partition_type) &&
+                if (fb->GetVar("partition-type:" + partition, &partition_type) == fastboot::SUCCESS &&
                     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);
+                fb->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:
@@ -1715,20 +1876,19 @@
             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);
-                }
-                fb_perform_format(transport, partition, 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, 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 (got %" PRId64 ")", sz);
-            fb_queue_download("signature", data, sz);
-            fb_queue_command("signature", "installing signature");
-        } else if (command == "reboot") {
+            std::vector<char> data;
+            if (!ReadFileToVector(filename, &data)) {
+                die("could not load '%s': %s", filename.c_str(), strerror(errno));
+            }
+            if (data.size() != 256) die("signature must be 256 bytes (got %zu)", data.size());
+            fb->Download("signature", data);
+            fb->RawCommand("signature", "installing signature");
+        } else if (command == FB_CMD_REBOOT) {
             wants_reboot = true;
 
             if (args.size() == 1) {
@@ -1736,30 +1896,36 @@
                 if (what == "bootloader") {
                     wants_reboot = false;
                     wants_reboot_bootloader = true;
-                } else if (what == "emergency") {
+                } else if (what == "recovery") {
                     wants_reboot = false;
-                    wants_reboot_emergency = true;
+                    wants_reboot_recovery = true;
+                } else if (what == "fastboot") {
+                    wants_reboot = false;
+                    wants_reboot_fastboot = true;
                 } else {
                     syntax_error("unknown reboot target %s", what.c_str());
                 }
 
             }
             if (!args.empty()) syntax_error("junk after reboot command");
-        } else if (command == "reboot-bootloader") {
+        } else if (command == FB_CMD_REBOOT_BOOTLOADER) {
             wants_reboot_bootloader = true;
-        } else if (command == "continue") {
-            fb_queue_command("continue", "resuming boot");
-        } else if (command == "boot") {
+        } else if (command == FB_CMD_REBOOT_RECOVERY) {
+            wants_reboot_recovery = true;
+        } else if (command == FB_CMD_REBOOT_FASTBOOT) {
+            wants_reboot_fastboot = true;
+        } else if (command == FB_CMD_CONTINUE) {
+            fb->Continue();
+        } else if (command == FB_CMD_BOOT) {
             std::string kernel = next_arg(&args);
             std::string ramdisk;
             if (!args.empty()) ramdisk = next_arg(&args);
             std::string second_stage;
             if (!args.empty()) second_stage = next_arg(&args);
-
-            data = load_bootable_image(kernel, ramdisk, second_stage, &sz, cmdline, header_version);
-            fb_queue_download("boot.img", data, sz);
-            fb_queue_command("boot", "booting");
-        } else if (command == "flash") {
+            auto data = LoadBootableImage(kernel, ramdisk, second_stage);
+            fb->Download("boot.img", data);
+            fb->Boot();
+        } else if (command == FB_CMD_FLASH) {
             std::string pname = next_arg(&args);
 
             std::string fname;
@@ -1771,12 +1937,19 @@
             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);
+                if (should_flash_in_userspace(partition) && !is_userspace_fastboot() &&
+                    !force_flash) {
+                    die("The partition you are trying to flash is dynamic, and "
+                        "should be flashed via fastbootd. Please run:\n"
+                        "\n"
+                        "    fastboot reboot fastboot\n"
+                        "\n"
+                        "And try again. If you are intentionally trying to "
+                        "overwrite a fixed partition, use --force.");
                 }
-                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, slot_override, flash, true);
         } else if (command == "flash:raw") {
             std::string partition = next_arg(&args);
             std::string kernel = next_arg(&args);
@@ -1785,17 +1958,17 @@
             std::string second_stage;
             if (!args.empty()) second_stage = next_arg(&args);
 
-            data = load_bootable_image(kernel, ramdisk, second_stage, &sz, cmdline, header_version);
-            auto flashraw = [&](const std::string& partition) {
-                fb_queue_flash(partition, data, sz);
+            auto data = LoadBootableImage(kernel, ramdisk, second_stage);
+            auto flashraw = [&data](const std::string& partition) {
+                fb->FlashPartition(partition, data);
             };
-            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, wants_wipe);
             } else {
-                do_flashall(transport, slot_override, erase_first, skip_secondary);
+                do_flashall(slot_override, skip_secondary, wants_wipe);
             }
             wants_reboot = true;
         } else if (command == "update") {
@@ -1807,49 +1980,63 @@
             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);
+        } else if (command == FB_CMD_SET_ACTIVE) {
+            std::string slot = verify_slot(next_arg(&args), false);
+            fb->SetActive(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, buf.fd, buf.sz);
+            fb->Download(filename, buf.fd, buf.sz);
         } else if (command == "get_staged") {
             std::string filename = next_arg(&args);
-            fb_queue_upload(filename);
-        } else if (command == "oem") {
-            do_oem_command("oem", &args);
+            fb->Upload(filename);
+        } else if (command == FB_CMD_OEM) {
+            do_oem_command(FB_CMD_OEM, &args);
         } else if (command == "flashing") {
             if (args.empty()) {
                 syntax_error("missing 'flashing' command");
             } 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());
             }
+        } else if (command == FB_CMD_CREATE_PARTITION) {
+            std::string partition = next_arg(&args);
+            std::string size = next_arg(&args);
+            fb->CreatePartition(partition, size);
+        } else if (command == FB_CMD_DELETE_PARTITION) {
+            std::string partition = next_arg(&args);
+            fb->DeletePartition(partition);
+        } else if (command == FB_CMD_RESIZE_PARTITION) {
+            std::string partition = next_arg(&args);
+            std::string size = next_arg(&args);
+            fb->ResizePartition(partition, size);
+        } else if (command == "gsi") {
+            std::string arg = next_arg(&args);
+            if (arg == "wipe") {
+                fb->RawCommand("gsi:wipe", "wiping GSI");
+            } else if (arg == "disable") {
+                fb->RawCommand("gsi:disable", "disabling GSI");
+            } else {
+                syntax_error("expected 'wipe' or 'disable'");
+            }
+        } else if (command == "wipe-super") {
+            std::string image;
+            if (args.empty()) {
+                image = find_item_given_name("super_empty.img");
+            } else {
+                image = next_arg(&args);
+            }
+            do_wipe_super(image, slot_override);
         } else {
             syntax_error("unknown command %s", command.c_str());
         }
@@ -1859,34 +2046,64 @@
         std::vector<std::string> partitions = { "userdata", "cache", "metadata" };
         for (const auto& partition : partitions) {
             std::string partition_type;
-            if (!fb_getvar(transport, std::string{"partition-type:"} + partition, &partition_type)) continue;
+            if (fb->GetVar("partition-type:" + partition, &partition_type) != fastboot::SUCCESS) {
+                continue;
+            }
             if (partition_type.empty()) continue;
-            fb_queue_erase(partition);
+            fb->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(transport, partition, 1, "", "", initial_userdata_dir);
+                fb_perform_format(partition, 1, "", "", initial_userdata_dir);
                 delete_fbemarker_tmpdir(initial_userdata_dir);
             } else {
-                fb_perform_format(transport, partition, 1, "", "", "");
+                fb_perform_format(partition, 1, "", "", "");
             }
         }
     }
     if (wants_set_active) {
-        fb_set_active(next_active);
+        fb->SetActive(next_active);
     }
     if (wants_reboot && !skip_reboot) {
-        fb_queue_reboot();
-        fb_queue_wait_for_disconnect();
+        fb->Reboot();
+        fb->WaitForDisconnect();
     } 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();
+        fb->RebootTo("bootloader");
+        fb->WaitForDisconnect();
+    } else if (wants_reboot_recovery) {
+        fb->RebootTo("recovery");
+        fb->WaitForDisconnect();
+    } else if (wants_reboot_fastboot) {
+        reboot_to_userspace_fastboot();
     }
 
-    int status = fb_execute_queue(transport) ? EXIT_FAILURE : EXIT_SUCCESS;
     fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start));
-    return status;
+
+    auto* old_transport = fb->set_transport(nullptr);
+    delete old_transport;
+
+    return 0;
+}
+
+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
index a31057a..9f18253 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,73 +26,12 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _FASTBOOT_H_
-#define _FASTBOOT_H_
+#include <bootimg.h>
 
-#include <inttypes.h>
-#include <stdlib.h>
+class FastBootTool {
+  public:
+    int Main(int argc, char* argv[]);
 
-#include <string>
-
-#include "transport.h"
-
-struct sparse_file;
-
-/* protocol.c - fastboot protocol */
-int fb_command(Transport* transport, const std::string& cmd);
-int fb_command_response(Transport* transport, const std::string& cmd, char* response);
-int64_t fb_download_data(Transport* transport, const void* data, uint32_t size);
-int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size);
-int fb_download_data_sparse(Transport* transport, struct sparse_file* s);
-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 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(Transport* transport);
-void fb_set_active(const std::string& slot);
-
-/* util stuff */
-double now();
-char* xstrdup(const char*);
-
-// These printf-like functions are implemented in terms of vsnprintf, so they
-// use the same attribute for compile-time format string checking. On Windows,
-// 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 FASTBOOT_FORMAT_ARCHETYPE __printf__
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-#undef FASTBOOT_FORMAT_ARCHETYPE
-#define FASTBOOT_FORMAT_ARCHETYPE gnu_printf
-#endif
-#endif
-void die(const char* fmt, ...) __attribute__((__noreturn__))
-__attribute__((__format__(FASTBOOT_FORMAT_ARCHETYPE, 1, 2)));
-#undef FASTBOOT_FORMAT_ARCHETYPE
-
-/* Current product */
-extern char cur_product[FB_RESPONSE_SZ + 1];
-
-#endif
+    void ParseOsPatchLevel(boot_img_hdr_v1*, const char*);
+    void ParseOsVersion(boot_img_hdr_v1*, const char*);
+};
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
new file mode 100644
index 0000000..fea0a77
--- /dev/null
+++ b/fastboot/fastboot_driver.cpp
@@ -0,0 +1,576 @@
+/*
+ * 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/mapped_file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include "constants.h"
+#include "transport.h"
+
+using android::base::StringPrintf;
+
+namespace fastboot {
+
+/*************************** PUBLIC *******************************/
+FastBootDriver::FastBootDriver(Transport* transport, DriverCallbacks driver_callbacks,
+                               bool no_checks)
+    : transport_(transport),
+      prolog_(std::move(driver_callbacks.prolog)),
+      epilog_(std::move(driver_callbacks.epilog)),
+      info_(std::move(driver_callbacks.info)),
+      disable_checks_(no_checks) {}
+
+FastBootDriver::~FastBootDriver() {
+}
+
+RetCode FastBootDriver::Boot(std::string* response, std::vector<std::string>* info) {
+    return RawCommand(FB_CMD_BOOT, "Booting", response, info);
+}
+
+RetCode FastBootDriver::Continue(std::string* response, std::vector<std::string>* info) {
+    return RawCommand(FB_CMD_CONTINUE, "Resuming boot", response, info);
+}
+
+RetCode FastBootDriver::CreatePartition(const std::string& partition, const std::string& size) {
+    return RawCommand(FB_CMD_CREATE_PARTITION ":" + partition + ":" + size,
+                      "Creating '" + partition + "'");
+}
+
+RetCode FastBootDriver::DeletePartition(const std::string& partition) {
+    return RawCommand(FB_CMD_DELETE_PARTITION ":" + partition, "Deleting '" + partition + "'");
+}
+
+RetCode FastBootDriver::Erase(const std::string& partition, std::string* response,
+                              std::vector<std::string>* info) {
+    return RawCommand(FB_CMD_ERASE ":" + partition, "Erasing '" + partition + "'", response, info);
+}
+
+RetCode FastBootDriver::Flash(const std::string& partition, std::string* response,
+                              std::vector<std::string>* info) {
+    return RawCommand(FB_CMD_FLASH ":" + partition, "Writing '" + partition + "'", response, info);
+}
+
+RetCode FastBootDriver::GetVar(const std::string& key, std::string* val,
+                               std::vector<std::string>* info) {
+    return RawCommand(FB_CMD_GETVAR ":" + key, val, info);
+}
+
+RetCode FastBootDriver::GetVarAll(std::vector<std::string>* response) {
+    std::string tmp;
+    return GetVar("all", &tmp, response);
+}
+
+RetCode FastBootDriver::Reboot(std::string* response, std::vector<std::string>* info) {
+    return RawCommand(FB_CMD_REBOOT, "Rebooting", response, info);
+}
+
+RetCode FastBootDriver::RebootTo(std::string target, std::string* response,
+                                 std::vector<std::string>* info) {
+    return RawCommand("reboot-" + target, "Rebooting into " + target, response, info);
+}
+
+RetCode FastBootDriver::ResizePartition(const std::string& partition, const std::string& size) {
+    return RawCommand(FB_CMD_RESIZE_PARTITION ":" + partition + ":" + size,
+                      "Resizing '" + partition + "'");
+}
+
+RetCode FastBootDriver::SetActive(const std::string& slot, std::string* response,
+                                  std::vector<std::string>* info) {
+    return RawCommand(FB_CMD_SET_ACTIVE ":" + slot, "Setting current slot to '" + slot + "'",
+                      response, info);
+}
+
+RetCode FastBootDriver::FlashPartition(const std::string& partition,
+                                       const std::vector<char>& data) {
+    RetCode ret;
+    if ((ret = Download(partition, data))) {
+        return ret;
+    }
+    return Flash(partition);
+}
+
+RetCode FastBootDriver::FlashPartition(const std::string& partition, int fd, uint32_t size) {
+    RetCode ret;
+    if ((ret = Download(partition, fd, size))) {
+        return ret;
+    }
+    return Flash(partition);
+}
+
+RetCode FastBootDriver::FlashPartition(const std::string& partition, sparse_file* s, uint32_t size,
+                                       size_t current, size_t total) {
+    RetCode ret;
+    if ((ret = Download(partition, s, size, current, total, false))) {
+        return ret;
+    }
+    return Flash(partition);
+}
+
+RetCode FastBootDriver::Partitions(std::vector<std::tuple<std::string, uint64_t>>* partitions) {
+    std::vector<std::string> all;
+    RetCode ret;
+    if ((ret = GetVarAll(&all))) {
+        return ret;
+    }
+
+    std::regex reg("partition-size[[:s:]]*:[[:s:]]*([[:w:]]+)[[:s:]]*:[[:s:]]*0x([[:xdigit:]]+)");
+    std::smatch sm;
+
+    for (auto& s : all) {
+        if (std::regex_match(s, sm, reg)) {
+            std::string m1(sm[1]);
+            std::string m2(sm[2]);
+            uint64_t tmp = strtoll(m2.c_str(), 0, 16);
+            partitions->push_back(std::make_tuple(m1, tmp));
+        }
+    }
+    return SUCCESS;
+}
+
+RetCode FastBootDriver::Download(const std::string& name, int fd, size_t size,
+                                 std::string* response, std::vector<std::string>* info) {
+    prolog_(StringPrintf("Sending '%s' (%zu KB)", name.c_str(), size / 1024));
+    auto result = Download(fd, size, response, info);
+    epilog_(result);
+    return result;
+}
+
+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::string& name, const std::vector<char>& buf,
+                                 std::string* response, std::vector<std::string>* info) {
+    prolog_(StringPrintf("Sending '%s' (%zu KB)", name.c_str(), buf.size() / 1024));
+    auto result = Download(buf, response, info);
+    epilog_(result);
+    return result;
+}
+
+RetCode FastBootDriver::Download(const std::vector<char>& buf, std::string* response,
+                                 std::vector<std::string>* info) {
+    RetCode ret;
+    error_ = "";
+    if ((buf.size() == 0 || buf.size() > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
+        error_ = "Buffer is too large or 0 bytes";
+        return BAD_ARG;
+    }
+
+    if ((ret = DownloadCommand(buf.size(), response, info))) {
+        return ret;
+    }
+
+    // Write the buffer
+    if ((ret = SendBuffer(buf))) {
+        return ret;
+    }
+
+    // Wait for response
+    return HandleResponse(response, info);
+}
+
+RetCode FastBootDriver::Download(const std::string& partition, struct sparse_file* s, uint32_t size,
+                                 size_t current, size_t total, bool use_crc, std::string* response,
+                                 std::vector<std::string>* info) {
+    prolog_(StringPrintf("Sending sparse '%s' %zu/%zu (%u KB)", partition.c_str(), current, total,
+                         size / 1024));
+    auto result = Download(s, use_crc, response, info);
+    epilog_(result);
+    return result;
+}
+
+RetCode FastBootDriver::Download(sparse_file* s, bool use_crc, std::string* response,
+                                 std::vector<std::string>* info) {
+    error_ = "";
+    int64_t size = sparse_file_len(s, true, use_crc);
+    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, use_crc, 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) {
+    prolog_("Uploading '" + outfile + "'");
+    auto result = UploadInner(outfile, response, info);
+    epilog_(result);
+    return result;
+}
+
+RetCode FastBootDriver::UploadInner(const std::string& outfile, std::string* response,
+                                    std::vector<std::string>* info) {
+    RetCode ret;
+    int dsize;
+    if ((ret = RawCommand(FB_CMD_UPLOAD, response, info, &dsize))) {
+        error_ = "Upload request failed: " + error_;
+        return ret;
+    }
+
+    if (!dsize) {
+        error_ = "Upload request failed, device reports 0 bytes available";
+        return BAD_DEV_RESP;
+    }
+
+    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(const std::string&)> info) {
+    info_ = 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, const std::string& message,
+                                   std::string* response, std::vector<std::string>* info,
+                                   int* dsize) {
+    prolog_(message);
+    auto result = RawCommand(cmd, response, info, dsize);
+    epilog_(result);
+    return result;
+}
+
+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, FB_CMD_DOWNLOAD, 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::steady_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::steady_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_(tmp);
+            add_info(std::move(tmp));
+            // We may receive one or more INFO packets during long operations,
+            // e.g. flash/erase if they are back by slow media like NAND/NOR
+            // flash. In that case, reset the timer since it's not a real
+            // timeout.
+            start = std::chrono::steady_clock::now();
+        } 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));
+}
+
+/******************************* 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
+        size_t len = std::min(remaining, MAX_MAP_SIZE);
+        auto mapping{android::base::MappedFile::FromFd(fd, offset, len, PROT_READ)};
+        if (!mapping) {
+            error_ = "Creating filemap failed";
+            return IO_ERROR;
+        }
+
+        if ((ret = SendBuffer(mapping->data(), mapping->size()))) {
+            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) {
+    // ioctl on 0-length buffer causes freezing
+    if (!size) {
+        return BAD_ARG;
+    }
+    // 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 (nbytes && SendBuffer(data + total, nbytes)) {  // Don't send a ZLP
+        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;
+}
+
+Transport* FastBootDriver::set_transport(Transport* transport) {
+    std::swap(transport_, transport);
+    return transport;
+}
+
+}  // End namespace fastboot
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
new file mode 100644
index 0000000..af02637
--- /dev/null
+++ b/fastboot/fastboot_driver.h
@@ -0,0 +1,165 @@
+/*
+ * 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 "constants.h"
+#include "transport.h"
+
+class Transport;
+
+namespace fastboot {
+
+enum RetCode : int {
+    SUCCESS = 0,
+    BAD_ARG,
+    IO_ERROR,
+    BAD_DEV_RESP,
+    DEVICE_FAIL,
+    TIMEOUT,
+};
+
+struct DriverCallbacks {
+    std::function<void(const std::string&)> prolog = [](const std::string&) {};
+    std::function<void(int)> epilog = [](int) {};
+    std::function<void(const std::string&)> info = [](const std::string&) {};
+};
+
+class FastBootDriver {
+    friend class FastBootTest;
+
+  public:
+    static constexpr int RESP_TIMEOUT = 30;  // 30 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, DriverCallbacks driver_callbacks = {},
+                   bool no_checks = false);
+    ~FastBootDriver();
+
+    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 CreatePartition(const std::string& partition, const std::string& size);
+    RetCode DeletePartition(const std::string& partition);
+    RetCode Download(const std::string& name, int fd, size_t size, 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::string& name, const std::vector<char>& buf,
+                     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);
+    RetCode Download(const std::string& partition, struct sparse_file* s, uint32_t sz,
+                     size_t current, size_t total, bool use_crc, std::string* response = nullptr,
+                     std::vector<std::string>* info = nullptr);
+    RetCode Download(sparse_file* s, bool use_crc = false, std::string* response = nullptr,
+                     std::vector<std::string>* info = nullptr);
+    RetCode Erase(const std::string& partition, std::string* response = nullptr,
+                  std::vector<std::string>* info = nullptr);
+    RetCode Flash(const std::string& partition, 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 Reboot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
+    RetCode RebootTo(std::string target, std::string* response = nullptr,
+                     std::vector<std::string>* info = nullptr);
+    RetCode ResizePartition(const std::string& partition, const std::string& size);
+    RetCode SetActive(const std::string& slot, 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);
+
+    /* HIGHER LEVEL COMMANDS -- Composed of the commands above */
+    RetCode FlashPartition(const std::string& partition, const std::vector<char>& data);
+    RetCode FlashPartition(const std::string& partition, int fd, uint32_t sz);
+    RetCode FlashPartition(const std::string& partition, sparse_file* s, uint32_t sz,
+                           size_t current, size_t total);
+
+    RetCode Partitions(std::vector<std::tuple<std::string, uint64_t>>* partitions);
+    RetCode Require(const std::string& var, const std::vector<std::string>& allowed, bool* reqmet,
+                    bool invert = false);
+
+    /* HELPERS */
+    void SetInfoCallback(std::function<void(const std::string&)> info);
+    static const std::string RCString(RetCode rc);
+    std::string Error();
+    RetCode WaitForDisconnect();
+
+    // Note: set_transport will return the previous transport.
+    Transport* set_transport(Transport* transport);
+    Transport* transport() const { return transport_; }
+
+    RetCode RawCommand(const std::string& cmd, const std::string& message,
+                       std::string* response = nullptr, std::vector<std::string>* info = nullptr,
+                       int* dsize = nullptr);
+
+    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);
+
+    Transport* 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);
+
+    RetCode UploadInner(const std::string& outfile, std::string* response = nullptr,
+                        std::vector<std::string>* info = nullptr);
+
+    int SparseWriteCallback(std::vector<char>& tpbuf, const char* data, size_t len);
+
+    std::string error_;
+    std::function<void(const std::string&)> prolog_;
+    std::function<void(int)> epilog_;
+    std::function<void(const std::string&)> info_;
+    bool disable_checks_;
+};
+
+}  // namespace fastboot
diff --git a/fastboot/fastboot_test.cpp b/fastboot/fastboot_test.cpp
new file mode 100644
index 0000000..9c3ab6e
--- /dev/null
+++ b/fastboot/fastboot_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 "fastboot.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");
+}
+
+extern bool ParseRequirementLine(const std::string& line, std::string* name, std::string* product,
+                                 bool* invert, std::vector<std::string>* options);
+
+static void ParseRequirementLineTest(const std::string& line, const std::string& expected_name,
+                                     const std::string& expected_product, bool expected_invert,
+                                     const std::vector<std::string>& expected_options) {
+    std::string name;
+    std::string product;
+    bool invert;
+    std::vector<std::string> options;
+
+    EXPECT_TRUE(ParseRequirementLine(line, &name, &product, &invert, &options)) << line;
+
+    EXPECT_EQ(expected_name, name) << line;
+    EXPECT_EQ(expected_product, product) << line;
+    EXPECT_EQ(expected_invert, invert) << line;
+    EXPECT_EQ(expected_options, options) << line;
+}
+
+TEST(FastBoot, ParseRequirementLineSuccesses) {
+    // Examples provided in the code + slight variations.
+    ParseRequirementLineTest("require product=alpha", "product", "", false, {"alpha"});
+    ParseRequirementLineTest("require product=alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require version-bootloader=1234", "version-bootloader", "", false,
+                             {"1234"});
+    ParseRequirementLineTest("require-for-product:gamma version-bootloader=istanbul",
+                             "version-bootloader", "gamma", false, {"istanbul"});
+    ParseRequirementLineTest("require-for-product:gamma version-bootloader=istanbul|constantinople",
+                             "version-bootloader", "gamma", false, {"istanbul", "constantinople"});
+    ParseRequirementLineTest("require partition-exists=vendor", "partition-exists", "", false,
+                             {"vendor"});
+    ParseRequirementLineTest("reject product=alpha", "product", "", true, {"alpha"});
+    ParseRequirementLineTest("reject product=alpha|beta|gamma", "product", "", true,
+                             {"alpha", "beta", "gamma"});
+
+    // Without any prefix, assume 'require'
+    ParseRequirementLineTest("product=alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    // Including if the variable name is otherwise a prefix keyword
+    ParseRequirementLineTest("require = alpha", "require", "", false, {"alpha"});
+    ParseRequirementLineTest("reject = alpha", "reject", "", false, {"alpha"});
+    ParseRequirementLineTest("require-for-product:gamma = alpha", "require-for-product:gamma", "",
+                             false, {"alpha"});
+
+    // Extra spaces are allowed.
+    ParseRequirementLineTest("require    product=alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product    =alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=   alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product   =   alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha  |beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha|  beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha  |  beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha|beta|gamma   ", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("product  =  alpha  |  beta  |  gamma   ", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require-for-product:  gamma version-bootloader=istanbul",
+                             "version-bootloader", "gamma", false, {"istanbul"});
+
+    // Extraneous ending | is okay, implies accepting an empty string.
+    ParseRequirementLineTest("require product=alpha|", "product", "", false, {"alpha", ""});
+    ParseRequirementLineTest("require product=alpha|beta|gamma|", "product", "", false,
+                             {"alpha", "beta", "gamma", ""});
+
+    // Accept empty options, double ||, etc, implies accepting an empty string.
+    ParseRequirementLineTest("require product=alpha||beta|   |gamma", "product", "", false,
+                             {"alpha", "", "beta", "", "gamma"});
+    ParseRequirementLineTest("require product=alpha||beta|gamma", "product", "", false,
+                             {"alpha", "", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha|beta|   |gamma", "product", "", false,
+                             {"alpha", "beta", "", "gamma"});
+    ParseRequirementLineTest("require product=alpha||", "product", "", false, {"alpha", "", ""});
+    ParseRequirementLineTest("require product=alpha|| ", "product", "", false, {"alpha", "", ""});
+    ParseRequirementLineTest("require product=alpha| ", "product", "", false, {"alpha", ""});
+    ParseRequirementLineTest("require product=alpha|beta| ", "product", "", false,
+                             {"alpha", "beta", ""});
+
+    // No option string is also treating as accepting an empty string.
+    ParseRequirementLineTest("require =", "require", "", false, {""});
+    ParseRequirementLineTest("require = |", "require", "", false, {"", ""});
+    ParseRequirementLineTest("reject =", "reject", "", false, {""});
+    ParseRequirementLineTest("reject = |", "reject", "", false, {"", ""});
+    ParseRequirementLineTest("require-for-product: =", "require-for-product:", "", false, {""});
+    ParseRequirementLineTest("require-for-product: = | ", "require-for-product:", "", false,
+                             {"", ""});
+    ParseRequirementLineTest("require product=", "product", "", false, {""});
+    ParseRequirementLineTest("require product = ", "product", "", false, {""});
+    ParseRequirementLineTest("require product = | ", "product", "", false, {"", ""});
+    ParseRequirementLineTest("reject product=", "product", "", true, {""});
+    ParseRequirementLineTest("reject product = ", "product", "", true, {""});
+    ParseRequirementLineTest("reject product = | ", "product", "", true, {"", ""});
+    ParseRequirementLineTest("require-for-product:gamma product=", "product", "gamma", false, {""});
+    ParseRequirementLineTest("require-for-product:gamma product = ", "product", "gamma", false,
+                             {""});
+    ParseRequirementLineTest("require-for-product:gamma product = |", "product", "gamma", false,
+                             {"", ""});
+
+    // Check for board -> product substitution.
+    ParseRequirementLineTest("require board=alpha", "product", "", false, {"alpha"});
+    ParseRequirementLineTest("board=alpha", "product", "", false, {"alpha"});
+}
+
+static void ParseRequirementLineTestMalformed(const std::string& line) {
+    std::string name;
+    std::string product;
+    bool invert;
+    std::vector<std::string> options;
+
+    EXPECT_FALSE(ParseRequirementLine(line, &name, &product, &invert, &options)) << line;
+}
+
+TEST(FastBoot, ParseRequirementLineMalformed) {
+    ParseRequirementLineTestMalformed("nothing");
+    ParseRequirementLineTestMalformed("");
+    ParseRequirementLineTestMalformed("=");
+    ParseRequirementLineTestMalformed("|");
+
+    ParseRequirementLineTestMalformed("require");
+    ParseRequirementLineTestMalformed("require ");
+    ParseRequirementLineTestMalformed("reject");
+    ParseRequirementLineTestMalformed("reject ");
+    ParseRequirementLineTestMalformed("require-for-product:");
+    ParseRequirementLineTestMalformed("require-for-product: ");
+
+    ParseRequirementLineTestMalformed("require product");
+    ParseRequirementLineTestMalformed("reject product");
+
+    ParseRequirementLineTestMalformed("require-for-product:gamma");
+    ParseRequirementLineTestMalformed("require-for-product:gamma product");
+
+    // No spaces allowed before between require-for-product and :.
+    ParseRequirementLineTestMalformed("require-for-product :");
+}
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index c30ca1e..8c0aa6b 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -1,6 +1,5 @@
 #include "fs.h"
 
-#include "fastboot.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -9,7 +8,7 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#ifndef WIN32
+#ifndef _WIN32
 #include <sys/wait.h>
 #else
 #include <tchar.h>
@@ -20,6 +19,7 @@
 
 #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>
 
@@ -27,7 +27,7 @@
 using android::base::StringPrintf;
 using android::base::unique_fd;
 
-#ifdef WIN32
+#ifdef _WIN32
 static int exec_cmd(const char* path, const char** argv, const char** envp) {
     std::string cmd;
     int i = 0;
@@ -172,13 +172,8 @@
     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("-O");
-    mkf2fs_args.push_back("verity");
+    mkf2fs_args.push_back("-g");
+    mkf2fs_args.push_back("android");
     mkf2fs_args.push_back(fileName);
     mkf2fs_args.push_back(nullptr);
 
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/fuzzy_fastboot/Android.bp b/fastboot/fuzzy_fastboot/Android.bp
new file mode 100644
index 0000000..277cc3a
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/Android.bp
@@ -0,0 +1,44 @@
+cc_test_host {
+  name: "fuzzy_fastboot",
+  compile_multilib: "first",
+
+  srcs: [
+    "main.cpp",
+    "extensions.cpp",
+    "usb_transport_sniffer.cpp",
+    "fixtures.cpp",
+    "test_utils.cpp",
+  ],
+
+  static_libs: [
+    "libfastboot2",
+    "libziparchive",
+    "libsparse",
+    "libutils",
+    "liblog",
+    "libz",
+    "libdiagnose_usb",
+    "libbase",
+    "libcutils",
+    "libgtest",
+    "libgtest_main",
+    "libbase",
+    "libadb_host",
+    "libtinyxml2",
+    "libsparse",
+    "liblp",
+    "libcrypto",
+    "libext4_utils",
+  ],
+
+  // Static libs (libfastboot2) shared library dependencies are not transitively included
+  // This is needed to avoid link time errors when building for mac
+  target: {
+    darwin: {
+      host_ldlibs: [
+          "-framework CoreFoundation",
+          "-framework IOKit",
+      ],
+    },
+  }
+}
diff --git a/fastboot/fuzzy_fastboot/README.md b/fastboot/fuzzy_fastboot/README.md
new file mode 100644
index 0000000..72967c5
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/README.md
@@ -0,0 +1,394 @@
+# Fuzzy Fastboot
+
+Fuzzy Fastboot (FF) is a standalone automated conformance and penetration tester for
+validating device-side fastboot protocol implementations.
+The tool is completely generic, and uses a simple extensible XML
+configuration file to auto-generate device-specific tests for any device.
+Any Android device that uses the fastboot protocol should have fuzzy fastboot run on it prior to
+release to find implementation bugs, make sure it conforms to the fastboot spec,
+and that it safely handles malicious inputs.
+
+
+## Background
+The [fastboot protocol](../README.md) provides an easy way to manage low level
+aspects of the device directly from bootloader. However, with great power comes
+great responsibility. An improper or insecure fastboot implementation can
+open the possibility for critical security exploits on the bootloader via fastboot
+commands. Furthermore, an untrustworthy or insecure bootloader means nothing that is
+either directly or indirectly bootstrapped by the bootloader can be trusted (including Android).
+By checking a bootloader's conformance to the fastboot spec, as well as make sure
+nefarious/malformed input is properly and gracefully handled, easy exploits of a
+device's bootloaders can be mitigated.
+
+Additionally, since the fastboot tool itself must support a myriad of fastboot
+implementations, it is important to make sure an implementation is conforming to
+avoid potential incompatibilities with the fastboot command line tool itself.
+Thus, Fuzzy Fastboot also checks for proper conformance to the spec.
+
+## Overview
+Fuzzy Fastboot is written in C++ and uses [Google Test](https://github.com/google/googletest)
+for the underlying test framework. This means that Fuzzy Fastboot supports all of
+gtest's command line flags and options.
+
+Additionally, by using gtest it makes it extremely easy to add additional C++ based
+tests to Fuzzy Fastboot. However, in most cases the optional device specific
+XML configuration file that is provided to Fuzzy Fastboot supports the necessary
+features and hooks for testing device specific commands/features
+without touching the underlying C++.
+
+### Generic Tests
+Without a provided device XML configuration, Fuzzy Fastboot can only perform
+some basic tests that are generic to any fastboot device. These generic tests are
+divided into several test suite categories:
+
+1. **USBFunctionality** - Test USB communication
+2. **Conformance** - Test the device properly handles well-formed fastboot commands
+3. **UnlockPermissions** - Test commands only allowed in the unlocked state work
+4. **LockPermissions** - Test commands only not allowed in the locked state are rejected
+5. **Fuzz** - Test malicious and/or ill-formed commands are properly and gracefully handled
+
+
+### XML Generated Tests
+With a provided XML device configuration, Fuzzy Fastboot will be able to generate
+many more additional tests cases.
+
+The device config XML has five element pairs all inside a root level `<config>`:
+
+#### `<getvar>` Element
+Inside the `<getvar></getvar>` element pairs, one should list all the device's getvar
+variables, with an associated ECMAScript regex you wish the returned variable to match on.
+Each tested variable should appear in a `<var key="key" assert="regex"/>` format.
+For example:
+```xml
+<getvar>
+  <var key="product" assert="superphone2000"/>
+  <var key="secure" assert="no|yes"/>
+  <var key="battery-voltage" assert="[34][[:digit:]]{3}"/>
+  <!-- etc...  -->
+</getvar>
+```
+
+#### `<partitions>` Element
+Inside the `<partitions></partitions>` element pairs, one should list all the device's
+partitions. Each device partition has should be put inside a `<part/>` element.
+The `<part/>` element supports the following attributes:
+
+
+| Attribute | Value          | Purpose                                                                                     | Default  |
+|-----------|----------------|---------------------------------------------------------------------------------------------|----------|
+| value     | Partition name | The name of the partition                                                                   | Required |
+| slots     | "yes" or "no"  | Is this partition is slotted                                                                | "no"     |
+| test      | "yes" or "no"  | Is Fuzzy Fastboot is allowed to generate tests that overwrite this partition                | Required |
+| hashable  | "yes" or "no"  | Is this partition hashable with the hash command specified in `<checksum>`                  | "yes"    |
+| parsed    | "yes" or "no"  | Does the bootloader parse this partition, such as look for a header, look for magic, etc... | "no"     |
+
+For example:
+```xml
+<!-- All the device partitions should be listed here -->
+<partitions>
+  <part value="boot" slots="yes" test="yes" hashable="yes" parsed="yes"/>
+  <part value="modem" slots="yes" test="yes" hashable="yes"/>
+  <part value="userdata" slots="no" test="yes" hashable="no"/>
+  <!-- etc...  -->
+</partitions>
+```
+
+#### `<packed>` Element
+Most devices have pseudo partitions, such as a `bootloader` partition,
+that in reality is composed of several real partitions.
+When one of these pseudo partitions is flashed, the bootloader
+will internally expand the image into the individual images for each underlying
+partition. These pseudo partitions should be listed inside a `<part></part>`
+element pair. Each element `<part>` has a mandatory attribute `value`,
+which lists the name of this pseudo partition, and a `slots` attribute,
+which can be yes or no if this pseudo partition is slotted.
+Additionally, inside the `<part></part>` element pair, one should list
+all the real partition that make up this pseudo partition inside of
+`<child>PART_NAME</child>` element pairs.
+An example is should below:
+
+```xml
+<!-- All the device packed partitions should be listed here -->
+<packed>
+	<part value="bootloader" slots="yes">
+		<!-- We list the real partitions it is composed of -->
+		<child>foo1</child>
+		<child>foo2</child>
+		<child>bar3</child>
+		<!-- We list tests, expect defaults to 'okay' -->
+		<test packed="bootloader.img" unpacked="unpacked"/>
+		<test packed="bootloader_garbage.img" expect="fail"/>
+	</part>
+</packed>
+```
+
+You might notice there are additional `<test/>` elements as well contained inside of
+a `<part></part>` pair. This is because Fuzzy Fastboot allows (and recommends) one to specify
+valid and invalid test packed images for flashing this particular pseudo partition.
+Additionally, one should specify a folder with all the partitions' images
+that the packed image unpacks to. If your device supports hashing partitions, this
+will allow Fuzzy Fastboot to validate the images are unpacking correctly, and
+the correct slots are being flashed.
+
+Each `<test/>` element has the following supported attributes:
+
+| Attribute | Value | Purpose | Default |
+|-----------|---------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------|
+| packed | The name of the packed test image | The image uploaded to the device. It is searched for in dir if --search_path=dir | Required |
+| unpacked | The name of the directory containing the unpacked version of packed | Searched for in dir if --search_path=dir. This folder should have the all the images that packed unpacks to. The name of each of the images should be the name of the real partition it is flashed to. | Required if expect != "fail" |
+| expect | "okay" or "fail" | If uploading a invalid or garbage image the bootloader should reject use "fail" otherwise "okay" | "okay" |
+
+
+#### `<oem>` Element
+Vendors can extend the fastboot protocol with oem commands. This allows vendors
+to support device/vendor specific features/commands over the fastboot protocol.
+Fuzzy Fastboot allows testing these oem commands as well.
+
+Oem commands are specefied in `<command></command>` element pairs. Each command
+element supports the following attributes:
+
+
+| Attribute   | Value                | Purpose                                                       | Default  |
+|-------------|----------------------|---------------------------------------------------------------|----------|
+| value       | The oem command name | Ex: if value="foo", the oem command will start with "oem foo" | Required |
+| permissions | "none" or "unlocked" | Whether the bootloader must be "unlocked" to perform command  | "none"   |
+
+An example is should below:
+```xml
+<oem>
+  <command value="self_destruct" permissions="unlocked">
+    <!-- This will test that "oem self_destruct now" returns 'okay' -->
+    <test value="now" expect="okay"/>
+    <!-- This will test that "oem self_destruct yesterday" returns 'fail' -->
+    <test value="yesterday" expect="fail" />
+  </command>
+
+  <command value="foobar" permissions="unlocked">
+    <!-- FF will first stage test_image.img before running 'oem foobar use_staged' -->
+    <test value="use_staged" expect="okay" input="test_image.img" />
+    <!-- FF will run 'oem foobar send_response', upload data from device, then run the validator script -->
+    <test value="send_response" expect="fail" validate="python validator.py"/>
+  </command>
+<oem/>
+```
+
+Again you will notice that one can, and should, specify tests to run with `<test/>` elements.
+The test elements support the following attributes:
+
+
+| Attribute | Value                                            | Purpose                                                                                                                                                                                                                                                                                                                                                                                                                                            | Default                    |
+|-----------|--------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------|
+| value     | The oem command argument                         | Ex: if value="bar", and the oem command name was "foo", the full command will be "oem foo bar"                                                                                                                                                                                                                                                                                                                                                     | Empty String (no argument) |
+| expect    | "okay" or "fail"                                 | Whether the bootloader should accept or reject this command                                                                                                                                                                                                                                                                                                                                                                                        | "okay"                     |
+| input     | A image filename                                 | Some oem commands require staging files before the command is executed                                                                                                                                                                                                                                                                                                                                                                             | Empty String (no argument) |
+| validate  | A program/script to run to validate the response | Some oem commands will stage data that can be downloaded afterwards and should be validated to be correct. Fuzzy Fastboot will launch the validation program with the first arg the oem command executed, the second arg  the path to the downloaded image. Ex: "python validate.py'. If the program has a non-zero  return code, the validation is marked as failed and anything from the launched programs stderr is logged in the test failure. | Empty String (no argument) |
+| assert    | A Regular expression                             | In the "okay" or "fail" response, Fuzzy Fastboot will assert the  response matches this regular expression.                                                                                                                                                                                                                                                                                                                                        | Empty String (no argument) |
+| output    | The name of the saved file                       | This is the name of the saved output file passed to the validation script. It is saved in whatever location is specified by the --output_path argument                                                                                                                                                                                                                                                                                             | out.img                    |
+
+
+#### `<checksum/>` Element
+If the bootloader supports hashing partitions (implementing this is strongly recommended), Fuzzy Fastboot can
+use it to do a bunch more testing. Make sure this hash is a cryptographically secure hash, as a non-secure one
+might reveal secrets about the partitions' contents.
+
+The checksum element has no children and only two required attributes:
+
+- **value** - The command that hashes a partition. Note that the name of the partition will be appended to the end of the command. For example, if `value="oem hash"`, hashing the partition `bar` would be issued with `oem hash bar`.
+- **parser** - How the hash is returned is up to the vendor's implementation. It could be part of the `OKAY` response, or be encoded in `INFO` responses. Thus, the parser attribute is used to specify a program/script that will extract the hash. The first argument to the program will be the be the response from `OKAY`, the second argument will be all the `INFO` responses joined by newlines. The extracted hash should be sent back to Fuzzy Fastboot as a string written to stderr, and a return code of 0 to signal the parsing was successful. In the case of failure, return a non-zero return code, an optionally an associated error message written to stderr.
+
+
+
+## Full Example XML Configuration
+Here is a basic example configuration. This can also be found in the 'example' folder
+as well as the associated python scripts 'checksum_parser.py' (used to extract partition hash),
+and 'validator.py' (used to validate an oem command that returns data).
+```xml
+<?xml version="1.0"?>
+<config>
+<!-- All the device getvar variables should be listed here -->
+<getvar>
+	<var key="product" assert="superphone2000"/>
+	<var key="secure" assert="no|yes"/>
+</getvar>
+
+<!-- All the device partitions should be listed here -->
+<partitions>
+	<part value="boot" slots="yes" test="yes" hashable="yes" parsed="yes"/>
+	<part value="modem" slots="yes" test="yes" hashable="yes"/>
+	<part value="userdata" slots="no" test="yes" hashable="no"/>
+
+	<!-- Bootloader partitions -->
+	<part value="foo1" slots="yes" test="no" hashable="yes"/>
+	<part value="foo2" slots="yes" test="no" hashable="yes"/>
+	<part value="bar3" slots="yes" test="no" hashable="yes"/>
+</partitions>
+
+<!-- All the device packed partitions should be listed here -->
+<packed>
+	<part value="bootloader" slots="yes">
+		<!-- We list the real partitions it is composed of -->
+		<child>foo1</child>
+		<child>foo2</child>
+		<child>bar3</child>
+		<!-- We list tests, expect defaults to 'okay' -->
+		<test packed="bootloader.img" unpacked="unpacked"/>
+		<test packed="bootloader_garbage.img" expect="fail"/>
+	</part>
+</packed>
+
+<!-- All the oem commands should be listed here -->
+<oem>
+	<!-- The 'oem self_destruct' command requires an unlocked bootloader -->
+	<command value="self_destruct" permissions="unlocked">
+		<!-- This will test that "oem self_destruct now" returns 'okay' -->
+		<test value="now" expect="okay"/>
+		<test value="yesterday" expect="fail" />
+	</command>
+
+	<!-- Test a fictional 'oem get' command -->
+	<command value="get" permissions="none">
+		<test value="batch_id" expect="okay" assert="[[:digit:]]+"/>
+		<test value="device_color" expect="okay" assert="green|blue"/>
+		<test value="build_num" expect="okay" assert="[\w\-.]+"/>
+		<test value="garbage" expect="fail" assert="Invalid var '[\w ]+'"/>
+	</command>
+
+	<!-- Some oem commands might require staging or downloading data, or both -->
+	<command value="foobar" permissions="unlocked">
+		<!-- FF will first stage test_image.img before running 'oem foobar use_staged' -->
+		<test value="use_staged" expect="okay" input="test_image.img" />
+		<!-- FF will run 'oem foobar send_response', upload data from device, then run the validator script -->
+		<test value="send_response" expect="fail" validate="python validator.py"/>
+	</command>
+</oem>
+
+<!-- If there is a custom oem checksum command to hash partitions, add it here -->
+<checksum value="oem sha1sum"/>
+</config>
+
+```
+
+## Running Fuzzy Fastboot
+Fuzzy Fastboot is built with the fastboot tool itself. It will appear in `out/host/linux-x86/testcases/fuzzy_fastboot/x86_64`.
+
+### Command Line Arguments
+- **--config=**: Specify the name of the configuration XML file. If omitted, only the generic tests will be available.
+- **--search_path=**: Specify the path where Fuzzy Fastboot will look for files referenced in the XML. This includes all the test images and the referenced programs/scripts. This is also where the --config is searched for. If this argument is omitted it defaults to the current directory.
+- **--output_path**: Some oem tests can download an image to the host for validation. This is the location where that image is stored. This deafults to '/tmp'.
+- **--serial_port**: Many devices have a UART or serial log, that reports logging information. Fuzzy Fastboot can include this logging information in the backtraces it generates. This can make debugging far easier. If your device has this, it can be specified with the path to the tty device. Ex: "/dev/ttyUSB0".
+- **--gtest_***: Any valid gtest argument (they all start with 'gtest_')
+- **-h**: Print gtest's help message
+
+
+## Using Fuzzy Fastboot on my Device
+All Fuzzy Fastboot tests should pass on your device. No test should be able to
+crash the bootloader. Invalid input MUST be handled gracefully. Using "asserts"
+or panicking on invalid or malformed input is not an acceptable way to handle
+these tests, as ungraceful forced termination of the bootloader can expose
+vulnerabilities and leave the device in a bad state.
+
+The following is the recommended workflow for using Fuzzy Fastboot on a new device:
+
+### Step 1: Pass the generic Conformance tests
+Begin with just the generic tests (i.e. no XML file). In particular, make sure all
+the conformance tests are passing before you move on. All other tests require that
+the basic generic conformance tests all pass for them to be valid. The conformance
+tests can be run with `./fuzzy_fastboot --gtests_filter=Conformance.*`.
+
+#### Understanding and Fixing Failed Tests
+Whenever a test fails, it will print out to the console the reason for failure
+and the lines and file where the error happened. At the end of each failure
+block, there will usually be a message that Fuzzy Fastboot reports to gtest
+explaining what went wrong. An example is shown below:
+
+```
+Expected equality of these values:
+  resp
+    Which is: "no"
+  unlock ? "yes" : "no"
+    Which is: "yes"
+getvar:unlocked response was not 'no' or 'yes': no
+system/core/fastboot/fuzzy_fastboot/fixtures.cpp:227: Failure
+Expected: SetLockState(UNLOCKED) doesn't generate new fatal failures in the current thread.
+  Actual: it does.
+[THERE WILL BE A MESSAGE HERE EXPLAINING WHY IT FAILED]
+```
+
+In most cases this message at the bottom is all that is needed to figure out why it failed.
+If this is not enough information, below this, gtest will also print out a human readable
+backtrace of the underlying fastboot commands leading up the failure in this test.
+Here is an example:
+```
+<<<<<<<< TRACE BEGIN >>>>>>>>>
+[WRITE 0ms](15 bytes): "getvar:unlocked"
+[READ 20ms](6 bytes): "OKAYno"
+<<<<<<<< TRACE END >>>>>>>>>
+```
+One can easily see the reason for the failure was the test expected the device to
+be unlocked.
+
+If it is still unclear why the failure is happening, the last thing to do is look
+at what line number and file is generating the error. Gtest will always print this out.
+You can then manually look through Fuzzy Fastboot's test source code, and figure out
+what went wrong.
+
+
+### Step 2: Pass all the other generic tests
+Run all the other generic tests (still no XML file). A list of all of them can be
+printed out with: "./fuzzy_fastboot --gtest_list_tests". As before, "--gtest_filter"
+can be used to select certain tests to run, once you figure out which ones failed.
+
+One particular set of tests to watch out for are the ones that involve USB resets.
+USB resets effectively unplug and replug the device in software. Fuzzy Fastboot,
+expects USB resets to cancel whatever transaction is currently going on.
+This is also how Fuzzy Fastboot attempts to recover from errors when the device is
+unresponsive.
+
+### Step 3: Create a device XML configuration
+Without a device specific configuration file, Fuzzy Fastboot will have poor test
+coverage of your device. The vast majority of tests are auto-generated via the XML
+configuration file. Use the guide above to generate a configuration for your device.
+Make sure to be as thorough as possible, and list everything in the configuration
+that can be tested. Finally, make sure that the packed pseudo partitions and
+oem commands all have provided test cases. Be sure to test both the positive case
+(i.e. with valid input), as well as the opposite. Make sure the failure tests
+have good coverage by thinking about all the ways invalid and malicious inputs
+could be formed. These means creating images with malformed headers, illegal chars,
+and other evil inputs.
+
+Now run fuzzy_fastboot with the supplied configuration file. If you do "--gtest_list_tests",
+you should see a ton more tests that were autogenerated by Fuzzy Fastboot.
+As before, run these tests till everything passes. Again, use "--gtest_filter"
+to select specific tests to run once you know what fail,
+as running the whole things with a large configuration can take over 30 minutes.
+See the gtest documentation, for nifty tricks and command line options.
+
+### Step 4: Figure out what Fuzzy Fastboot can't/isn't testing
+While Fuzzy Fastboot with a XML configuration file, should provide good test coverage.
+Think about what device specific things are not being tested, and test them manually.
+In particular, things that if not handled carefully could create security exploits.
+Don't be lazy here, as you already put in the time to get this far.
+
+### Step 5: Celebrate
+You're done :). Now you can be more confident that your implementation is sound, and
+have piece of mind knowing you are protecting the users' security and data by
+running these tests. Don't get too complacent. If the bootloader's source code
+is modified in a way that could introduce bugs or security issues. Make sure to
+test again. You might have to add to your existing configuration file.
+
+## Limitations and Warnings
+- Currently this only works on Linux (even if it builds on Mac)
+- Only fastboot over USB is currently supported
+- Passing these tests does not mean there are not bugs/security issues. For example, a buffer overrun might not always trigger a crash or have any noticeable side effects.
+- **Be extremely careful of the Fuzzy Fastboot tests you are running. Know exactly what the tests do you are about to run before you run them. It is very possible to brick a device with many of these tests.**
+
+## Fuzzy Fastboot Missing Features TODO's
+The following are missing features that should eventually be added
+- *Sparse Image Tests*: Currently there are no tests that tests sparse images. Both well-formed and malicious images need to be tested.
+- *Unlocking/Locking Critical*: Currently there are no tests that tests that locking/unlocking critical functionality.
+- *Saved Test Log*: Fuzzy Fastboot should be able to create a failure log for every failing test and save it to a file. This file should include the test information, the reason it failed, and the fastboot command trace (with the serial console logs). Currently it just prints it to the console at the end of every test.
+- *Host Side Hashing*: One should be able to provide the hashing algorithm to the Fuzzy Fastboot, so it can be checked to agree with what the device is reporting.
+
+
+## Author
+Aaron Wisner - awisner@google.com
diff --git a/fastboot/fuzzy_fastboot/example/checksum_parser.py b/fastboot/fuzzy_fastboot/example/checksum_parser.py
new file mode 100644
index 0000000..1a890e6
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/example/checksum_parser.py
@@ -0,0 +1,37 @@
+'''
+Some bootloader's support hashing partitions. This is a great feature for testing
+correctness. However, the format for the way the hash is returned depends on the
+implementation. The hash could be send through an INFO response, or be as part
+of the OKAY response itself. This script is called with the first argument
+as the string mesage from the okay response. The second argument is each
+info response joined by newlines into one argument.
+'''
+
+import sys
+
+
+def main():
+  '''
+  Data is sent back to the parent fuzzy_fastboot process through the stderr pipe.
+  There are two interpretations of this data by FF.
+
+  0 return code:
+    Anything written to STDERR will be interpreted as part of the hash.
+
+  non-zero return code:
+    Anything written to STDERR is part of the error message that will logged by FF
+    to explain why hash extraction failed.
+
+  Feel free to print to to STDOUT with print() as usual to print info to console
+  '''
+  script, response, info = sys.argv
+  # the info responses are concated by newlines
+  infos = [s.strip() for s in info.splitlines()]
+  sys.stderr.write(infos[-1])
+  print("Extracted checksum: '%s'" % infos[-1])
+  # non-zero return code signals error
+  return 0
+
+
+if __name__ == "__main__":
+  sys.exit(main())
diff --git a/fastboot/fuzzy_fastboot/example/config.xml b/fastboot/fuzzy_fastboot/example/config.xml
new file mode 100644
index 0000000..af2a3b9
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/example/config.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<config>
+<!-- All the device getvar variables should be listed here -->
+<getvar>
+	<var key="product" assert="superphone2000"/>
+	<var key="secure" assert="no|yes"/>
+</getvar>
+
+<!-- All the device partitions should be listed here -->
+<partitions>
+	<part value="boot" slots="yes" test="yes" hashable="yes" parsed="yes"/>
+	<part value="modem" slots="yes" test="yes" hashable="yes"/>
+	<part value="userdata" slots="no" test="yes" hashable="no"/>
+
+	<!-- Bootloader partitions -->
+	<part value="foo1" slots="yes" test="no" hashable="yes"/>
+	<part value="foo2" slots="yes" test="no" hashable="yes"/>
+	<part value="bar3" slots="yes" test="no" hashable="yes"/>
+</partitions>
+
+<!-- All the device packed partitions should be listed here -->
+<packed>
+	<part value="bootloader" slots="yes">
+		<!-- We list the real partitions it is composed of -->
+		<child>foo1</child>
+		<child>foo2</child>
+		<child>bar3</child>
+		<!-- We list tests, expect defaults to 'okay' -->
+		<test packed="bootloader.img" unpacked="unpacked"/>
+		<test packed="bootloader_garbage.img" expect="fail"/>
+	</part>
+</packed>
+
+<!-- All the oem commands should be listed here -->
+<oem>
+	<!-- The 'oem self_destruct' command requires an unlocked bootloader -->
+	<command value="self_destruct" permissions="unlocked">
+		<!-- This will test that "oem self_destruct now" returns 'okay' -->
+		<test value="now" expect="okay"/>
+		<test value="yesterday" expect="fail" />
+	</command>
+
+	<!-- Test a fictional 'oem get' command -->
+	<command value="get" permissions="none">
+		<test value="batch_id" expect="okay" assert="[[:digit:]]+"/>
+		<test value="device_color" expect="okay" assert="green|blue"/>
+		<test value="build_num" expect="okay" assert="[\w\-.]+"/>
+		<test value="garbage" expect="fail" assert="Invalid var '[\w ]+'"/>
+	</command>
+
+	<!-- Some oem commands might require staging or downloading data, or both -->
+	<command value="foobar" permissions="unlocked">
+		<!-- FF will first stage test_image.img before running 'oem foobar use_staged' -->
+		<test value="use_staged" expect="okay" input="test_image.img" />
+		<!-- FF will run 'oem foobar send_response', upload data from device, then run the validator script -->
+		<test value="send_response" expect="fail" validate="python validator.py"/>
+	</command>
+</oem>
+
+<!-- If there is a custom oem checksum command to hash partitions, add it here -->
+<checksum value="oem sha1sum"/>
+</config>
diff --git a/fastboot/fuzzy_fastboot/example/validator.py b/fastboot/fuzzy_fastboot/example/validator.py
new file mode 100644
index 0000000..9c5081f
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/example/validator.py
@@ -0,0 +1,37 @@
+'''
+This is an example validator to be used with oem commands that allow you to
+upload data afterwards that you wish to validate locally.
+'''
+import sys
+
+def eprint(msg):
+  '''
+  A helper function for logging error messages to fuzzy_fastboot
+  Use this function as you would "print()"
+  '''
+  sys.stderr.write(msg + '\n')
+
+
+def main():
+  '''
+  Data is sent back to the parent fuzzy_fastboot process through the stderr pipe.
+
+  If this script has a non-zero return code, anything written to STDERR is part of
+  the error message that will logged by FF to explain why this validation failed.
+
+  Feel free to print to to STDOUT with print() as usual to print info to console
+  '''
+  script, command, fname = sys.argv
+  eprint("Messages here will go to the parent testers logs")
+  eprint("Hello world")
+  print("This goes to stdout as expected")
+  with open(fname, "rb") as fd:
+    # Do some validation on the buffer
+    pass
+
+  # non-zero return code signals error
+  return -1
+
+
+if __name__ == "__main__":
+  sys.exit(main())
diff --git a/fastboot/fuzzy_fastboot/extensions.cpp b/fastboot/fuzzy_fastboot/extensions.cpp
new file mode 100644
index 0000000..62ef5bab
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/extensions.cpp
@@ -0,0 +1,314 @@
+/*
+ * 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 <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <csignal>
+#include <cstdlib>
+#include <fstream>
+
+#include "extensions.h"
+#include "test_utils.h"
+#include "tinyxml2.h"
+
+namespace fastboot {
+namespace extension {
+
+namespace {  // private to this file
+
+// Since exceptions are disabled, a bad regex will trigger an abort in the constructor
+// We at least need to print something out
+std::regex MakeRegex(const std::string& regex_str, int line_num,
+                     std::regex_constants::syntax_option_type type = std::regex::ECMAScript) {
+    // The signal handler can only access static vars
+    static std::string err_str;
+    err_str = android::base::StringPrintf("'%s' is not a valid regex string (line %d)\n",
+                                          regex_str.c_str(), line_num);
+    const auto sighandler = [](int) {
+        int nbytes = write(fileno(stderr), err_str.c_str(), err_str.length());
+        static_cast<void>(nbytes);  // need to supress the unused nbytes/ or unused result
+    };
+    std::signal(SIGABRT, sighandler);
+    // Now attempt to create the regex
+    std::regex ret(regex_str, type);
+    // unregister
+    std::signal(SIGABRT, SIG_DFL);
+
+    return ret;
+}
+
+bool XMLAssert(bool cond, const tinyxml2::XMLElement* elem, const char* msg) {
+    if (!cond) {
+        printf("%s (line %d)\n", msg, elem->GetLineNum());
+    }
+    return !cond;
+}
+
+const std::string XMLAttribute(const tinyxml2::XMLElement* elem, const std::string key,
+                               const std::string key_default = "") {
+    if (!elem->Attribute(key.c_str())) {
+        return key_default;
+    }
+
+    return elem->Attribute(key.c_str());
+}
+
+bool XMLYesNo(const tinyxml2::XMLElement* elem, const std::string key, bool* res,
+              bool def = false) {
+    if (!elem->Attribute(key.c_str())) {
+        *res = def;
+        return true;
+    }
+    const std::string val = elem->Attribute(key.c_str());
+    if (val != "yes" && val != "no") {
+        return false;
+    }
+    *res = (val == "yes");
+    return true;
+}
+
+bool ExtractPartitions(tinyxml2::XMLConstHandle handle, Configuration* config) {
+    // Extract partitions
+    const tinyxml2::XMLElement* part = handle.FirstChildElement("part").ToElement();
+    while (part) {
+        Configuration::PartitionInfo part_info;
+        const std::string name = XMLAttribute(part, "value");
+        const std::string test = XMLAttribute(part, "test");
+        if (XMLAssert(!name.empty(), part, "The name of a partition can not be empty") ||
+            XMLAssert(XMLYesNo(part, "slots", &part_info.slots), part,
+                      "Slots attribute must be 'yes' or 'no'") ||
+            XMLAssert(XMLYesNo(part, "hashable", &part_info.hashable, true), part,
+                      "Hashable attribute must be 'yes' or 'no'") ||
+            XMLAssert(XMLYesNo(part, "parsed", &part_info.parsed), part,
+                      "Parsed attribute must be 'yes' or 'no'"))
+            return false;
+
+        bool allowed = test == "yes" || test == "no-writes" || test == "no";
+        if (XMLAssert(allowed, part, "The test attribute must be 'yes' 'no-writes' or 'no'"))
+            return false;
+        if (XMLAssert(config->partitions.find(name) == config->partitions.end(), part,
+                      "The same partition name is listed twice"))
+            return false;
+        part_info.test = (test == "yes")
+                                 ? Configuration::PartitionInfo::YES
+                                 : (test == "no-writes") ? Configuration::PartitionInfo::NO_WRITES
+                                                         : Configuration::PartitionInfo::NO;
+        config->partitions[name] = part_info;
+        part = part->NextSiblingElement("part");
+    }
+    return true;
+}
+
+bool ExtractPacked(tinyxml2::XMLConstHandle handle, Configuration* config) {
+    // Extract partitions
+    const tinyxml2::XMLElement* part = handle.FirstChildElement("part").ToElement();
+    while (part) {
+        Configuration::PackedInfo packed_info;
+        const std::string name = XMLAttribute(part, "value");
+
+        if (XMLAssert(!name.empty(), part, "The name of a packed partition can not be empty") ||
+            XMLAssert(XMLYesNo(part, "slots", &packed_info.slots), part,
+                      "Slots attribute must be 'yes' or 'no'") ||
+            XMLAssert(config->partitions.find(name) == config->partitions.end(), part,
+                      "A packed partition can not have same name as a real one") ||
+            XMLAssert(config->partitions.find(name) == config->partitions.end(), part,
+                      "The same partition name is listed twice"))
+            return false;
+
+        // Extract children partitions
+        const tinyxml2::XMLElement* child = part->FirstChildElement("child")
+                                                    ? part->FirstChildElement("child")->ToElement()
+                                                    : nullptr;
+        while (child) {
+            const std::string text(child->GetText());
+            // Make sure child exists
+            if (XMLAssert(config->partitions.find(text) != config->partitions.end(), child,
+                          "The child partition was not listed in <partitions>"))
+                return false;
+            packed_info.children.insert(text);
+            child = child->NextSiblingElement("child");
+        }
+
+        // Extract tests
+        const tinyxml2::XMLElement* test = part->FirstChildElement("test")
+                                                   ? part->FirstChildElement("test")->ToElement()
+                                                   : nullptr;
+        while (test) {
+            Configuration::PackedInfoTest packed_test;
+            packed_test.packed_img = XMLAttribute(test, "packed");
+            packed_test.unpacked_dir = XMLAttribute(test, "unpacked");
+            const std::string expect = XMLAttribute(test, "expect", "okay");
+
+            if (XMLAssert(!packed_test.packed_img.empty(), test,
+                          "The packed image location must be specified") ||
+                XMLAssert(CMD_EXPECTS.find(expect) != CMD_EXPECTS.end(), test,
+                          "Expect attribute must be 'okay' or 'fail'"))
+                return false;
+
+            packed_test.expect = CMD_EXPECTS.at(expect);
+            // The expect is only unpacked directory is only needed if success
+            if (packed_test.expect == OKAY &&
+                XMLAssert(!packed_test.unpacked_dir.empty(), test,
+                          "The unpacked image folder location must be specified"))
+                return false;
+
+            packed_info.tests.push_back(packed_test);
+            test = test->NextSiblingElement("test");
+        }
+
+        config->packed[name] = packed_info;
+        part = part->NextSiblingElement("part");
+    }
+    return true;
+}
+
+bool ExtractGetVars(tinyxml2::XMLConstHandle handle, Configuration* config) {
+    // Extract getvars
+    const tinyxml2::XMLElement* var = handle.FirstChildElement("var").ToElement();
+    while (var) {
+        const std::string key = XMLAttribute(var, "key");
+        const std::string reg = XMLAttribute(var, "assert");
+        if (XMLAssert(key.size(), var, "The var key name is empty")) return false;
+        if (XMLAssert(config->getvars.find(key) == config->getvars.end(), var,
+                      "The same getvar variable name is listed twice"))
+            return false;
+        Configuration::GetVar getvar{reg, MakeRegex(reg, var->GetLineNum()), var->GetLineNum()};
+        config->getvars[key] = std::move(getvar);
+        var = var->NextSiblingElement("var");
+    }
+    return true;
+}
+
+bool ExtractOem(tinyxml2::XMLConstHandle handle, Configuration* config) {
+    // Extract getvars
+    // Extract oem commands
+    const tinyxml2::XMLElement* command = handle.FirstChildElement("command").ToElement();
+    while (command) {
+        const std::string cmd = XMLAttribute(command, "value");
+        const std::string permissions = XMLAttribute(command, "permissions");
+        if (XMLAssert(cmd.size(), command, "Empty command value")) return false;
+        if (XMLAssert(permissions == "none" || permissions == "unlocked", command,
+                      "Permissions attribute must be 'none' or 'unlocked'"))
+            return false;
+
+        // Each command has tests
+        std::vector<Configuration::CommandTest> tests;
+        const tinyxml2::XMLElement* test = command->FirstChildElement("test");
+        while (test) {  // iterate through tests
+            Configuration::CommandTest ctest;
+
+            ctest.line_num = test->GetLineNum();
+            const std::string default_name = "XMLTest-line-" + std::to_string(test->GetLineNum());
+            ctest.name = XMLAttribute(test, "name", default_name);
+            ctest.arg = XMLAttribute(test, "value");
+            ctest.input = XMLAttribute(test, "input");
+            ctest.output = XMLAttribute(test, "output");
+            ctest.validator = XMLAttribute(test, "validate");
+            ctest.regex_str = XMLAttribute(test, "assert");
+
+            const std::string expect = XMLAttribute(test, "expect");
+
+            if (XMLAssert(CMD_EXPECTS.find(expect) != CMD_EXPECTS.end(), test,
+                          "Expect attribute must be 'okay' or 'fail'"))
+                return false;
+            ctest.expect = CMD_EXPECTS.at(expect);
+            std::regex regex;
+            if (expect == "okay" && ctest.regex_str.size()) {
+                ctest.regex = MakeRegex(ctest.regex_str, test->GetLineNum());
+            }
+            tests.push_back(std::move(ctest));
+            test = test->NextSiblingElement("test");
+        }
+
+        // Build the command struct
+        const Configuration::OemCommand oem_cmd{permissions == "unlocked", std::move(tests)};
+        config->oem[cmd] = std::move(oem_cmd);
+
+        command = command->NextSiblingElement("command");
+    }
+    return true;
+}
+
+bool ExtractChecksum(tinyxml2::XMLConstHandle handle, Configuration* config) {
+    const tinyxml2::XMLElement* checksum = handle.ToElement();
+    if (checksum && checksum->Attribute("value")) {
+        config->checksum = XMLAttribute(checksum, "value");
+        config->checksum_parser = XMLAttribute(checksum, "parser");
+        if (XMLAssert(config->checksum_parser != "", checksum,
+                      "A checksum parser attribute is mandatory"))
+            return false;
+    }
+    return true;
+}
+
+}  // anonymous namespace
+
+bool ParseXml(const std::string& file, Configuration* config) {
+    tinyxml2::XMLDocument doc;
+    if (doc.LoadFile(file.c_str())) {
+        printf("Failed to open/parse XML file '%s'\nXMLError: %s\n", file.c_str(), doc.ErrorStr());
+        return false;
+    }
+
+    tinyxml2::XMLConstHandle handle(&doc);
+    tinyxml2::XMLConstHandle root(handle.FirstChildElement("config"));
+
+    // Extract the getvars
+    if (!ExtractGetVars(root.FirstChildElement("getvar"), config)) {
+        return false;
+    }
+    // Extract the partition info
+    if (!ExtractPartitions(root.FirstChildElement("partitions"), config)) {
+        return false;
+    }
+
+    // Extract packed
+    if (!ExtractPacked(root.FirstChildElement("packed"), config)) {
+        return false;
+    }
+
+    // Extract oem commands
+    if (!ExtractOem(root.FirstChildElement("oem"), config)) {
+        return false;
+    }
+
+    // Extract checksum
+    if (!ExtractChecksum(root.FirstChildElement("checksum"), config)) {
+        return false;
+    }
+
+    return true;
+}
+
+}  // namespace extension
+}  // namespace fastboot
diff --git a/fastboot/fuzzy_fastboot/extensions.h b/fastboot/fuzzy_fastboot/extensions.h
new file mode 100644
index 0000000..58312e5
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/extensions.h
@@ -0,0 +1,132 @@
+/*
+ * 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 <regex>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+namespace fastboot {
+namespace extension {
+
+enum Expect { OKAY = 0, FAIL, DATA };
+
+static const std::unordered_map<std::string, Expect> CMD_EXPECTS = {
+        {"okay", OKAY},
+        {"fail", FAIL},
+        {"data", DATA},
+};
+
+static const std::unordered_map<Expect, std::string> EXPECTS_STR = {
+        {OKAY, "okay"},
+        {FAIL, "fail"},
+        {DATA, "data"},
+};
+
+struct Configuration {
+    struct GetVar {
+        std::string regex_str;
+        std::regex regex;
+        int line_num;
+
+        // So gtest can print me
+        friend ::std::ostream& operator<<(::std::ostream& os, const GetVar& self) {
+            return os << "<regex='" << self.regex_str << "' line_num=" << self.line_num << ">";
+        }
+    };
+    struct PartitionInfo {
+        enum TestConfig { NO = 0, NO_WRITES, YES };
+        bool hashable;
+        bool slots;   // Does it have slots
+        bool parsed;  // Does the bootloader do parsing on the img?
+        TestConfig test;
+
+        // So gtest can print me
+        friend ::std::ostream& operator<<(::std::ostream& os, const PartitionInfo& pinfo) {
+            return os << "<hashable=" << pinfo.hashable << " slots=" << pinfo.slots
+                      << " parsed=" << pinfo.parsed << ">";
+        }
+    };
+
+    struct PackedInfoTest {
+        Expect expect;  // Does it have slots
+        std::string packed_img;
+        std::string unpacked_dir;
+
+        // So gtest can print me
+        friend ::std::ostream& operator<<(::std::ostream& os, const PackedInfoTest& pinfo) {
+            return os << "<"
+                      << "expect=" << EXPECTS_STR.at(pinfo.expect)
+                      << " packed_img=" << pinfo.packed_img
+                      << " unpacked_dir=" << pinfo.unpacked_dir << ">";
+        }
+    };
+
+    struct PackedInfo {
+        bool slots;  // Does it have slots
+        std::unordered_set<std::string> children;
+        std::vector<PackedInfoTest> tests;
+    };
+
+    struct CommandTest {
+        std::string name;
+        int line_num;
+        std::string arg;
+        Expect expect;
+        std::string regex_str;
+        std::regex regex;
+        std::string input;
+        std::string output;
+        std::string validator;
+
+        // So gtest can print me
+        friend ::std::ostream& operator<<(::std::ostream& os, const CommandTest& self) {
+            return os << "test: " << self.name << " (line: " << self.line_num << ")";
+        }
+    };
+
+    struct OemCommand {
+        bool restricted;  // Does device need to be unlocked?
+        std::vector<CommandTest> tests;
+    };
+
+    std::unordered_map<std::string, GetVar> getvars;
+    std::unordered_map<std::string, PartitionInfo> partitions;
+    std::unordered_map<std::string, PackedInfo> packed;
+    std::unordered_map<std::string, OemCommand> oem;
+
+    std::string checksum;
+    std::string checksum_parser;
+};
+
+bool ParseXml(const std::string& file, Configuration* config);
+
+}  // namespace extension
+}  // namespace fastboot
diff --git a/fastboot/fuzzy_fastboot/fixtures.cpp b/fastboot/fuzzy_fastboot/fixtures.cpp
new file mode 100644
index 0000000..bc13a8c
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/fixtures.cpp
@@ -0,0 +1,310 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <termios.h>
+#include <unistd.h>
+#include <chrono>
+#include <cstdlib>
+#include <fstream>
+#include <map>
+#include <random>
+#include <regex>
+#include <set>
+#include <thread>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include "fastboot_driver.h"
+#include "usb.h"
+
+#include "extensions.h"
+#include "fixtures.h"
+#include "test_utils.h"
+#include "usb_transport_sniffer.h"
+
+using namespace std::literals::chrono_literals;
+
+namespace fastboot {
+
+int FastBootTest::MatchFastboot(usb_ifc_info* info, const std::string& local_serial) {
+    if (info->ifc_class != 0xff || info->ifc_subclass != 0x42 || info->ifc_protocol != 0x03) {
+        return -1;
+    }
+
+    cb_scratch = info->device_path;
+
+    // require matching serial number or device path if requested
+    // at the command line with the -s option.
+    if (!local_serial.empty() && local_serial != info->serial_number &&
+        local_serial != info->device_path)
+        return -1;
+    return 0;
+}
+
+bool FastBootTest::UsbStillAvailible() {
+    // For some reason someone decided to prefix the path with "usb:"
+    std::string prefix("usb:");
+    if (std::equal(prefix.begin(), prefix.end(), device_path.begin())) {
+        std::string fname(device_path.begin() + prefix.size(), device_path.end());
+        std::string real_path =
+                android::base::StringPrintf("/sys/bus/usb/devices/%s/serial", fname.c_str());
+        std::ifstream f(real_path.c_str());
+        return f.good();
+    }
+    exit(-1);  // This should never happen
+    return true;
+}
+
+bool FastBootTest::UserSpaceFastboot() {
+    std::string value;
+    fb->GetVar("is-userspace", &value);
+    return value == "yes";
+}
+
+RetCode FastBootTest::DownloadCommand(uint32_t size, std::string* response,
+                                      std::vector<std::string>* info) {
+    return fb->DownloadCommand(size, response, info);
+}
+
+RetCode FastBootTest::SendBuffer(const std::vector<char>& buf) {
+    return fb->SendBuffer(buf);
+}
+
+RetCode FastBootTest::HandleResponse(std::string* response, std::vector<std::string>* info,
+                                     int* dsize) {
+    return fb->HandleResponse(response, info, dsize);
+}
+
+void FastBootTest::SetUp() {
+    if (device_path != "") {               // make sure the device is still connected
+        ASSERT_TRUE(UsbStillAvailible());  // The device disconnected
+    }
+
+    const auto matcher = [](usb_ifc_info* info) -> int {
+        return MatchFastboot(info, device_serial);
+    };
+    for (int i = 0; i < MAX_USB_TRIES && !transport; i++) {
+        std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
+        if (usb)
+            transport = std::unique_ptr<UsbTransportSniffer>(
+                    new UsbTransportSniffer(std::move(usb), serial_port));
+        std::this_thread::sleep_for(std::chrono::milliseconds(10));
+    }
+
+    ASSERT_TRUE(transport);  // no nullptr
+
+    if (device_path == "") {  // We set it the first time, then make sure it never changes
+        device_path = cb_scratch;
+    } else {
+        ASSERT_EQ(device_path, cb_scratch);  // The path can not change
+    }
+    fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+    // No error checking since non-A/B devices may not support the command
+    fb->GetVar("current-slot", &initial_slot);
+}
+
+void FastBootTest::TearDown() {
+    EXPECT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
+    // No error checking since non-A/B devices may not support the command
+    fb->SetActive(initial_slot);
+
+    TearDownSerial();
+
+    fb.reset();
+
+    if (transport) {
+        transport.reset();
+    }
+
+    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
+}
+
+// TODO, this should eventually be piped to a file instead of stdout
+void FastBootTest::TearDownSerial() {
+    if (!transport) return;
+    // One last read from serial
+    transport->ProcessSerial();
+    if (HasFailure()) {
+        // TODO, print commands leading up
+        printf("<<<<<<<< TRACE BEGIN >>>>>>>>>\n");
+        printf("%s", transport->CreateTrace().c_str());
+        printf("<<<<<<<< TRACE END >>>>>>>>>\n");
+        // std::vector<std::pair<const TransferType, const std::vector<char>>>  prev =
+        // transport->Transfers();
+    }
+}
+
+void FastBootTest::ReconnectFastbootDevice() {
+    fb.reset();
+    transport.reset();
+    while (UsbStillAvailible())
+        ;
+    printf("WAITING FOR DEVICE\n");
+    // Need to wait for device
+    const auto matcher = [](usb_ifc_info* info) -> int {
+        return MatchFastboot(info, device_serial);
+    };
+    while (!transport) {
+        std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
+        if (usb) {
+            transport = std::unique_ptr<UsbTransportSniffer>(
+                    new UsbTransportSniffer(std::move(usb), serial_port));
+        }
+        std::this_thread::sleep_for(1s);
+    }
+    device_path = cb_scratch;
+    fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+}
+
+void FastBootTest::SetLockState(bool unlock, bool assert_change) {
+    if (!fb) {
+        return;
+    }
+
+    // User space fastboot implementations are not allowed to communicate to
+    // secure hardware and hence cannot lock/unlock the device.
+    if (UserSpaceFastboot()) {
+        return;
+    }
+
+    std::string resp;
+    std::vector<std::string> info;
+    // To avoid risk of bricking device, make sure unlock ability is set to 1
+    ASSERT_EQ(fb->RawCommand("flashing get_unlock_ability", &resp, &info), SUCCESS)
+            << "'flashing get_unlock_ability' failed";
+
+    // There are two ways this can be reported, through info or the actual response
+    if (!resp.empty()) {  // must be in the info response
+        ASSERT_EQ(resp.back(), '1')
+                << "Unlock ability must be set to 1 to avoid bricking device, see "
+                   "'https://source.android.com/devices/bootloader/unlock-trusty'";
+    } else {
+        ASSERT_FALSE(info.empty()) << "'flashing get_unlock_ability' returned empty response";
+        ASSERT_FALSE(info.back().empty()) << "Expected non-empty info response";
+        ASSERT_EQ(info.back().back(), '1')
+                << "Unlock ability must be set to 1 to avoid bricking device, see "
+                   "'https://source.android.com/devices/bootloader/unlock-trusty'";
+    }
+
+    EXPECT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
+    ASSERT_TRUE(resp == "no" || resp == "yes")
+            << "getvar:unlocked response was not 'no' or 'yes': " + resp;
+
+    if ((unlock && resp == "no") || (!unlock && resp == "yes")) {
+        std::string cmd = unlock ? "unlock" : "lock";
+        ASSERT_EQ(fb->RawCommand("flashing " + cmd, &resp), SUCCESS)
+                << "Attempting to change locked state, but 'flashing" + cmd + "' command failed";
+        printf("PLEASE RESPOND TO PROMPT FOR '%sing' BOOTLOADER ON DEVICE\n", cmd.c_str());
+        ReconnectFastbootDevice();
+        if (assert_change) {
+            ASSERT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
+            ASSERT_EQ(resp, unlock ? "yes" : "no")
+                    << "getvar:unlocked response was not 'no' or 'yes': " + resp;
+        }
+        printf("SUCCESS\n");
+    }
+}
+
+std::string FastBootTest::device_path = "";
+std::string FastBootTest::cb_scratch = "";
+std::string FastBootTest::initial_slot = "";
+int FastBootTest::serial_port = 0;
+std::string FastBootTest::device_serial = "";
+
+template <bool UNLOCKED>
+void ModeTest<UNLOCKED>::SetUp() {
+    ASSERT_NO_FATAL_FAILURE(FastBootTest::SetUp());
+    ASSERT_NO_FATAL_FAILURE(SetLockState(UNLOCKED));
+}
+// Need to instatiate it, so linker can find it later
+template class ModeTest<true>;
+template class ModeTest<false>;
+
+void Fuzz::TearDown() {
+    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
+
+    TearDownSerial();
+
+    std::string tmp;
+    if (fb->GetVar("product", &tmp) != SUCCESS) {
+        printf("DEVICE UNRESPONSE, attempting to recover...");
+        transport->Reset();
+        printf("issued USB reset...");
+
+        if (fb->GetVar("product", &tmp) != SUCCESS) {
+            printf("FAIL\n");
+            exit(-1);
+        }
+        printf("SUCCESS!\n");
+    }
+
+    if (transport) {
+        transport.reset();
+    }
+
+    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
+}
+
+template <bool UNLOCKED>
+void ExtensionsPartition<UNLOCKED>::SetUp() {
+    ASSERT_NO_FATAL_FAILURE(FastBootTest::SetUp());
+    ASSERT_NO_FATAL_FAILURE(SetLockState(UNLOCKED));
+
+    if (!fb) {
+        return;
+    }
+    const std::string name = GetParam().first;
+
+    std::string var;
+    ASSERT_EQ(fb->GetVar("slot-count", &var), SUCCESS) << "Getting slot count failed";
+    int32_t num_slots = strtol(var.c_str(), nullptr, 10);
+    real_parts = GeneratePartitionNames(name, GetParam().second.slots ? num_slots : 0);
+
+    ASSERT_EQ(fb->GetVar("partition-size:" + real_parts.front(), &var), SUCCESS)
+            << "Getting partition size failed";
+    part_size = strtoll(var.c_str(), nullptr, 16);
+    ASSERT_GT(part_size, 0) << "Partition size reported was invalid";
+
+    ASSERT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "Getting max download size failed";
+    max_dl = strtoll(var.c_str(), nullptr, 16);
+    ASSERT_GT(max_dl, 0) << "Max download size reported was invalid";
+
+    max_flash = std::min(part_size, max_dl);
+}
+template class ExtensionsPartition<true>;
+template class ExtensionsPartition<false>;
+
+}  // end namespace fastboot
diff --git a/fastboot/fuzzy_fastboot/fixtures.h b/fastboot/fuzzy_fastboot/fixtures.h
new file mode 100644
index 0000000..c71c897
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/fixtures.h
@@ -0,0 +1,143 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include "fastboot_driver.h"
+
+#include "extensions.h"
+#include "usb_transport_sniffer.h"
+
+namespace fastboot {
+
+const int USB_TIMEOUT = 30000;
+
+constexpr char USB_PORT_GONE[] =
+        "The USB port has disappeared, this is usually due to the bootloader crashing";
+
+class FastBootTest : public testing::Test {
+  public:
+    static int serial_port;
+    static std::string device_serial;
+    static constexpr int MAX_USB_TRIES = 10;
+
+    static int MatchFastboot(usb_ifc_info* info, const std::string& local_serial = "");
+    bool UsbStillAvailible();
+    bool UserSpaceFastboot();
+    void ReconnectFastbootDevice();
+
+  protected:
+    RetCode DownloadCommand(uint32_t size, std::string* response = nullptr,
+                            std::vector<std::string>* info = nullptr);
+
+    RetCode SendBuffer(const std::vector<char>& buf);
+    RetCode HandleResponse(std::string* response = nullptr,
+                           std::vector<std::string>* info = nullptr, int* dsize = nullptr);
+
+    void SetUp() override;
+    void TearDown() override;
+    void TearDownSerial();
+    void SetLockState(bool unlock, bool assert_change = true);
+
+    std::unique_ptr<UsbTransportSniffer> transport;
+    std::unique_ptr<FastBootDriver> fb;
+
+  private:
+    // This is an annoying hack
+    static std::string cb_scratch;
+    static std::string device_path;
+    static std::string initial_slot;
+};
+
+template <bool UNLOCKED>
+class ModeTest : public FastBootTest {
+  protected:
+    void SetUp() override;
+};
+
+class Fuzz : public ModeTest<true> {
+  protected:
+    void TearDown() override;
+};
+
+// These derived classes without overrides serve no purpose other than to allow gtest to name them
+// differently
+class BasicFunctionality : public ModeTest<true> {};
+class Conformance : public ModeTest<true> {};
+class LogicalPartitionCompliance : public ModeTest<true> {};
+class UnlockPermissions : public ModeTest<true> {};
+class LockPermissions : public ModeTest<false> {};
+
+// Magic C++ double inheritance
+class ExtensionsGetVarConformance
+    : public ModeTest<true>,
+      public ::testing::WithParamInterface<
+              std::pair<std::string, extension::Configuration::GetVar>> {};
+
+class ExtensionsOemConformance
+    : public ModeTest<true>,
+      public ::testing::WithParamInterface<
+              std::tuple<std::string, bool, extension::Configuration::CommandTest>> {};
+
+class ExtensionsPackedValid
+    : public ModeTest<true>,
+      public ::testing::WithParamInterface<
+              std::pair<std::string, extension::Configuration::PackedInfoTest>> {};
+
+class ExtensionsPackedInvalid
+    : public ModeTest<true>,
+      public ::testing::WithParamInterface<
+              std::pair<std::string, extension::Configuration::PackedInfoTest>> {};
+
+template <bool UNLOCKED>
+class ExtensionsPartition
+    : public FastBootTest,
+      public ::testing::WithParamInterface<
+              std::pair<std::string, extension::Configuration::PartitionInfo>> {
+  protected:
+    void SetUp() override;
+    int64_t part_size;
+    int64_t max_flash;
+    int64_t max_dl;
+    std::vector<std::string> real_parts;  // includes the slots
+};
+
+class AnyPartition : public ExtensionsPartition<true> {};
+class WriteablePartition : public ExtensionsPartition<true> {};
+class WriteHashablePartition : public ExtensionsPartition<true> {};
+class WriteHashNonParsedPartition : public ExtensionsPartition<true> {};
+
+class FuzzWriteablePartition : public ExtensionsPartition<true> {};
+class FuzzWriteableParsedPartition : public ExtensionsPartition<true> {};
+class FuzzAnyPartitionLocked : public ExtensionsPartition<false> {};
+
+class UserdataPartition : public ExtensionsPartition<true> {};
+
+class SparseTestPartition : public ExtensionsPartition<true> {};
+
+}  // end namespace fastboot
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
new file mode 100644
index 0000000..a1d69d2
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -0,0 +1,1780 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <chrono>
+#include <cstdlib>
+#include <fstream>
+#include <map>
+#include <random>
+#include <regex>
+#include <set>
+#include <thread>
+#include <vector>
+
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+#include <sparse/sparse.h>
+
+#include "fastboot_driver.h"
+#include "usb.h"
+
+#include "extensions.h"
+#include "fixtures.h"
+#include "test_utils.h"
+#include "usb_transport_sniffer.h"
+
+namespace fastboot {
+
+extension::Configuration config;  // The parsed XML config
+
+std::string SEARCH_PATH;
+std::string OUTPUT_PATH;
+
+// gtest's INSTANTIATE_TEST_CASE_P() must be at global scope,
+// so our autogenerated tests must be as well
+std::vector<std::pair<std::string, extension::Configuration::GetVar>> GETVAR_XML_TESTS;
+std::vector<std::tuple<std::string, bool, extension::Configuration::CommandTest>> OEM_XML_TESTS;
+std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>> PARTITION_XML_TESTS;
+std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
+        PARTITION_XML_WRITEABLE;
+std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
+        PARTITION_XML_WRITE_HASHABLE;
+std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
+        PARTITION_XML_WRITE_PARSED;
+std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
+        PARTITION_XML_WRITE_HASH_NONPARSED;
+std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
+        PARTITION_XML_USERDATA_CHECKSUM_WRITEABLE;
+std::vector<std::pair<std::string, extension::Configuration::PackedInfoTest>>
+        PACKED_XML_SUCCESS_TESTS;
+std::vector<std::pair<std::string, extension::Configuration::PackedInfoTest>> PACKED_XML_FAIL_TESTS;
+// This only has 1 or zero elements so it will disappear from gtest when empty
+std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
+        SINGLE_PARTITION_XML_WRITE_HASHABLE;
+
+const std::string DEFAULT_OUPUT_NAME = "out.img";
+// const char scratch_partition[] = "userdata";
+const std::vector<std::string> CMDS{"boot",    "continue", "download:",   "erase:", "flash:",
+                                    "getvar:", "reboot",   "set_active:", "upload"};
+
+// For pretty printing we need all these overloads
+::std::ostream& operator<<(::std::ostream& os, const RetCode& ret) {
+    return os << FastBootDriver::RCString(ret);
+}
+
+bool PartitionHash(FastBootDriver* fb, const std::string& part, std::string* hash, int* retcode,
+                   std::string* err_msg) {
+    if (config.checksum.empty()) {
+        return -1;
+    }
+
+    std::string resp;
+    std::vector<std::string> info;
+    const std::string cmd = config.checksum + ' ' + part;
+    RetCode ret;
+    if ((ret = fb->RawCommand(cmd, &resp, &info)) != SUCCESS) {
+        *err_msg =
+                android::base::StringPrintf("Hashing partition with command '%s' failed with: %s",
+                                            cmd.c_str(), fb->RCString(ret).c_str());
+        return false;
+    }
+    std::stringstream imploded;
+    std::copy(info.begin(), info.end(), std::ostream_iterator<std::string>(imploded, "\n"));
+
+    // If payload, we validate that as well
+    const std::vector<std::string> args = SplitBySpace(config.checksum_parser);
+    std::vector<std::string> prog_args(args.begin() + 1, args.end());
+    prog_args.push_back(resp);                          // Pass in the full command
+    prog_args.push_back(SEARCH_PATH + imploded.str());  // Pass in the save location
+
+    int pipe;
+    pid_t pid = StartProgram(args[0], prog_args, &pipe);
+    if (pid <= 0) {
+        *err_msg = android::base::StringPrintf("Launching hash parser '%s' failed with: %s",
+                                               config.checksum_parser.c_str(), strerror(errno));
+        return false;
+    }
+    *retcode = WaitProgram(pid, pipe, hash);
+    if (*retcode) {
+        // In this case the stderr pipe is a log message
+        *err_msg = android::base::StringPrintf("Hash parser '%s' failed with: %s",
+                                               config.checksum_parser.c_str(), hash->c_str());
+        return false;
+    }
+
+    return true;
+}
+
+bool SparseToBuf(sparse_file* sf, std::vector<char>* out, bool with_crc = false) {
+    int64_t len = sparse_file_len(sf, true, with_crc);
+    if (len <= 0) {
+        return false;
+    }
+    out->clear();
+    auto cb = [](void* priv, const void* data, size_t len) {
+        auto vec = static_cast<std::vector<char>*>(priv);
+        const char* cbuf = static_cast<const char*>(data);
+        vec->insert(vec->end(), cbuf, cbuf + len);
+        return 0;
+    };
+
+    return !sparse_file_callback(sf, true, with_crc, cb, out);
+}
+
+// Only allow alphanumeric, _, -, and .
+const auto not_allowed = [](char c) -> int {
+    return !(isalnum(c) || c == '_' || c == '-' || c == '.');
+};
+
+// Test that USB even works
+TEST(USBFunctionality, USBConnect) {
+    const auto matcher = [](usb_ifc_info* info) -> int {
+        return FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
+    };
+    Transport* transport = nullptr;
+    for (int i = 0; i < FastBootTest::MAX_USB_TRIES && !transport; i++) {
+        transport = usb_open(matcher);
+        std::this_thread::sleep_for(std::chrono::milliseconds(10));
+    }
+    ASSERT_NE(transport, nullptr) << "Could not find the fastboot device after: "
+                                  << 10 * FastBootTest::MAX_USB_TRIES << "ms";
+    if (transport) {
+        transport->Close();
+        delete transport;
+    }
+}
+
+// Test commands related to super partition
+TEST_F(LogicalPartitionCompliance, SuperPartition) {
+    ASSERT_TRUE(UserSpaceFastboot());
+    std::string partition_type;
+    // getvar partition-type:super must fail for retrofit devices because the
+    // partition does not exist.
+    if (fb->GetVar("partition-type:super", &partition_type) == SUCCESS) {
+        std::string is_logical;
+        EXPECT_EQ(fb->GetVar("is-logical:super", &is_logical), SUCCESS)
+                << "getvar is-logical:super failed";
+        EXPECT_EQ(is_logical, "no") << "super must not be a logical partition";
+        std::string super_name;
+        EXPECT_EQ(fb->GetVar("super-partition-name", &super_name), SUCCESS)
+                << "'getvar super-partition-name' failed";
+        EXPECT_EQ(super_name, "super") << "'getvar super-partition-name' must return 'super' for "
+                                          "device with a super partition";
+    }
+}
+
+// Test 'fastboot getvar is-logical'
+TEST_F(LogicalPartitionCompliance, GetVarIsLogical) {
+    ASSERT_TRUE(UserSpaceFastboot());
+    std::string has_slot;
+    EXPECT_EQ(fb->GetVar("has-slot:system", &has_slot), SUCCESS) << "getvar has-slot:system failed";
+    std::string is_logical_cmd_system = "is-logical:system";
+    std::string is_logical_cmd_vendor = "is-logical:vendor";
+    std::string is_logical_cmd_boot = "is-logical:boot";
+    if (has_slot == "yes") {
+        std::string current_slot;
+        ASSERT_EQ(fb->GetVar("current-slot", &current_slot), SUCCESS)
+                << "getvar current-slot failed";
+        std::string slot_suffix = "_" + current_slot;
+        is_logical_cmd_system += slot_suffix;
+        is_logical_cmd_vendor += slot_suffix;
+        is_logical_cmd_boot += slot_suffix;
+    }
+    std::string is_logical;
+    EXPECT_EQ(fb->GetVar(is_logical_cmd_system, &is_logical), SUCCESS)
+            << "system must be a logical partition";
+    EXPECT_EQ(is_logical, "yes");
+    EXPECT_EQ(fb->GetVar(is_logical_cmd_vendor, &is_logical), SUCCESS)
+            << "vendor must be a logical partition";
+    EXPECT_EQ(is_logical, "yes");
+    EXPECT_EQ(fb->GetVar(is_logical_cmd_boot, &is_logical), SUCCESS)
+            << "boot must not be logical partition";
+    EXPECT_EQ(is_logical, "no");
+}
+
+TEST_F(LogicalPartitionCompliance, FastbootRebootTest) {
+    ASSERT_TRUE(UserSpaceFastboot());
+    GTEST_LOG_(INFO) << "Rebooting to bootloader mode";
+    // Test 'fastboot reboot bootloader' from fastbootd
+    fb->RebootTo("bootloader");
+
+    // Test fastboot reboot fastboot from bootloader
+    ReconnectFastbootDevice();
+    ASSERT_FALSE(UserSpaceFastboot());
+    GTEST_LOG_(INFO) << "Rebooting back to fastbootd mode";
+    fb->RebootTo("fastboot");
+
+    ReconnectFastbootDevice();
+    ASSERT_TRUE(UserSpaceFastboot());
+}
+
+// Testing creation/resize/delete of logical partitions
+TEST_F(LogicalPartitionCompliance, CreateResizeDeleteLP) {
+    ASSERT_TRUE(UserSpaceFastboot());
+    std::string test_partition_name = "test_partition";
+    std::string slot_count;
+    // Add suffix to test_partition_name if device is slotted.
+    EXPECT_EQ(fb->GetVar("slot-count", &slot_count), SUCCESS) << "getvar slot-count failed";
+    int32_t num_slots = strtol(slot_count.c_str(), nullptr, 10);
+    if (num_slots > 0) {
+        std::string current_slot;
+        EXPECT_EQ(fb->GetVar("current-slot", &current_slot), SUCCESS)
+                << "getvar current-slot failed";
+        std::string slot_suffix = "_" + current_slot;
+        test_partition_name += slot_suffix;
+    }
+
+    GTEST_LOG_(INFO) << "Testing 'fastboot create-logical-partition' command";
+    EXPECT_EQ(fb->CreatePartition(test_partition_name, "0"), SUCCESS)
+            << "create-logical-partition failed";
+    GTEST_LOG_(INFO) << "Testing 'fastboot resize-logical-partition' command";
+    EXPECT_EQ(fb->ResizePartition(test_partition_name, "4096"), SUCCESS)
+            << "resize-logical-partition failed";
+    std::vector<char> buf(4096);
+
+    GTEST_LOG_(INFO) << "Flashing a logical partition..";
+    EXPECT_EQ(fb->FlashPartition(test_partition_name, buf), SUCCESS)
+            << "flash logical -partition failed";
+    GTEST_LOG_(INFO) << "Rebooting to bootloader mode";
+    // Reboot to bootloader mode and attempt to flash the logical partitions
+    fb->RebootTo("bootloader");
+
+    ReconnectFastbootDevice();
+    ASSERT_FALSE(UserSpaceFastboot());
+    GTEST_LOG_(INFO) << "Attempt to flash a logical partition..";
+    EXPECT_EQ(fb->FlashPartition(test_partition_name, buf), DEVICE_FAIL)
+            << "flash logical partition must fail in bootloader";
+    GTEST_LOG_(INFO) << "Rebooting back to fastbootd mode";
+    fb->RebootTo("fastboot");
+
+    ReconnectFastbootDevice();
+    ASSERT_TRUE(UserSpaceFastboot());
+    GTEST_LOG_(INFO) << "Testing 'fastboot delete-logical-partition' command";
+    EXPECT_EQ(fb->DeletePartition(test_partition_name), SUCCESS)
+            << "delete logical-partition failed";
+}
+
+// Conformance tests
+TEST_F(Conformance, GetVar) {
+    std::string product;
+    EXPECT_EQ(fb->GetVar("product", &product), SUCCESS) << "getvar:product failed";
+    EXPECT_NE(product, "") << "getvar:product response was empty string";
+    EXPECT_EQ(std::count_if(product.begin(), product.end(), not_allowed), 0)
+            << "getvar:product response contained illegal chars";
+    EXPECT_LE(product.size(), FB_RESPONSE_SZ - 4) << "getvar:product response was too large";
+}
+
+TEST_F(Conformance, GetVarVersionBootloader) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("version-bootloader", &var), SUCCESS)
+            << "getvar:version-bootloader failed";
+    EXPECT_NE(var, "") << "getvar:version-bootloader response was empty string";
+    EXPECT_EQ(std::count_if(var.begin(), var.end(), not_allowed), 0)
+            << "getvar:version-bootloader response contained illegal chars";
+    EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4) << "getvar:version-bootloader response was too large";
+}
+
+TEST_F(Conformance, GetVarVersionBaseband) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("version-baseband", &var), SUCCESS) << "getvar:version-baseband failed";
+    EXPECT_NE(var, "") << "getvar:version-baseband response was empty string";
+    EXPECT_EQ(std::count_if(var.begin(), var.end(), not_allowed), 0)
+            << "getvar:version-baseband response contained illegal chars";
+    EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4) << "getvar:version-baseband response was too large";
+}
+
+TEST_F(Conformance, GetVarSerialNo) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("serialno", &var), SUCCESS) << "getvar:serialno failed";
+    EXPECT_NE(var, "") << "getvar:serialno can not be empty string";
+    EXPECT_EQ(std::count_if(var.begin(), var.end(), isalnum), var.size())
+            << "getvar:serialno must be alpha-numeric";
+    EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4) << "getvar:serialno response is too long";
+}
+
+TEST_F(Conformance, GetVarSecure) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("secure", &var), SUCCESS);
+    EXPECT_TRUE(var == "yes" || var == "no");
+}
+
+TEST_F(Conformance, GetVarOffModeCharge) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("off-mode-charge", &var), SUCCESS) << "getvar:off-mode-charge failed";
+    EXPECT_TRUE(var == "0" || var == "1") << "getvar:off-mode-charge response must be '0' or '1'";
+}
+
+TEST_F(Conformance, GetVarVariant) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("variant", &var), SUCCESS) << "getvar:variant failed";
+    EXPECT_NE(var, "") << "getvar:variant response can not be empty";
+    EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4) << "getvar:variant response is too large";
+}
+
+TEST_F(Conformance, GetVarRevision) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("hw-revision", &var), SUCCESS) << "getvar:hw-revision failed";
+    EXPECT_NE(var, "") << "getvar:battery-voltage response was empty";
+    EXPECT_EQ(std::count_if(var.begin(), var.end(), not_allowed), 0)
+            << "getvar:hw-revision contained illegal ASCII chars";
+    EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4) << "getvar:hw-revision response was too large";
+}
+
+TEST_F(Conformance, GetVarBattVoltage) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("battery-voltage", &var), SUCCESS) << "getvar:battery-voltage failed";
+    EXPECT_NE(var, "") << "getvar:battery-voltage response was empty";
+    EXPECT_EQ(std::count_if(var.begin(), var.end(), not_allowed), 0)
+            << "getvar:battery-voltage response contains illegal ASCII chars";
+    EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4)
+            << "getvar:battery-voltage response is too large: " + var;
+}
+
+TEST_F(Conformance, GetVarBattVoltageOk) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("battery-soc-ok", &var), SUCCESS) << "getvar:battery-soc-ok failed";
+    EXPECT_TRUE(var == "yes" || var == "no") << "getvar:battery-soc-ok must be 'yes' or 'no'";
+}
+
+TEST_F(Conformance, GetVarDownloadSize) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "getvar:max-download-size failed";
+    EXPECT_NE(var, "") << "getvar:max-download-size responded with empty string";
+    // This must start with 0x
+    EXPECT_FALSE(isspace(var.front()))
+            << "getvar:max-download-size responded with a string with leading whitespace";
+    EXPECT_FALSE(var.compare(0, 2, "0x"))
+            << "getvar:max-download-size responded with a string that does not start with 0x...";
+    int64_t size = strtoll(var.c_str(), nullptr, 16);
+    EXPECT_GT(size, 0) << "'" + var + "' is not a valid response from getvar:max-download-size";
+    // At most 32-bits
+    EXPECT_LE(size, std::numeric_limits<uint32_t>::max())
+            << "getvar:max-download-size must fit in a uint32_t";
+    EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4)
+            << "getvar:max-download-size responded with too large of string: " + var;
+}
+
+TEST_F(Conformance, GetVarAll) {
+    std::vector<std::string> vars;
+    EXPECT_EQ(fb->GetVarAll(&vars), SUCCESS) << "getvar:all failed";
+    EXPECT_GT(vars.size(), 0) << "getvar:all did not respond with any INFO responses";
+    for (const auto& s : vars) {
+        EXPECT_LE(s.size(), FB_RESPONSE_SZ - 4)
+                << "getvar:all included an INFO response: 'INFO" + s << "' which is too long";
+    }
+}
+
+TEST_F(Conformance, UnlockAbility) {
+    std::string resp;
+    std::vector<std::string> info;
+    // Userspace fastboot implementations do not have a way to get this
+    // information.
+    if (UserSpaceFastboot()) {
+        GTEST_LOG_(INFO) << "This test is skipped for userspace fastboot.";
+        return;
+    }
+    EXPECT_EQ(fb->RawCommand("flashing get_unlock_ability", &resp, &info), SUCCESS)
+            << "'flashing get_unlock_ability' failed";
+    // There are two ways this can be reported, through info or the actual response
+    char last;
+    if (!resp.empty()) {  // must be in the response
+        last = resp.back();
+    } else {  // else must be in info
+        ASSERT_FALSE(info.empty()) << "'flashing get_unlock_ability' returned empty response";
+        ASSERT_FALSE(info.back().empty()) << "Expected non-empty info response";
+        last = info.back().back();
+    }
+    ASSERT_TRUE(last == '1' || last == '0') << "Unlock ability must report '0' or '1' in response";
+}
+
+TEST_F(Conformance, PartitionInfo) {
+    std::vector<std::tuple<std::string, uint64_t>> parts;
+    EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
+    EXPECT_GT(parts.size(), 0)
+            << "getvar:all did not report any partition-size: through INFO responses";
+    std::set<std::string> allowed{"ext4", "f2fs", "raw"};
+    for (const auto& p : parts) {
+        EXPECT_GE(std::get<1>(p), 0);
+        std::string part(std::get<0>(p));
+        std::set<std::string> allowed{"ext4", "f2fs", "raw"};
+        std::string resp;
+        EXPECT_EQ(fb->GetVar("partition-type:" + part, &resp), SUCCESS);
+        EXPECT_NE(allowed.find(resp), allowed.end()) << "getvar:partition-type:" + part << " was '"
+                                                     << resp << "' this is not a valid type";
+        const std::string cmd = "partition-size:" + part;
+        EXPECT_EQ(fb->GetVar(cmd, &resp), SUCCESS);
+
+        // This must start with 0x
+        EXPECT_FALSE(isspace(resp.front()))
+                << cmd + " responded with a string with leading whitespace";
+        EXPECT_FALSE(resp.compare(0, 2, "0x"))
+                << cmd + "responded with a string that does not start with 0x...";
+        uint64_t size;
+        ASSERT_TRUE(android::base::ParseUint(resp, &size))
+                << "'" + resp + "' is not a valid response from " + cmd;
+    }
+}
+
+TEST_F(Conformance, Slots) {
+    std::string var;
+    ASSERT_EQ(fb->GetVar("slot-count", &var), SUCCESS) << "getvar:slot-count failed";
+    ASSERT_EQ(std::count_if(var.begin(), var.end(), isdigit), var.size())
+            << "'" << var << "' is not all digits which it should be for getvar:slot-count";
+    int32_t num_slots = strtol(var.c_str(), nullptr, 10);
+
+    // Can't run out of alphabet letters...
+    ASSERT_LE(num_slots, 26) << "What?! You can't have more than 26 slots";
+
+    std::vector<std::tuple<std::string, uint64_t>> parts;
+    EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
+
+    std::map<std::string, std::set<char>> part_slots;
+    if (num_slots > 0) {
+        EXPECT_EQ(fb->GetVar("current-slot", &var), SUCCESS) << "getvar:current-slot failed";
+
+        for (const auto& p : parts) {
+            std::string part(std::get<0>(p));
+            std::regex reg("([[:graph:]]*)_([[:lower:]])");
+            std::smatch sm;
+
+            if (std::regex_match(part, sm, reg)) {  // This partition has slots
+                std::string part_base(sm[1]);
+                std::string slot(sm[2]);
+                EXPECT_EQ(fb->GetVar("has-slot:" + part_base, &var), SUCCESS)
+                        << "'getvar:has-slot:" << part_base << "' failed";
+                EXPECT_EQ(var, "yes") << "'getvar:has-slot:" << part_base << "' was not 'yes'";
+                EXPECT_TRUE(islower(slot.front()))
+                        << "'" << slot.front() << "' is an invalid slot-suffix for " << part_base;
+                std::set<char> tmp{slot.front()};
+                part_slots.emplace(part_base, tmp);
+                part_slots.at(part_base).insert(slot.front());
+            } else {
+                EXPECT_EQ(fb->GetVar("has-slot:" + part, &var), SUCCESS)
+                        << "'getvar:has-slot:" << part << "' failed";
+                EXPECT_EQ(var, "no") << "'getvar:has-slot:" << part << "' should be no";
+            }
+        }
+        // Ensure each partition has the correct slot suffix
+        for (const auto& iter : part_slots) {
+            const std::set<char>& char_set = iter.second;
+            std::string chars;
+            for (char c : char_set) {
+                chars += c;
+                chars += ',';
+            }
+            EXPECT_EQ(char_set.size(), num_slots)
+                    << "There should only be slot suffixes from a to " << 'a' + num_slots - 1
+                    << " instead encountered: " << chars;
+            for (const char c : char_set) {
+                EXPECT_GE(c, 'a') << "Encountered invalid slot suffix of '" << c << "'";
+                EXPECT_LT(c, 'a' + num_slots) << "Encountered invalid slot suffix of '" << c << "'";
+            }
+        }
+    }
+}
+
+TEST_F(Conformance, SetActive) {
+    std::string var;
+    ASSERT_EQ(fb->GetVar("slot-count", &var), SUCCESS) << "getvar:slot-count failed";
+    ASSERT_EQ(std::count_if(var.begin(), var.end(), isdigit), var.size())
+            << "'" << var << "' is not all digits which it should be for getvar:slot-count";
+    int32_t num_slots = strtol(var.c_str(), nullptr, 10);
+
+    // Can't run out of alphabet letters...
+    ASSERT_LE(num_slots, 26) << "You can't have more than 26 slots";
+
+    for (char c = 'a'; c < 'a' + num_slots; c++) {
+        const std::string slot(&c, &c + 1);
+        ASSERT_EQ(fb->SetActive(slot), SUCCESS) << "Set active for slot '" << c << "' failed";
+        ASSERT_EQ(fb->GetVar("current-slot", &var), SUCCESS) << "getvar:current-slot failed";
+        EXPECT_EQ(var, slot) << "getvar:current-slot repots incorrect slot after setting it";
+    }
+}
+
+TEST_F(Conformance, LockAndUnlockPrompt) {
+    std::string resp;
+    ASSERT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
+    ASSERT_TRUE(resp == "yes" || resp == "no")
+            << "Device did not respond with 'yes' or 'no' for getvar:unlocked";
+    bool curr = resp == "yes";
+    if (UserSpaceFastboot()) {
+        GTEST_LOG_(INFO) << "This test is skipped for userspace fastboot.";
+        return;
+    }
+
+    for (int i = 0; i < 2; i++) {
+        std::string action = !curr ? "unlock" : "lock";
+        printf("Device should prompt to '%s' bootloader, select 'no'\n", action.c_str());
+        SetLockState(!curr, false);
+        ASSERT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
+        ASSERT_EQ(resp, curr ? "yes" : "no") << "The locked/unlocked state of the bootloader "
+                                                "incorrectly changed after selecting no";
+        printf("Device should prompt to '%s' bootloader, select 'yes'\n", action.c_str());
+        SetLockState(!curr, true);
+        ASSERT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
+        ASSERT_EQ(resp, !curr ? "yes" : "no") << "The locked/unlocked state of the bootloader "
+                                                 "failed to change after selecting yes";
+        curr = !curr;
+    }
+}
+
+TEST_F(Conformance, SparseBlockSupport0) {
+    // The sparse block size can be any multiple of 4
+    std::string var;
+    EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "getvar:max-download-size failed";
+    int64_t size = strtoll(var.c_str(), nullptr, 16);
+
+    // It is reasonable to expect it to handle a single dont care block equal to its DL size
+    for (int64_t bs = 4; bs < size; bs <<= 1) {
+        SparseWrapper sparse(bs, bs);
+        ASSERT_TRUE(*sparse) << "Sparse file creation failed on: " << bs;
+        EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+        EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+    }
+}
+
+TEST_F(Conformance, SparseBlockSupport1) {
+    // The sparse block size can be any multiple of 4
+    std::string var;
+    EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "getvar:max-download-size failed";
+    int64_t size = strtoll(var.c_str(), nullptr, 16);
+
+    // handle a packed block to half its max download size block
+    for (int64_t bs = 4; bs < size / 2; bs <<= 1) {
+        SparseWrapper sparse(bs, bs);
+        ASSERT_TRUE(*sparse) << "Sparse file creation failed on: " << bs;
+        std::vector<char> buf = RandomBuf(bs);
+        ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
+                << "Adding data failed to sparse file: " << sparse.Rep();
+        EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+        EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+    }
+}
+
+// A single don't care download
+TEST_F(Conformance, SparseDownload0) {
+    SparseWrapper sparse(4096, 4096);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+}
+
+TEST_F(Conformance, SparseDownload1) {
+    SparseWrapper sparse(4096, 10 * 4096);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    std::vector<char> buf = RandomBuf(4096);
+    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 9), 0)
+            << "Adding data failed to sparse file: " << sparse.Rep();
+    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+}
+
+TEST_F(Conformance, SparseDownload2) {
+    SparseWrapper sparse(4096, 4097);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    std::vector<char> buf = RandomBuf(4096);
+    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
+            << "Adding data failed to sparse file: " << sparse.Rep();
+    std::vector<char> buf2 = RandomBuf(1);
+    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 1), 0)
+            << "Adding data failed to sparse file: " << sparse.Rep();
+    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+}
+
+TEST_F(Conformance, SparseDownload3) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "getvar:max-download-size failed";
+    int size = strtoll(var.c_str(), nullptr, 16);
+
+    SparseWrapper sparse(4096, size);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    // Don't want this to take forever
+    unsigned num_chunks = std::min(1000, size / (2 * 4096));
+    for (int i = 0; i < num_chunks; i++) {
+        std::vector<char> buf;
+        int r = random_int(0, 2);
+        // Three cases
+        switch (r) {
+            case 0:
+                break;  // Dont Care chunnk
+            case 1:     // Buffer
+                buf = RandomBuf(4096);
+                ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), i), 0)
+                        << "Adding data failed to sparse file: " << sparse.Rep();
+                break;
+            case 2:  // fill
+                ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, 4096, i), 0)
+                        << "Adding fill to sparse file failed: " << sparse.Rep();
+                break;
+        }
+    }
+    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+}
+
+TEST_F(Conformance, SparseVersionCheck) {
+    SparseWrapper sparse(4096, 4096);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    std::vector<char> buf;
+    ASSERT_TRUE(SparseToBuf(*sparse, &buf)) << "Sparse buffer creation failed";
+    // Invalid, right after magic
+    buf[4] = 0xff;
+    ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
+    ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
+
+    // It can either reject this download or reject it during flash
+    if (HandleResponse() != DEVICE_FAIL) {
+        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+                << "Flashing an invalid sparse version should fail " << sparse.Rep();
+    }
+}
+
+TEST_F(UnlockPermissions, Download) {
+    std::vector<char> buf{'a', 'o', 's', 'p'};
+    EXPECT_EQ(fb->Download(buf), SUCCESS) << "Download 4-byte payload failed";
+}
+
+TEST_F(UnlockPermissions, DownloadFlash) {
+    std::vector<char> buf{'a', 'o', 's', 'p'};
+    EXPECT_EQ(fb->Download(buf), SUCCESS) << "Download failed in unlocked mode";
+    ;
+    std::vector<std::tuple<std::string, uint64_t>> parts;
+    EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed in unlocked mode";
+}
+
+TEST_F(LockPermissions, DownloadFlash) {
+    std::vector<char> buf{'a', 'o', 's', 'p'};
+    EXPECT_EQ(fb->Download(buf), SUCCESS) << "Download failed in locked mode";
+    std::vector<std::tuple<std::string, uint64_t>> parts;
+    EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed in locked mode";
+    std::string resp;
+    for (const auto& tup : parts) {
+        EXPECT_EQ(fb->Flash(std::get<0>(tup), &resp), DEVICE_FAIL)
+                << "Device did not respond with FAIL when trying to flash '" << std::get<0>(tup)
+                << "' in locked mode";
+        EXPECT_GT(resp.size(), 0)
+                << "Device sent empty error message after FAIL";  // meaningful error message
+    }
+}
+
+TEST_F(LockPermissions, Erase) {
+    std::vector<std::tuple<std::string, uint64_t>> parts;
+    EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
+    std::string resp;
+    for (const auto& tup : parts) {
+        EXPECT_EQ(fb->Erase(std::get<0>(tup), &resp), DEVICE_FAIL)
+                << "Device did not respond with FAIL when trying to erase '" << std::get<0>(tup)
+                << "' in locked mode";
+        EXPECT_GT(resp.size(), 0) << "Device sent empty error message after FAIL";
+    }
+}
+
+TEST_F(LockPermissions, SetActive) {
+    std::vector<std::tuple<std::string, uint64_t>> parts;
+    EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
+
+    std::string resp;
+    EXPECT_EQ(fb->GetVar("slot-count", &resp), SUCCESS) << "getvar:slot-count failed";
+    int32_t num_slots = strtol(resp.c_str(), nullptr, 10);
+
+    for (const auto& tup : parts) {
+        std::string part(std::get<0>(tup));
+        std::regex reg("([[:graph:]]*)_([[:lower:]])");
+        std::smatch sm;
+
+        if (std::regex_match(part, sm, reg)) {  // This partition has slots
+            std::string part_base(sm[1]);
+            for (char c = 'a'; c < 'a' + num_slots; c++) {
+                // We should not be able to SetActive any of these
+                EXPECT_EQ(fb->SetActive(part_base + '_' + c, &resp), DEVICE_FAIL)
+                        << "set:active:" << part_base + '_' + c << " did not fail in locked mode";
+            }
+        }
+    }
+}
+
+TEST_F(LockPermissions, Boot) {
+    std::vector<char> buf;
+    buf.resize(1000);
+    EXPECT_EQ(fb->Download(buf), SUCCESS) << "A 1000 byte download failed";
+    std::string resp;
+    ASSERT_EQ(fb->Boot(&resp), DEVICE_FAIL)
+            << "The device did not respond with failure for 'boot' when locked";
+    EXPECT_GT(resp.size(), 0) << "No error message was returned by device after FAIL";
+}
+
+TEST_F(Fuzz, DownloadSize) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "getvar:max-download-size failed";
+    int64_t size = strtoll(var.c_str(), nullptr, 0);
+    EXPECT_GT(size, 0) << '\'' << var << "' is not a valid response for getvar:max-download-size";
+
+    EXPECT_EQ(DownloadCommand(size + 1), DEVICE_FAIL)
+            << "Device reported max-download-size as '" << size
+            << "' but did not reject a download of " << size + 1;
+
+    std::vector<char> buf(size);
+    EXPECT_EQ(fb->Download(buf), SUCCESS) << "Device reported max-download-size as '" << size
+                                          << "' but downloading a payload of this size failed";
+    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
+}
+
+TEST_F(Fuzz, DownloadPartialBuf) {
+    std::vector<char> buf{'a', 'o', 's', 'p'};
+    ASSERT_EQ(DownloadCommand(buf.size() + 1), SUCCESS)
+            << "Download command for " << buf.size() + 1 << " bytes failed";
+
+    std::string resp;
+    RetCode ret = SendBuffer(buf);
+    EXPECT_EQ(ret, SUCCESS) << "Device did not accept partial payload download";
+    // Send the partial buffer, then cancel it with a reset
+    EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
+
+    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
+    // The device better still work after all that if we unplug and replug
+    EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS) << "getvar:product failed";
+}
+
+TEST_F(Fuzz, DownloadOverRun) {
+    std::vector<char> buf(1000, 'F');
+    ASSERT_EQ(DownloadCommand(10), SUCCESS) << "Device rejected download request for 10 bytes";
+    // There are two ways to handle this
+    // Accept download, but send error response
+    // Reject the download outright
+    std::string resp;
+    RetCode ret = SendBuffer(buf);
+    if (ret == SUCCESS) {
+        // If it accepts the buffer, it better send back an error response
+        EXPECT_EQ(HandleResponse(&resp), DEVICE_FAIL)
+                << "After sending too large of a payload for a download command, device accepted "
+                   "payload and did not respond with FAIL";
+    } else {
+        EXPECT_EQ(ret, IO_ERROR) << "After sending too large of a payload for a download command, "
+                                    "device did not return error";
+    }
+
+    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
+    // The device better still work after all that if we unplug and replug
+    EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
+    EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
+            << "Device did not respond with SUCCESS to getvar:product.";
+}
+
+TEST_F(Fuzz, DownloadInvalid1) {
+    EXPECT_EQ(DownloadCommand(0), DEVICE_FAIL)
+            << "Device did not respond with FAIL for malformed download command 'download:0'";
+}
+
+TEST_F(Fuzz, DownloadInvalid2) {
+    std::string cmd("download:1");
+    EXPECT_EQ(fb->RawCommand("download:1"), DEVICE_FAIL)
+            << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
+}
+
+TEST_F(Fuzz, DownloadInvalid3) {
+    std::string cmd("download:-1");
+    EXPECT_EQ(fb->RawCommand("download:-1"), DEVICE_FAIL)
+            << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
+}
+
+TEST_F(Fuzz, DownloadInvalid4) {
+    std::string cmd("download:-01000000");
+    EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
+            << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
+}
+
+TEST_F(Fuzz, DownloadInvalid5) {
+    std::string cmd("download:-0100000");
+    EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
+            << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
+}
+
+TEST_F(Fuzz, DownloadInvalid6) {
+    std::string cmd("download:");
+    EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
+            << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
+}
+
+TEST_F(Fuzz, DownloadInvalid7) {
+    std::string cmd("download:01000000\0999", sizeof("download:01000000\0999"));
+    EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
+            << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
+}
+
+TEST_F(Fuzz, DownloadInvalid8) {
+    std::string cmd("download:01000000\0dkjfvijafdaiuybgidabgybr",
+                    sizeof("download:01000000\0dkjfvijafdaiuybgidabgybr"));
+    EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
+            << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
+}
+
+TEST_F(Fuzz, GetVarAllSpam) {
+    auto start = std::chrono::high_resolution_clock::now();
+    std::chrono::duration<double> elapsed;
+    unsigned i = 1;
+    do {
+        std::vector<std::string> vars;
+        ASSERT_EQ(fb->GetVarAll(&vars), SUCCESS) << "Device did not respond with success after "
+                                                 << i << "getvar:all commands in a row";
+        ASSERT_GT(vars.size(), 0)
+                << "Device did not send any INFO responses after getvar:all command";
+        elapsed = std::chrono::high_resolution_clock::now() - start;
+    } while (i++, elapsed.count() < 5);
+}
+
+TEST_F(Fuzz, BadCommandTooLarge) {
+    std::string s = RandomString(FB_COMMAND_SZ + 1, rand_legal);
+    EXPECT_EQ(fb->RawCommand(s), DEVICE_FAIL)
+            << "Device did not respond with failure after sending length " << s.size()
+            << " string of random ASCII chars";
+    std::string s1 = RandomString(1000, rand_legal);
+    EXPECT_EQ(fb->RawCommand(s1), DEVICE_FAIL)
+            << "Device did not respond with failure after sending length " << s1.size()
+            << " string of random ASCII chars";
+    std::string s2 = RandomString(1000, rand_illegal);
+    EXPECT_EQ(fb->RawCommand(s2), DEVICE_FAIL)
+            << "Device did not respond with failure after sending length " << s1.size()
+            << " string of random non-ASCII chars";
+    std::string s3 = RandomString(1000, rand_char);
+    EXPECT_EQ(fb->RawCommand(s3), DEVICE_FAIL)
+            << "Device did not respond with failure after sending length " << s1.size()
+            << " string of random chars";
+}
+
+TEST_F(Fuzz, CommandTooLarge) {
+    for (const std::string& s : CMDS) {
+        std::string rs = RandomString(1000, rand_char);
+        EXPECT_EQ(fb->RawCommand(s + rs), DEVICE_FAIL)
+                << "Device did not respond with failure after '" << s + rs << "'";
+        ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
+        std::string resp;
+        EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
+                << "Device is unresponsive to getvar command";
+    }
+}
+
+TEST_F(Fuzz, CommandMissingArgs) {
+    for (const std::string& s : CMDS) {
+        if (s.back() == ':') {
+            EXPECT_EQ(fb->RawCommand(s), DEVICE_FAIL)
+                    << "Device did not respond with failure after '" << s << "'";
+            std::string sub(s.begin(), s.end() - 1);
+            EXPECT_EQ(fb->RawCommand(sub), DEVICE_FAIL)
+                    << "Device did not respond with failure after '" << sub << "'";
+        } else {
+            std::string rs = RandomString(10, rand_illegal);
+            EXPECT_EQ(fb->RawCommand(rs + s), DEVICE_FAIL)
+                    << "Device did not respond with failure after '" << rs + s << "'";
+        }
+        std::string resp;
+        EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
+                << "Device is unresponsive to getvar command";
+    }
+}
+
+TEST_F(Fuzz, SparseZeroLength) {
+    SparseWrapper sparse(4096, 0);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    RetCode ret = fb->Download(*sparse);
+    // Two ways to handle it
+    if (ret != DEVICE_FAIL) {  // if lazily parsed it better fail on a flash
+        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+                << "Flashing zero length sparse image did not fail: " << sparse.Rep();
+    }
+    ret = fb->Download(*sparse, true);
+    if (ret != DEVICE_FAIL) {  // if lazily parsed it better fail on a flash
+        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+                << "Flashing zero length sparse image did not fail " << sparse.Rep();
+    }
+}
+
+TEST_F(Fuzz, SparseTooManyChunks) {
+    SparseWrapper sparse(4096, 4096);  // 1 block, but we send two chunks that will use 2 blocks
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    std::vector<char> buf = RandomBuf(4096);
+    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
+            << "Adding data failed to sparse file: " << sparse.Rep();
+    // We take advantage of the fact the sparse library does not check this
+    ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, 4096, 1), 0)
+            << "Adding fill to sparse file failed: " << sparse.Rep();
+
+    RetCode ret = fb->Download(*sparse);
+    // Two ways to handle it
+    if (ret != DEVICE_FAIL) {  // if lazily parsed it better fail on a flash
+        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+                << "Flashing sparse image with 'total_blks' in header 1 too small did not fail "
+                << sparse.Rep();
+    }
+    ret = fb->Download(*sparse, true);
+    if (ret != DEVICE_FAIL) {  // if lazily parsed it better fail on a flash
+        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+                << "Flashing sparse image with 'total_blks' in header 1 too small did not fail "
+                << sparse.Rep();
+    }
+}
+
+TEST_F(Fuzz, USBResetSpam) {
+    auto start = std::chrono::high_resolution_clock::now();
+    std::chrono::duration<double> elapsed;
+    int i = 0;
+    do {
+        ASSERT_EQ(transport->Reset(), 0) << "USB Reset failed after " << i << " resets in a row";
+        elapsed = std::chrono::high_resolution_clock::now() - start;
+    } while (i++, elapsed.count() < 5);
+    std::string resp;
+    EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
+            << "getvar failed after " << i << " USB reset(s) in a row";
+}
+
+TEST_F(Fuzz, USBResetCommandSpam) {
+    auto start = std::chrono::high_resolution_clock::now();
+    std::chrono::duration<double> elapsed;
+    do {
+        std::string resp;
+        std::vector<std::string> all;
+        ASSERT_EQ(transport->Reset(), 0) << "USB Reset failed";
+        EXPECT_EQ(fb->GetVarAll(&all), SUCCESS) << "getvar:all failed after USB reset";
+        EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS) << "getvar:product failed";
+        elapsed = std::chrono::high_resolution_clock::now() - start;
+    } while (elapsed.count() < 10);
+}
+
+TEST_F(Fuzz, USBResetAfterDownload) {
+    std::vector<char> buf;
+    buf.resize(1000000);
+    EXPECT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Download command failed";
+    EXPECT_EQ(transport->Reset(), 0) << "USB Reset failed";
+    std::vector<std::string> all;
+    EXPECT_EQ(fb->GetVarAll(&all), SUCCESS) << "getvar:all failed after USB reset.";
+}
+
+// Getvar XML tests
+TEST_P(ExtensionsGetVarConformance, VarExists) {
+    std::string resp;
+    EXPECT_EQ(fb->GetVar(GetParam().first, &resp), SUCCESS);
+}
+
+TEST_P(ExtensionsGetVarConformance, VarMatchesRegex) {
+    std::string resp;
+    ASSERT_EQ(fb->GetVar(GetParam().first, &resp), SUCCESS);
+    std::smatch sm;
+    std::regex_match(resp, sm, GetParam().second.regex);
+    EXPECT_FALSE(sm.empty()) << "The regex did not match";
+}
+
+INSTANTIATE_TEST_CASE_P(XMLGetVar, ExtensionsGetVarConformance,
+                        ::testing::ValuesIn(GETVAR_XML_TESTS));
+
+TEST_P(AnyPartition, ReportedGetVarAll) {
+    // As long as the partition is reported in INFO, it would be tested by generic Conformance
+    std::vector<std::tuple<std::string, uint64_t>> parts;
+    ASSERT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
+    const std::string name = GetParam().first;
+    if (GetParam().second.slots) {
+        auto matcher = [&](const std::tuple<std::string, uint32_t>& tup) {
+            return std::get<0>(tup) == name + "_a";
+        };
+        EXPECT_NE(std::find_if(parts.begin(), parts.end(), matcher), parts.end())
+                << "partition '" + name + "_a' not reported in getvar:all";
+    } else {
+        auto matcher = [&](const std::tuple<std::string, uint32_t>& tup) {
+            return std::get<0>(tup) == name;
+        };
+        EXPECT_NE(std::find_if(parts.begin(), parts.end(), matcher), parts.end())
+                << "partition '" + name + "' not reported in getvar:all";
+    }
+}
+
+TEST_P(AnyPartition, Hashable) {
+    const std::string name = GetParam().first;
+    if (!config.checksum.empty()) {  // We can use hash to validate
+        for (const auto& part_name : real_parts) {
+            // Get hash
+            std::string hash;
+            int retcode;
+            std::string err_msg;
+            if (GetParam().second.hashable) {
+                ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))
+                        << err_msg;
+                EXPECT_EQ(retcode, 0) << err_msg;
+            } else {  // Make sure it fails
+                const std::string cmd = config.checksum + ' ' + part_name;
+                EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
+                        << part_name + " is marked as non-hashable, but hashing did not fail";
+            }
+        }
+    }
+}
+
+TEST_P(WriteablePartition, FlashCheck) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+
+    for (const auto& part_name : real_parts) {
+        std::vector<char> buf = RandomBuf(max_flash, rand_char);
+        EXPECT_EQ(fb->FlashPartition(part_name, buf), part_info.parsed ? DEVICE_FAIL : SUCCESS)
+                << "A partition with an image parsed by the bootloader should reject random "
+                   "garbage "
+                   "otherwise it should succeed";
+    }
+}
+
+TEST_P(WriteablePartition, EraseCheck) {
+    const std::string name = GetParam().first;
+
+    for (const auto& part_name : real_parts) {
+        ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
+    }
+}
+
+TEST_P(WriteHashNonParsedPartition, EraseZerosData) {
+    const std::string name = GetParam().first;
+
+    for (const auto& part_name : real_parts) {
+        std::string err_msg;
+        int retcode;
+        const std::vector<char> buf = RandomBuf(max_flash, rand_char);
+        // Partition is too big to write to entire thing
+        // This can eventually be supported by using sparse images if too large
+        if (max_flash < part_size) {
+            std::string hash_before, hash_after;
+            ASSERT_EQ(fb->FlashPartition(part_name, buf), SUCCESS);
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_NE(hash_before, hash_after)
+                    << "The partition hash for " + part_name +
+                               " did not change after erasing a known value";
+        } else {
+            std::string hash_zeros, hash_ones, hash_middle, hash_after;
+            const std::vector<char> buf_zeros(max_flash, 0);
+            const std::vector<char> buf_ones(max_flash, -1);  // All bits are set to 1
+            ASSERT_EQ(fb->FlashPartition(part_name, buf_zeros), SUCCESS);
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_zeros, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            ASSERT_EQ(fb->FlashPartition(part_name, buf_ones), SUCCESS);
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_ones, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            ASSERT_NE(hash_zeros, hash_ones)
+                    << "Hashes of partion should not be the same when all bytes are 0xFF or 0x00";
+            ASSERT_EQ(fb->FlashPartition(part_name, buf), SUCCESS);
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_middle, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            ASSERT_NE(hash_zeros, hash_middle)
+                    << "Hashes of partion are the same when all bytes are 0x00 or test payload";
+            ASSERT_NE(hash_ones, hash_middle)
+                    << "Hashes of partion are the same when all bytes are 0xFF or test payload";
+            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_TRUE(hash_zeros == hash_after || hash_ones == hash_after)
+                    << "Erasing " + part_name + " should set all the bytes to 0xFF or 0x00";
+        }
+    }
+}
+
+// Only partitions that we can write and hash (name, fixture), TEST_P is (Fixture, test_name)
+INSTANTIATE_TEST_CASE_P(XMLPartitionsWriteHashNonParsed, WriteHashNonParsedPartition,
+                        ::testing::ValuesIn(PARTITION_XML_WRITE_HASH_NONPARSED));
+
+INSTANTIATE_TEST_CASE_P(XMLPartitionsWriteHashable, WriteHashablePartition,
+                        ::testing::ValuesIn(PARTITION_XML_WRITE_HASHABLE));
+
+// only partitions writeable
+INSTANTIATE_TEST_CASE_P(XMLPartitionsWriteable, WriteablePartition,
+                        ::testing::ValuesIn(PARTITION_XML_WRITEABLE));
+
+// Every partition
+INSTANTIATE_TEST_CASE_P(XMLPartitionsAll, AnyPartition, ::testing::ValuesIn(PARTITION_XML_TESTS));
+
+// Partition Fuzz tests
+TEST_P(FuzzWriteablePartition, BoundsCheck) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+
+    for (const auto& part_name : real_parts) {
+        // try and flash +1 too large, first erase and get a hash, make sure it does not change
+        std::vector<char> buf = RandomBuf(max_flash + 1);  // One too large
+        if (part_info.hashable) {
+            std::string hash_before, hash_after, err_msg;
+            int retcode;
+            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                    << "Flashing an image 1 byte too large to " + part_name + " did not fail";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_EQ(hash_before, hash_after)
+                    << "Flashing too large of an image resulted in a changed partition hash for " +
+                               part_name;
+        } else {
+            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                    << "Flashing an image 1 byte too large to " + part_name + " did not fail";
+        }
+    }
+}
+
+INSTANTIATE_TEST_CASE_P(XMLFuzzPartitionsWriteable, FuzzWriteablePartition,
+                        ::testing::ValuesIn(PARTITION_XML_WRITEABLE));
+
+// A parsed partition should have magic and such that is checked by the bootloader
+// Attempting to flash a random single byte should definately fail
+TEST_P(FuzzWriteableParsedPartition, FlashGarbageImageSmall) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+
+    for (const auto& part_name : real_parts) {
+        std::vector<char> buf = RandomBuf(1);
+        if (part_info.hashable) {
+            std::string hash_before, hash_after, err_msg;
+            int retcode;
+            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                    << "A parsed partition should fail on a single byte";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_EQ(hash_before, hash_after)
+                    << "Flashing a single byte to parsed partition  " + part_name +
+                               " should fail and not change the partition hash";
+        } else {
+            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                    << "Flashing a 1 byte image to a parsed partition should fail";
+        }
+    }
+}
+
+TEST_P(FuzzWriteableParsedPartition, FlashGarbageImageLarge) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+
+    for (const auto& part_name : real_parts) {
+        std::vector<char> buf = RandomBuf(max_flash);
+        if (part_info.hashable) {
+            std::string hash_before, hash_after, err_msg;
+            int retcode;
+            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                    << "A parsed partition should not accept randomly generated images";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_EQ(hash_before, hash_after)
+                    << "The hash of the partition has changed after attempting to flash garbage to "
+                       "a parsed partition";
+        } else {
+            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                    << "A parsed partition should not accept randomly generated images";
+        }
+    }
+}
+
+TEST_P(FuzzWriteableParsedPartition, FlashGarbageImageLarge2) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+
+    for (const auto& part_name : real_parts) {
+        std::vector<char> buf(max_flash, -1);  // All 1's
+        if (part_info.hashable) {
+            std::string hash_before, hash_after, err_msg;
+            int retcode;
+            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                    << "A parsed partition should not accept a image of all 0xFF";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_EQ(hash_before, hash_after)
+                    << "The hash of the partition has changed after attempting to flash garbage to "
+                       "a parsed partition";
+        } else {
+            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                    << "A parsed partition should not accept a image of all 0xFF";
+        }
+    }
+}
+
+TEST_P(FuzzWriteableParsedPartition, FlashGarbageImageLarge3) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+
+    for (const auto& part_name : real_parts) {
+        std::vector<char> buf(max_flash, 0);  // All 0's
+        if (part_info.hashable) {
+            std::string hash_before, hash_after, err_msg;
+            int retcode;
+            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                    << "A parsed partition should not accept a image of all 0x00";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_EQ(hash_before, hash_after)
+                    << "The hash of the partition has changed after attempting to flash garbage to "
+                       "a parsed partition";
+        } else {
+            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                    << "A parsed partition should not accept a image of all 0x00";
+        }
+    }
+}
+
+INSTANTIATE_TEST_CASE_P(XMLFuzzPartitionsWriteableParsed, FuzzWriteableParsedPartition,
+                        ::testing::ValuesIn(PARTITION_XML_WRITE_PARSED));
+
+// Make sure all attempts to flash things are rejected
+TEST_P(FuzzAnyPartitionLocked, RejectFlash) {
+    std::vector<char> buf = RandomBuf(5);
+    for (const auto& part_name : real_parts) {
+        ASSERT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                << "Flashing a partition should always fail in locked mode";
+    }
+}
+
+INSTANTIATE_TEST_CASE_P(XMLFuzzAnyPartitionLocked, FuzzAnyPartitionLocked,
+                        ::testing::ValuesIn(PARTITION_XML_TESTS));
+
+// Test flashing unlock erases userdata
+TEST_P(UserdataPartition, UnlockErases) {
+    // Get hash after an erase
+    int retcode;
+    std::string err_msg, hash_before, hash_buf, hash_after;
+    ASSERT_EQ(fb->Erase("userdata"), SUCCESS) << "Erasing uesrdata failed";
+    ASSERT_TRUE(PartitionHash(fb.get(), "userdata", &hash_before, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+
+    // Write garbage
+    std::vector<char> buf = RandomBuf(max_flash / 2);
+    ASSERT_EQ(fb->FlashPartition("userdata", buf), SUCCESS);
+    ASSERT_TRUE(PartitionHash(fb.get(), "userdata", &hash_buf, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+
+    // Sanity check of hash
+    EXPECT_NE(hash_before, hash_buf)
+            << "Writing a random buffer to 'userdata' had the same hash as after erasing it";
+    SetLockState(true);  // Lock the device
+
+    SetLockState(false);  // Unlock the device (should cause erase)
+    ASSERT_TRUE(PartitionHash(fb.get(), "userdata", &hash_after, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+
+    EXPECT_NE(hash_after, hash_buf) << "Unlocking the device did not cause the hash of userdata to "
+                                       "change (i.e. it was not erased as required)";
+    EXPECT_EQ(hash_after, hash_before) << "Unlocking the device did not produce the same hash of "
+                                          "userdata as after doing an erase to userdata";
+}
+
+// This is a hack to make this test disapeer if there is not a checsum, userdata is not hashable,
+// or userdata is not marked to be writeable in testing
+INSTANTIATE_TEST_CASE_P(XMLUserdataLocked, UserdataPartition,
+                        ::testing::ValuesIn(PARTITION_XML_USERDATA_CHECKSUM_WRITEABLE));
+
+// Packed images test
+TEST_P(ExtensionsPackedValid, TestDeviceUnpack) {
+    const std::string& packed_name = GetParam().first;
+    const std::string& packed_image = GetParam().second.packed_img;
+    const std::string& unpacked = GetParam().second.unpacked_dir;
+
+    // First we need to check for existence of images
+    const extension::Configuration::PackedInfo& info = config.packed[packed_name];
+
+    const auto flash_part = [&](const std::string fname, const std::string part_name) {
+        FILE* to_flash = fopen((SEARCH_PATH + fname).c_str(), "rb");
+        ASSERT_NE(to_flash, nullptr) << "'" << fname << "'"
+                                     << " failed to open for flashing";
+        int fd = fileno(to_flash);
+        size_t fsize = lseek(fd, 0, SEEK_END);
+        ASSERT_GT(fsize, 0) << fname + " appears to be an empty image";
+        ASSERT_EQ(fb->FlashPartition(part_name, fd, fsize), SUCCESS);
+        fclose(to_flash);
+    };
+
+    // We first need to set the slot count
+    std::string var;
+    int num_slots = 1;
+    if (info.slots) {
+        ASSERT_EQ(fb->GetVar("slot-count", &var), SUCCESS) << "Getting slot count failed";
+        num_slots = strtol(var.c_str(), nullptr, 10);
+    } else {
+        for (const auto& part : info.children) {
+            EXPECT_FALSE(config.partitions[part].slots)
+                    << "A partition can not have slots if the packed image does not";
+        }
+    }
+
+    for (int i = 0; i < num_slots; i++) {
+        std::unordered_map<std::string, std::string> initial_hashes;
+        const std::string packed_suffix =
+                info.slots ? android::base::StringPrintf("_%c", 'a' + i) : "";
+
+        // Flash the paritions manually and get hash
+        for (const auto& part : info.children) {
+            const extension::Configuration::PartitionInfo& part_info = config.partitions[part];
+            const std::string suffix = part_info.slots ? packed_suffix : "";
+            const std::string part_name = part + suffix;
+
+            ASSERT_EQ(fb->Erase(part_name), SUCCESS);
+            const std::string fpath = unpacked + '/' + part + ".img";
+            ASSERT_NO_FATAL_FAILURE(flash_part(fpath, part_name))
+                    << "Failed to flash '" + fpath + "'";
+            // If the partition is hashable we store it
+            if (part_info.hashable) {
+                std::string hash, err_msg;
+                int retcode;
+                ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))
+                        << err_msg;
+                ASSERT_EQ(retcode, 0) << err_msg;
+                initial_hashes[part] = hash;
+            }
+        }
+
+        // erase once at the end, to avoid false positives if flashing does nothing
+        for (const auto& part : info.children) {
+            const std::string suffix = config.partitions[part].slots ? packed_suffix : "";
+            ASSERT_EQ(fb->Erase(part + suffix), SUCCESS);
+        }
+
+        // Now we flash the packed image and compare our hashes
+        ASSERT_NO_FATAL_FAILURE(flash_part(packed_image, packed_name + packed_suffix));
+
+        for (const auto& part : info.children) {
+            const extension::Configuration::PartitionInfo& part_info = config.partitions[part];
+            // If the partition is hashable we check it
+            if (part_info.hashable) {
+                const std::string suffix = part_info.slots ? packed_suffix : "";
+                const std::string part_name = part + suffix;
+                std::string hash, err_msg;
+                int retcode;
+                ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))
+                        << err_msg;
+                ASSERT_EQ(retcode, 0) << err_msg;
+                std::string msg =
+                        "The hashes between flashing the packed image and directly flashing '" +
+                        part_name + "' does not match";
+                EXPECT_EQ(hash, initial_hashes[part]) << msg;
+            }
+        }
+    }
+}
+
+INSTANTIATE_TEST_CASE_P(XMLTestPacked, ExtensionsPackedValid,
+                        ::testing::ValuesIn(PACKED_XML_SUCCESS_TESTS));
+
+// Packed images test
+TEST_P(ExtensionsPackedInvalid, TestDeviceUnpack) {
+    const std::string& packed_name = GetParam().first;
+    const std::string& packed_image = GetParam().second.packed_img;
+
+    // First we need to check for existence of images
+    const extension::Configuration::PackedInfo& info = config.packed[packed_name];
+
+    // We first need to set the slot count
+    std::string var;
+    int num_slots = 1;
+    if (info.slots) {
+        ASSERT_EQ(fb->GetVar("slot-count", &var), SUCCESS) << "Getting slot count failed";
+        num_slots = strtol(var.c_str(), nullptr, 10);
+    } else {
+        for (const auto& part : info.children) {
+            EXPECT_FALSE(config.partitions[part].slots)
+                    << "A partition can not have slots if the packed image does not";
+        }
+    }
+
+    for (int i = 0; i < num_slots; i++) {
+        std::unordered_map<std::string, std::string> initial_hashes;
+        const std::string packed_suffix =
+                info.slots ? android::base::StringPrintf("_%c", 'a' + i) : "";
+
+        // manually and get hash
+        for (const auto& part : info.children) {
+            const extension::Configuration::PartitionInfo& part_info = config.partitions[part];
+            const std::string suffix = part_info.slots ? packed_suffix : "";
+            const std::string part_name = part + suffix;
+
+            // If the partition is hashable we store it
+            if (part_info.hashable) {
+                std::string hash, err_msg;
+                int retcode;
+                ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))
+                        << err_msg;
+                ASSERT_EQ(retcode, 0) << err_msg;
+                initial_hashes[part] = hash;
+            }
+        }
+
+        // Attempt to flash the invalid file
+        FILE* to_flash = fopen((SEARCH_PATH + packed_image).c_str(), "rb");
+        ASSERT_NE(to_flash, nullptr) << "'" << packed_image << "'"
+                                     << " failed to open for flashing";
+        int fd = fileno(to_flash);
+        size_t fsize = lseek(fd, 0, SEEK_END);
+        ASSERT_GT(fsize, 0) << packed_image + " appears to be an empty image";
+        ASSERT_EQ(fb->FlashPartition(packed_name + packed_suffix, fd, fsize), DEVICE_FAIL)
+                << "Expected flashing to fail for " + packed_image;
+        fclose(to_flash);
+
+        for (const auto& part : info.children) {
+            const extension::Configuration::PartitionInfo& part_info = config.partitions[part];
+            // If the partition is hashable we check it
+            if (part_info.hashable) {
+                const std::string suffix = part_info.slots ? packed_suffix : "";
+                const std::string part_name = part + suffix;
+                std::string hash, err_msg;
+                int retcode;
+                ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))
+                        << err_msg;
+                ASSERT_EQ(retcode, 0) << err_msg;
+                std::string msg = "Flashing an invalid image changed the hash of '" + part_name;
+                EXPECT_EQ(hash, initial_hashes[part]) << msg;
+            }
+        }
+    }
+}
+
+INSTANTIATE_TEST_CASE_P(XMLTestPacked, ExtensionsPackedInvalid,
+                        ::testing::ValuesIn(PACKED_XML_FAIL_TESTS));
+
+// OEM xml tests
+TEST_P(ExtensionsOemConformance, RunOEMTest) {
+    const std::string& cmd = std::get<0>(GetParam());
+    // bool restricted = std::get<1>(GetParam());
+    const extension::Configuration::CommandTest& test = std::get<2>(GetParam());
+
+    const RetCode expect = (test.expect == extension::FAIL) ? DEVICE_FAIL : SUCCESS;
+
+    // Does the test require staging something?
+    if (!test.input.empty()) {  // Non-empty string
+        FILE* to_stage = fopen((SEARCH_PATH + test.input).c_str(), "rb");
+        ASSERT_NE(to_stage, nullptr) << "'" << test.input << "'"
+                                     << " failed to open for staging";
+        int fd = fileno(to_stage);
+        size_t fsize = lseek(fd, 0, SEEK_END);
+        std::string var;
+        EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS);
+        int64_t size = strtoll(var.c_str(), nullptr, 16);
+        EXPECT_LT(fsize, size) << "'" << test.input << "'"
+                               << " is too large for staging";
+        ASSERT_EQ(fb->Download(fd, fsize), SUCCESS) << "'" << test.input << "'"
+                                                    << " failed to download for staging";
+        fclose(to_stage);
+    }
+    // Run the command
+    int dsize = -1;
+    std::string resp;
+    const std::string full_cmd = "oem " + cmd + " " + test.arg;
+    ASSERT_EQ(fb->RawCommand(full_cmd, &resp, nullptr, &dsize), expect);
+
+    // This is how we test if indeed data response
+    if (test.expect == extension::DATA) {
+        EXPECT_GT(dsize, 0);
+    }
+
+    // Validate response if neccesary
+    if (!test.regex_str.empty()) {
+        std::smatch sm;
+        std::regex_match(resp, sm, test.regex);
+        EXPECT_FALSE(sm.empty()) << "The oem regex did not match";
+    }
+
+    // If payload, we validate that as well
+    const std::vector<std::string> args = SplitBySpace(test.validator);
+    if (args.size()) {
+        // Save output
+        const std::string save_loc =
+                OUTPUT_PATH + (test.output.empty() ? DEFAULT_OUPUT_NAME : test.output);
+        std::string resp;
+        ASSERT_EQ(fb->Upload(save_loc, &resp), SUCCESS)
+                << "Saving output file failed with (" << fb->Error() << ") " << resp;
+        // Build the arguments to the validator
+        std::vector<std::string> prog_args(args.begin() + 1, args.end());
+        prog_args.push_back(full_cmd);  // Pass in the full command
+        prog_args.push_back(save_loc);  // Pass in the save location
+        // Run the validation program
+        int pipe;
+        const pid_t pid = StartProgram(args[0], prog_args, &pipe);
+        ASSERT_GT(pid, 0) << "Failed to launch validation program: " << args[0];
+        std::string error_msg;
+        int ret = WaitProgram(pid, pipe, &error_msg);
+        EXPECT_EQ(ret, 0) << error_msg;  // Program exited correctly
+    }
+}
+
+INSTANTIATE_TEST_CASE_P(XMLOEM, ExtensionsOemConformance, ::testing::ValuesIn(OEM_XML_TESTS));
+
+// Sparse Tests
+TEST_P(SparseTestPartition, SparseSingleBlock) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+    const std::string part_name = name + (part_info.slots ? "_a" : "");
+    SparseWrapper sparse(4096, 4096);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    std::vector<char> buf = RandomBuf(4096);
+    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
+            << "Adding data failed to sparse file: " << sparse.Rep();
+
+    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+    std::string hash, hash_new, err_msg;
+    int retcode;
+    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+    // Now flash it the non-sparse way
+    EXPECT_EQ(fb->FlashPartition(part_name, buf), SUCCESS) << "Flashing image failed: ";
+    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+
+    EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
+                                 "methods did not result in the same hash";
+}
+
+TEST_P(SparseTestPartition, SparseFill) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+    const std::string part_name = name + (part_info.slots ? "_a" : "");
+    int64_t size = (max_dl / 4096) * 4096;
+    SparseWrapper sparse(4096, size);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, size, 0), 0)
+            << "Adding data failed to sparse file: " << sparse.Rep();
+
+    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+    std::string hash, hash_new, err_msg;
+    int retcode;
+    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+    // Now flash it the non-sparse way
+    std::vector<char> buf(size);
+    for (auto iter = buf.begin(); iter < buf.end(); iter += 4) {
+        iter[0] = 0xef;
+        iter[1] = 0xbe;
+        iter[2] = 0xad;
+        iter[3] = 0xde;
+    }
+    EXPECT_EQ(fb->FlashPartition(part_name, buf), SUCCESS) << "Flashing image failed: ";
+    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+
+    EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
+                                 "methods did not result in the same hash";
+}
+
+// This tests to make sure it does not overwrite previous flashes
+TEST_P(SparseTestPartition, SparseMultiple) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+    const std::string part_name = name + (part_info.slots ? "_a" : "");
+    int64_t size = (max_dl / 4096) * 4096;
+    SparseWrapper sparse(4096, size / 2);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, size / 2, 0), 0)
+            << "Adding data failed to sparse file: " << sparse.Rep();
+    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+
+    SparseWrapper sparse2(4096, size / 2);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    std::vector<char> buf = RandomBuf(size / 2);
+    ASSERT_EQ(sparse_file_add_data(*sparse2, buf.data(), buf.size(), (size / 2) / 4096), 0)
+            << "Adding data failed to sparse file: " << sparse2.Rep();
+    EXPECT_EQ(fb->Download(*sparse2), SUCCESS) << "Download sparse failed: " << sparse2.Rep();
+    EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse2.Rep();
+
+    std::string hash, hash_new, err_msg;
+    int retcode;
+    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+    // Now flash it the non-sparse way
+    std::vector<char> fbuf(size);
+    for (auto iter = fbuf.begin(); iter < fbuf.begin() + size / 2; iter += 4) {
+        iter[0] = 0xef;
+        iter[1] = 0xbe;
+        iter[2] = 0xad;
+        iter[3] = 0xde;
+    }
+    fbuf.assign(buf.begin(), buf.end());
+    EXPECT_EQ(fb->FlashPartition(part_name, fbuf), SUCCESS) << "Flashing image failed: ";
+    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+
+    EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
+                                 "methods did not result in the same hash";
+}
+
+INSTANTIATE_TEST_CASE_P(XMLSparseTest, SparseTestPartition,
+                        ::testing::ValuesIn(SINGLE_PARTITION_XML_WRITE_HASHABLE));
+
+void GenerateXmlTests(const extension::Configuration& config) {
+    // Build the getvar tests
+    for (const auto& it : config.getvars) {
+        GETVAR_XML_TESTS.push_back(std::make_pair(it.first, it.second));
+    }
+
+    // Build the partition tests, to interface with gtest we need to do it this way
+    for (const auto& it : config.partitions) {
+        const auto tup = std::make_tuple(it.first, it.second);
+        PARTITION_XML_TESTS.push_back(tup);  // All partitions
+
+        if (it.second.test == it.second.YES) {
+            PARTITION_XML_WRITEABLE.push_back(tup);  // All writeable partitions
+
+            if (it.second.hashable) {
+                PARTITION_XML_WRITE_HASHABLE.push_back(tup);  // All write and hashable
+                if (!it.second.parsed) {
+                    PARTITION_XML_WRITE_HASH_NONPARSED.push_back(
+                            tup);  // All write hashed and non-parsed
+                }
+            }
+            if (it.second.parsed) {
+                PARTITION_XML_WRITE_PARSED.push_back(tup);  // All write and parsed
+            }
+        }
+    }
+
+    // Build the packed tests, only useful if we have a hash
+    if (!config.checksum.empty()) {
+        for (const auto& it : config.packed) {
+            for (const auto& test : it.second.tests) {
+                const auto tup = std::make_tuple(it.first, test);
+                if (test.expect == extension::OKAY) {  // only testing the success case
+                    PACKED_XML_SUCCESS_TESTS.push_back(tup);
+                } else {
+                    PACKED_XML_FAIL_TESTS.push_back(tup);
+                }
+            }
+        }
+    }
+
+    // This is a hack to make this test disapeer if there is not a checksum, userdata is not
+    // hashable, or userdata is not marked to be writeable in testing
+    const auto part_info = config.partitions.find("userdata");
+    if (!config.checksum.empty() && part_info != config.partitions.end() &&
+        part_info->second.hashable &&
+        part_info->second.test == extension::Configuration::PartitionInfo::YES) {
+        PARTITION_XML_USERDATA_CHECKSUM_WRITEABLE.push_back(
+                std::make_tuple(part_info->first, part_info->second));
+    }
+
+    if (!PARTITION_XML_WRITE_HASHABLE.empty()) {
+        SINGLE_PARTITION_XML_WRITE_HASHABLE.push_back(PARTITION_XML_WRITE_HASHABLE.front());
+    }
+
+    // Build oem tests
+    for (const auto& it : config.oem) {
+        auto oem_cmd = it.second;
+        for (const auto& t : oem_cmd.tests) {
+            OEM_XML_TESTS.push_back(std::make_tuple(it.first, oem_cmd.restricted, t));
+        }
+    }
+}
+
+}  // namespace fastboot
+
+int main(int argc, char** argv) {
+    std::string err;
+    // Parse the args
+    const std::unordered_map<std::string, std::string> args = fastboot::ParseArgs(argc, argv, &err);
+    if (!err.empty()) {
+        printf("%s\n", err.c_str());
+        return -1;
+    }
+
+    if (args.find("config") != args.end()) {
+        auto found = args.find("search_path");
+        fastboot::SEARCH_PATH = (found != args.end()) ? found->second + "/" : "";
+        found = args.find("output_path");
+        fastboot::OUTPUT_PATH = (found != args.end()) ? found->second + "/" : "/tmp/";
+        if (!fastboot::extension::ParseXml(fastboot::SEARCH_PATH + args.at("config"),
+                                           &fastboot::config)) {
+            printf("XML config parsing failed\n");
+            return -1;
+        }
+        // To interface with gtest, must set global scope test variables
+        fastboot::GenerateXmlTests(fastboot::config);
+    }
+
+    if (args.find("serial") != args.end()) {
+        fastboot::FastBootTest::device_serial = args.at("serial");
+    }
+
+    setbuf(stdout, NULL);  // no buffering
+    printf("<Waiting for Device>\n");
+    const auto matcher = [](usb_ifc_info* info) -> int {
+        return fastboot::FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
+    };
+    Transport* transport = nullptr;
+    while (!transport) {
+        transport = usb_open(matcher);
+        std::this_thread::sleep_for(std::chrono::milliseconds(10));
+    }
+    transport->Close();
+
+    if (args.find("serial_port") != args.end()) {
+        fastboot::FastBootTest::serial_port = fastboot::ConfigureSerial(args.at("serial_port"));
+    }
+
+    ::testing::InitGoogleTest(&argc, argv);
+    auto ret = RUN_ALL_TESTS();
+    if (fastboot::FastBootTest::serial_port > 0) {
+        close(fastboot::FastBootTest::serial_port);
+    }
+    return ret;
+}
diff --git a/fastboot/fuzzy_fastboot/test_listeners.h b/fastboot/fuzzy_fastboot/test_listeners.h
new file mode 100644
index 0000000..ae5b0cb
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/test_listeners.h
@@ -0,0 +1,49 @@
+/*
+ * 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
+
+// TODO, print out logs for each failed test with custom listner
+
+class MinimalistPrinter : public ::testing::EmptyTestEventListener {
+    // Called before a test starts.
+    virtual void OnTestStart(const ::testing::TestInfo& test_info) {
+        printf("*** Test %s.%s starting.\n", test_info.test_case_name(), test_info.name());
+    }
+
+    // Called after a failed assertion or a SUCCESS().
+    virtual void OnTestPartResult(const ::testing::TestPartResult& test_part_result) {
+        printf("%s in %s:%d\n%s\n", test_part_result.failed() ? "*** Failure" : "Success",
+               test_part_result.file_name(), test_part_result.line_number(),
+               test_part_result.summary());
+    }
+
+    // Called after a test ends.
+    virtual void OnTestEnd(const ::testing::TestInfo& test_info) {
+        printf("*** Test %s.%s ending.\n", test_info.test_case_name(), test_info.name());
+    }
+};
diff --git a/fastboot/fuzzy_fastboot/test_utils.cpp b/fastboot/fuzzy_fastboot/test_utils.cpp
new file mode 100644
index 0000000..9ad98be
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/test_utils.cpp
@@ -0,0 +1,208 @@
+/*
+ * 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 "test_utils.h"
+#include <fcntl.h>
+#include <termios.h>
+#include <sstream>
+
+namespace fastboot {
+
+namespace {
+constexpr int rand_seed = 0;
+std::default_random_engine rnd(rand_seed);
+}  // namespace
+
+char rand_legal() {
+    return rnd() % 128;
+}
+
+char rand_illegal() {
+    return rand_legal() + 128;
+}
+
+char rand_char() {
+    return rnd() % 256;
+}
+
+int random_int(int start, int end) {
+    std::uniform_int_distribution<int> uni(start, end);
+    return uni(rnd);
+}
+
+std::string RandomString(size_t length, std::function<char(void)> provider) {
+    std::string str(length, 0);
+    std::generate_n(str.begin(), length, provider);
+    return str;
+}
+
+std::vector<char> RandomBuf(size_t length, std::function<char(void)> provider) {
+    std::vector<char> ret;
+    ret.resize(length);
+    std::generate_n(ret.begin(), length, provider);
+    return ret;
+}
+
+std::vector<std::string> SplitBySpace(const std::string& s) {
+    std::istringstream iss(s);
+    return std::vector<std::string>{std::istream_iterator<std::string>{iss},
+                                    std::istream_iterator<std::string>{}};
+}
+
+std::vector<std::string> GeneratePartitionNames(const std::string& base, int num_slots) {
+    if (!num_slots) {
+        return std::vector<std::string>{base};
+    }
+    std::vector<std::string> ret;
+    for (char c = 'a'; c < 'a' + num_slots; c++) {
+        ret.push_back(base + '_' + c);
+    }
+
+    return ret;
+}
+
+std::unordered_map<std::string, std::string> ParseArgs(int argc, char** argv,
+                                                       std::string* err_msg) {
+    // We ignore any gtest stuff
+    std::unordered_map<std::string, std::string> ret;
+
+    for (int i = 1; i < argc - 1; i++) {
+        std::string arg(argv[i]);
+
+        const std::string gtest_start("--gtest");
+        // We found a non gtest argument
+        if (!arg.find("-h") ||
+            (!arg.find("--") && arg.find("--gtest") && arg.find("=") != arg.npos)) {
+            const std::string start(arg.begin() + 2, arg.begin() + arg.find("="));
+            const std::string end(arg.begin() + arg.find("=") + 1, arg.end());
+            ret[start] = end;
+        } else if (arg.find("--gtest") != 0) {
+            *err_msg = android::base::StringPrintf("Illegal argument '%s'\n", arg.c_str());
+            return ret;
+        }
+    }
+
+    return ret;
+}
+
+int ConfigureSerial(const std::string& port) {
+    int fd = open(port.c_str(), O_RDONLY | O_NOCTTY | O_NONBLOCK);
+
+    if (fd <= 0) {
+        return fd;
+    }
+
+    struct termios tty;
+    tcgetattr(fd, &tty);
+
+    cfsetospeed(&tty, (speed_t)B115200);
+    cfsetispeed(&tty, (speed_t)B115200);
+
+    tty.c_cflag &= ~PARENB;
+    tty.c_cflag &= ~CSTOPB;
+    tty.c_cflag &= ~CSIZE;
+    tty.c_cflag |= CS8;
+
+    tty.c_cflag &= ~CRTSCTS;
+    tty.c_cc[VMIN] = 0;
+    tty.c_cc[VTIME] = 2;
+    tty.c_cflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+
+    cfmakeraw(&tty);
+
+    tcflush(fd, TCIFLUSH);
+    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
+        return -1;
+    }
+
+    return fd;
+}
+
+int StartProgram(const std::string program, const std::vector<std::string> args, int* rpipe) {
+    int link[2];
+    if (pipe(link) < 0) {
+        return -1;
+    }
+
+    pid_t pid = fork();
+    if (pid < 0) {  // error
+        return -1;
+    }
+
+    if (pid) {  // parent
+        close(link[1]);
+        *rpipe = link[0];
+        fcntl(*rpipe, F_SETFL, O_NONBLOCK);  // Non-blocking
+    } else {                                 // child
+        std::vector<const char*> argv(args.size() + 2, nullptr);
+        argv[0] = program.c_str();
+
+        for (int i = 0; i < args.size(); i++) {
+            argv[i + 1] = args[i].c_str();
+        }
+
+        // We pipe any stderr writes to the parent test process
+        dup2(link[1], STDERR_FILENO);  // close stdout and have it now be link[1]
+        // Close duplicates
+        close(link[0]);
+        close(link[1]);
+
+        execvp(program.c_str(), const_cast<char* const*>(argv.data()));
+        fprintf(stderr, "Launching validator process '%s' failed with: %s\n", program.c_str(),
+                strerror(errno));
+        exit(-1);
+    }
+
+    return pid;
+}
+
+int WaitProgram(const int pid, const int pipe, std::string* error_msg) {
+    int status;
+    if (waitpid(pid, &status, 0) != pid) {
+        close(pipe);
+        return -1;
+    }
+    // Read from pipe
+    char buf[1024];
+    int n;
+    while ((n = read(pipe, buf, sizeof(buf))) > 0) {
+        buf[n] = 0; /* terminate the string */
+        error_msg->append(buf, n);
+    }
+    close(pipe);
+
+    if (WIFEXITED(status)) {
+        // This WEXITSTATUS macro masks off lower bytes, with no sign extension
+        // casting it as a signed char fixes the sign extension issue
+        int retmask = WEXITSTATUS(status);
+        return reinterpret_cast<int8_t*>(&retmask)[0];
+    }
+
+    return -1;
+}
+
+}  // namespace fastboot
diff --git a/fastboot/fuzzy_fastboot/test_utils.h b/fastboot/fuzzy_fastboot/test_utils.h
new file mode 100644
index 0000000..0b032e4
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/test_utils.h
@@ -0,0 +1,93 @@
+/*
+ * 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 <sparse/sparse.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <cstdlib>
+#include <fstream>
+#include <random>
+#include <string>
+#include <unordered_map>
+#include "fastboot_driver.h"
+
+namespace fastboot {
+
+char rand_legal();
+char rand_illegal();
+char rand_char();
+// start and end are inclusive
+int random_int(int start, int end);
+
+// I don't want to have to manage memory for this guy
+struct SparseWrapper {
+    SparseWrapper(unsigned int block_size, int64_t len) {
+        sparse = sparse_file_new(block_size, len);
+    }
+
+    SparseWrapper(struct sparse_file* sf) { sparse = sf; }
+
+    ~SparseWrapper() {
+        if (sparse) {
+            sparse_file_destroy(sparse);
+        }
+    }
+
+    const std::string Rep() {
+        unsigned bs = sparse_file_block_size(sparse);
+        unsigned len = sparse_file_len(sparse, true, false);
+        return android::base::StringPrintf("[block_size=%u, len=%u]", bs, len);
+    }
+
+    struct sparse_file* operator*() {
+        return sparse;
+    }
+
+    struct sparse_file* sparse;
+};
+
+std::string RandomString(size_t length, std::function<char(void)> provider);
+// RVO will avoid copy
+std::vector<char> RandomBuf(size_t length, std::function<char(void)> provider = rand_char);
+
+std::vector<std::string> SplitBySpace(const std::string& s);
+
+std::unordered_map<std::string, std::string> ParseArgs(int argc, char** argv, std::string* err_msg);
+
+std::vector<std::string> GeneratePartitionNames(const std::string& base, int num_slots = 0);
+
+int ConfigureSerial(const std::string& port);
+
+int StartProgram(const std::string program, const std::vector<std::string> args, int* pipe);
+int WaitProgram(const pid_t pid, const int pipe, std::string* error_msg);
+
+}  // namespace fastboot
diff --git a/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp b/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp
new file mode 100644
index 0000000..7c595f4
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp
@@ -0,0 +1,193 @@
+#include "usb_transport_sniffer.h"
+#include <android-base/stringprintf.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <iomanip>
+#include <sstream>
+
+namespace fastboot {
+
+UsbTransportSniffer::UsbTransportSniffer(std::unique_ptr<UsbTransport> transport,
+                                         const int serial_fd)
+    : transport_(std::move(transport)), serial_fd_(serial_fd) {}
+
+UsbTransportSniffer::~UsbTransportSniffer() {
+    Close();
+}
+
+ssize_t UsbTransportSniffer::Read(void* data, size_t len) {
+    ProcessSerial();
+
+    ssize_t ret = transport_->Read(data, len);
+    if (ret < 0) {
+        const char* err = strerror(errno);
+        std::vector<char> buf(err, err + strlen(err));
+        Event e(READ_ERROR, std::move(buf));
+        transfers_.push_back(e);
+        return ret;
+    }
+
+    char* cdata = static_cast<char*>(data);
+    std::vector<char> buf(cdata, cdata + ret);
+    Event e(READ, std::move(buf));
+    transfers_.push_back(e);
+
+    ProcessSerial();
+    return ret;
+}
+
+ssize_t UsbTransportSniffer::Write(const void* data, size_t len) {
+    ProcessSerial();
+
+    size_t ret = transport_->Write(data, len);
+    if (ret != len) {
+        const char* err = strerror(errno);
+        std::vector<char> buf(err, err + strlen(err));
+        Event e(WRITE_ERROR, std::move(buf));
+        transfers_.push_back(e);
+        return ret;
+    }
+
+    const char* cdata = static_cast<const char*>(data);
+    std::vector<char> buf(cdata, cdata + len);
+    Event e(WRITE, std::move(buf));
+    transfers_.push_back(e);
+
+    ProcessSerial();
+    return ret;
+}
+
+int UsbTransportSniffer::Close() {
+    return transport_->Close();
+}
+
+int UsbTransportSniffer::Reset() {
+    ProcessSerial();
+    int ret = transport_->Reset();
+    std::vector<char> buf;
+    Event e(RESET, std::move(buf));
+    transfers_.push_back(e);
+    ProcessSerial();
+    return ret;
+}
+
+const std::vector<UsbTransportSniffer::Event> UsbTransportSniffer::Transfers() {
+    return transfers_;
+}
+
+/*
+ * When a test fails, we want a human readable log of everything going on up until
+ * the failure. This method will look through its log of captured events, and
+ * create a clean printable string of everything that happened.
+ */
+std::string UsbTransportSniffer::CreateTrace() {
+    std::string ret;
+
+    const auto no_print = [](char c) -> bool { return !isprint(c); };
+    // This lambda creates a humand readable representation of a byte buffer
+    // It first attempts to figure out whether it should be interpreted as an ASCII buffer,
+    // and be printed as a string, or just a raw byte-buffer
+    const auto msg = [&ret, no_print](const std::vector<char>& buf) {
+        ret += android::base::StringPrintf("(%lu bytes): ", buf.size());
+        std::vector<const char>::iterator iter = buf.end();
+        const unsigned max_chars = 50;
+        if (buf.size() > max_chars) {
+            iter = buf.begin() + max_chars;
+        }
+        ret += '"';
+        if (std::count_if(buf.begin(), iter, no_print) == 0) {  // print as ascii
+            ret.insert(ret.end(), buf.begin(), iter);
+        } else {  // print as hex
+            std::stringstream ss;
+            for (auto c = buf.begin(); c < iter; c++) {
+                ss << std::hex << std::setw(2) << std::setfill('0')
+                   << static_cast<uint16_t>(static_cast<uint8_t>(*c));
+                ss << ',';
+            }
+            ret += ss.str();
+        }
+        if (buf.size() > max_chars) {
+            ret += android::base::StringPrintf("...\"(+%lu bytes)\n", buf.size() - max_chars);
+        } else {
+            ret += "\"\n";
+        }
+    };
+
+    // Now we just scan through the log of everything that happened and create a
+    // printable string for each one
+    for (const auto& event : transfers_) {
+        const std::vector<char>& cbuf = event.buf;
+        const std::string tmp(cbuf.begin(), cbuf.end());
+        auto start = transfers_.front().start;
+        auto durr = event.start - start;
+        auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(durr).count();
+
+        switch (event.type) {
+            case READ:
+                ret += android::base::StringPrintf("[READ %lldms]", millis);
+                msg(cbuf);
+                break;
+
+            case WRITE:
+                ret += android::base::StringPrintf("[WRITE %lldms]", millis);
+                msg(cbuf);
+                break;
+
+            case RESET:
+                ret += android::base::StringPrintf("[RESET %lldms]\n", millis);
+                break;
+
+            case READ_ERROR:
+                ret += android::base::StringPrintf("[READ_ERROR %lldms] %s\n", millis, tmp.c_str());
+                break;
+
+            case WRITE_ERROR:
+                ret += android::base::StringPrintf("[WRITE_ERROR %lldms] %s\n", millis,
+                                                   tmp.c_str());
+                break;
+
+            case SERIAL:
+                ret += android::base::StringPrintf("[SERIAL %lldms] %s", millis, tmp.c_str());
+                if (ret.back() != '\n') ret += '\n';
+                break;
+        }
+    }
+    return ret;
+}
+
+// This is a quick call to flush any UART logs the device might have sent
+// to our internal event log. It will wait up to 10ms for data to appear
+void UsbTransportSniffer::ProcessSerial() {
+    if (serial_fd_ <= 0) return;
+
+    fd_set set;
+    struct timeval timeout;
+
+    FD_ZERO(&set);
+    FD_SET(serial_fd_, &set);
+    timeout.tv_sec = 0;
+    timeout.tv_usec = 10000;  // 10ms
+
+    int count = 0;
+    int n = 0;
+    std::vector<char> buf;
+    buf.resize(1000);
+    while (select(serial_fd_ + 1, &set, NULL, NULL, &timeout) > 0) {
+        n = read(serial_fd_, buf.data() + count, buf.size() - count);
+        if (n > 0) {
+            count += n;
+        } else {
+            break;
+        }
+    }
+
+    buf.resize(count);
+
+    if (count > 0) {
+        Event e(SERIAL, std::move(buf));
+        transfers_.push_back(e);
+    }
+}
+
+}  // namespace fastboot
diff --git a/fastboot/fuzzy_fastboot/usb_transport_sniffer.h b/fastboot/fuzzy_fastboot/usb_transport_sniffer.h
new file mode 100644
index 0000000..8119aea
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/usb_transport_sniffer.h
@@ -0,0 +1,88 @@
+/*
+ * 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 <sys/types.h>
+#include <unistd.h>
+#include <chrono>
+#include <cstdlib>
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include "usb.h"
+
+namespace fastboot {
+
+/* A special class for sniffing reads and writes
+ *
+ * A useful debugging tool is to see the raw fastboot transactions going between
+ * the host and device. This class wraps the UsbTransport class, and snoops and saves
+ * all the transactions going on. Additionally, if there is a console serial port
+ * from the device, this class can monitor it as well and capture the interleaving of
+ * transport transactions and UART log messages.
+ */
+class UsbTransportSniffer : public UsbTransport {
+  public:
+    enum EventType {
+        READ,
+        WRITE,
+        RESET,
+        SERIAL,  // Serial log message from device
+        READ_ERROR,
+        WRITE_ERROR,
+    };
+
+    struct Event {
+        Event(EventType t, const std::vector<char> cbuf) : type(t), buf(cbuf) {
+            start = std::chrono::high_resolution_clock::now();
+        };
+        EventType type;
+        std::chrono::high_resolution_clock::time_point start;
+        const std::vector<char> buf;
+    };
+
+    UsbTransportSniffer(std::unique_ptr<UsbTransport> transport, const int serial_fd = 0);
+    ~UsbTransportSniffer() override;
+
+    virtual ssize_t Read(void* data, size_t len) override;
+    virtual ssize_t Write(const void* data, size_t len) override;
+    virtual int Close() override final;  // note usage in destructor
+    virtual int Reset() override;
+
+    const std::vector<Event> Transfers();
+    std::string CreateTrace();
+    void ProcessSerial();
+
+  private:
+    std::vector<Event> transfers_;
+    std::unique_ptr<UsbTransport> transport_;
+    const int serial_fd_;
+};
+
+}  // End namespace fastboot
diff --git a/fastboot/main.cpp b/fastboot/main.cpp
new file mode 100644
index 0000000..35f4218
--- /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 "fastboot.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 c239861..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 std::string& cmd, uint32_t size,
-                              char* response) {
-    if (cmd.size() > 64) {
-        g_error = android::base::StringPrintf("command too large (%zu)", cmd.size());
-        return -1;
-    }
-
-    if (response) {
-        response[0] = 0;
-    }
-
-    if (transport->Write(cmd.c_str(), cmd.size()) != static_cast<int>(cmd.size())) {
-        g_error = android::base::StringPrintf("command write failed (%s)", strerror(errno));
-        transport->Close();
-        return -1;
-    }
-
-    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 std::string& 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 std::string& 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 std::string& cmd, char* response) {
-    return _command_start(transport, cmd, 0, response);
-}
-
-int fb_command(Transport* transport, const std::string& cmd) {
-    return _command_send_no_data(transport, cmd, 0);
-}
-
-int fb_command_response(Transport* transport, const std::string& 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, 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..6e95b160 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..6363aa5 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)) {}
-    ~LinuxUsbTransport() override = default;
+    explicit LinuxUsbTransport(std::unique_ptr<usb_handle> handle, uint32_t ms_timeout = 0)
+        : handle_(std::move(handle)), ms_timeout_(ms_timeout) {}
+    ~LinuxUsbTransport() override;
 
     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);
 };
@@ -384,6 +387,10 @@
     return usb;
 }
 
+LinuxUsbTransport::~LinuxUsbTransport() {
+    Close();
+}
+
 ssize_t LinuxUsbTransport::Write(const void* _data, size_t len)
 {
     unsigned char *data = (unsigned char*) _data;
@@ -402,7 +409,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 +443,7 @@
         bulk.ep = handle_->ep_in;
         bulk.len = xfer;
         bulk.data = data;
-        bulk.timeout = 0;
+        bulk.timeout = ms_timeout_;
         retry = 0;
 
         do {
@@ -447,7 +454,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 +484,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 e95b049..8a3c213 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -61,34 +61,38 @@
 
     UInt8 bulkIn;
     UInt8 bulkOut;
-    IOUSBInterfaceInterface190 **interface;
+    IOUSBInterfaceInterface500** interface;
     unsigned int zero_mask;
 };
 
-class OsxUsbTransport : public Transport {
+class OsxUsbTransport : public UsbTransport {
   public:
-    OsxUsbTransport(std::unique_ptr<usb_handle> handle) : handle_(std::move(handle)) {}
-    ~OsxUsbTransport() override = default;
+    // 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;
 
     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);
 };
 
 /** Try out all the interfaces and see if there's a match. Returns 0 on
  * success, -1 on failure. */
-static int try_interfaces(IOUSBDeviceInterface182 **dev, usb_handle *handle) {
+static int try_interfaces(IOUSBDeviceInterface500** dev, usb_handle* handle) {
     IOReturn kr;
     IOUSBFindInterfaceRequest request;
     io_iterator_t iterator;
     io_service_t usbInterface;
     IOCFPlugInInterface **plugInInterface;
-    IOUSBInterfaceInterface190 **interface = NULL;
+    IOUSBInterfaceInterface500** interface = NULL;
     HRESULT result;
     SInt32 score;
     UInt8 interfaceNumEndpoints;
@@ -102,7 +106,7 @@
     kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator);
 
     if (kr != 0) {
-        ERR("Couldn't create a device interface iterator: (%08x)\n", kr);
+        WARN("Couldn't create a device interface iterator: (%08x)\n", kr);
         return -1;
     }
 
@@ -124,10 +128,10 @@
         }
 
         // Now create the interface interface for the interface
-        result = (*plugInInterface)->QueryInterface(
-                plugInInterface,
-                CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
-                (LPVOID*) &interface);
+        result = (*plugInInterface)
+                         ->QueryInterface(plugInInterface,
+                                          CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID500),
+                                          (LPVOID*)&interface);
 
         // No longer need the intermediate plugin
         (*plugInInterface)->Release(plugInInterface);
@@ -266,7 +270,7 @@
 static int try_device(io_service_t device, usb_handle *handle) {
     kern_return_t kr;
     IOCFPlugInInterface **plugin = NULL;
-    IOUSBDeviceInterface182 **dev = NULL;
+    IOUSBDeviceInterface500** dev = NULL;
     SInt32 score;
     HRESULT result;
     UInt8 serialIndex;
@@ -283,8 +287,8 @@
     }
 
     // Now create the device interface.
-    result = (*plugin)->QueryInterface(plugin,
-            CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*) &dev);
+    result = (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID500),
+                                       (LPVOID*)&dev);
     if ((result != 0) || (dev == NULL)) {
         ERR("Couldn't create a device interface (%08x)\n", (int) result);
         goto error;
@@ -456,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) {
@@ -464,7 +468,11 @@
         return nullptr;
     }
 
-    return new OsxUsbTransport(std::move(handle));
+    return new OsxUsbTransport(std::move(handle), timeout_ms);
+}
+
+OsxUsbTransport::~OsxUsbTransport() {
+    Close();
 }
 
 int OsxUsbTransport::Close() {
@@ -472,6 +480,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;
@@ -494,7 +515,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;
@@ -541,8 +569,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..bf840f8 100644
--- a/fastboot/usb_windows.cpp
+++ b/fastboot/usb_windows.cpp
@@ -66,14 +66,15 @@
     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;
+    ~WindowsUsbTransport() override;
 
     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) {
@@ -193,25 +195,28 @@
 ssize_t WindowsUsbTransport::Read(void* data, size_t len) {
     unsigned long time_out = 0;
     unsigned long read = 0;
+    size_t count = 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;
+        while (len > 0) {
+            size_t xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
 
             ret = AdbReadEndpointSync(handle_->adb_read_pipe, data, xfer, &read, time_out);
             errno = GetLastError();
-            DBG("usb_read got: %ld, expected: %d, errno: %d\n", read, xfer, errno);
-            if (ret) {
-                return read;
-            } else {
+            DBG("usb_read got: %lu, expected: %zu, errno: %d\n", read, xfer, errno);
+            if (ret == 0) {
                 // assume ERROR_INVALID_HANDLE indicates we are disconnected
                 if (errno == ERROR_INVALID_HANDLE)
                     usb_kick(handle_.get());
                 break;
             }
-            // else we timed out - try again
+            count += read;
+            len -= read;
+            data = (char*)data + read;
+
+            if (xfer != read || len == 0) return count;
         }
     } else {
         DBG("usb_read NULL handle\n");
@@ -248,6 +253,10 @@
     }
 }
 
+WindowsUsbTransport::~WindowsUsbTransport() {
+    Close();
+}
+
 int WindowsUsbTransport::Close() {
     DBG("usb_close\n");
 
@@ -260,6 +269,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 +284,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 +323,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 +360,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 +380,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 fb085e7..900d6ea 100644
--- a/fastboot/util.cpp
+++ b/fastboot/util.cpp
@@ -33,7 +33,9 @@
 
 #include <sys/time.h>
 
-#include "fastboot.h"
+#include "util.h"
+
+static bool g_verbose = false;
 
 double now() {
     struct timeval tv;
@@ -44,15 +46,30 @@
 void die(const char* fmt, ...) {
     va_list ap;
     va_start(ap, fmt);
-    fprintf(stderr,"error: ");
+    fprintf(stderr, "fastboot: error: ");
     vfprintf(stderr, fmt, ap);
-    fprintf(stderr,"\n");
+    fprintf(stderr, "\n");
     va_end(ap);
     exit(EXIT_FAILURE);
 }
 
-char* xstrdup(const char* s) {
-    char* result = strdup(s);
-    if (!result) die("out of memory");
-    return result;
+void die(const std::string& str) {
+    die("%s", str.c_str());
+}
+
+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");
 }
diff --git a/fastboot/util.h b/fastboot/util.h
new file mode 100644
index 0000000..c719df2
--- /dev/null
+++ b/fastboot/util.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <string>
+
+#include <bootimg.h>
+
+/* util stuff */
+double now();
+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)));
+
+void die(const std::string& str) __attribute__((__noreturn__));
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index f23150d..3d3503c 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -23,43 +23,56 @@
     cflags: [
         "-Wall",
         "-Werror",
-        "-Wno-unused-variable",
-    ],
-    cppflags: [
-        "-std=gnu++1z",
     ],
 }
 
-cc_library_static {
+cc_library {
+    // Do not ever allow this library to be vendor_available as a shared library.
+    // It does not have a stable interface.
     name: "libfs_mgr",
     defaults: ["fs_mgr_defaults"],
+    recovery_available: true,
     export_include_dirs: ["include"],
     include_dirs: ["system/vold"],
     srcs: [
+        "file_wait.cpp",
         "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",
+        "fs_mgr_overlayfs.cpp",
+        "fs_mgr_roots.cpp",
+        "fs_mgr_vendor_overlay.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcrypto",
+        "libcrypto_utils",
+        "libcutils",
+        "libext4_utils",
+        "libfec",
+        "liblog",
+        "liblp",
+        "libselinux",
     ],
     static_libs: [
-        "libfec",
-        "libfec_rs",
-        "libbase",
-        "libcrypto_utils",
-        "libcrypto",
-        "libext4_utils",
-        "libsquashfs_utils",
-        "libselinux",
         "libavb",
+        "libfs_avb",
         "libfstab",
+        "libdm",
+        "libgsi",
     ],
     export_static_lib_headers: [
+        "libfs_avb",
         "libfstab",
+        "libdm",
+    ],
+    export_shared_lib_headers: [
+        "liblp",
     ],
     whole_static_libs: [
         "liblogwrap",
+        "libdm",
         "libfstab",
     ],
     cppflags: [
@@ -76,14 +89,63 @@
 }
 
 cc_library_static {
+    // Do not ever make this a shared library as long as it is vendor_available.
+    // It does not have a stable interface.
     name: "libfstab",
     vendor_available: true,
+    recovery_available: true,
+    host_supported: true,
     defaults: ["fs_mgr_defaults"],
     srcs: [
         "fs_mgr_fstab.cpp",
         "fs_mgr_boot_config.cpp",
         "fs_mgr_slotselect.cpp",
     ],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
     export_include_dirs: ["include_fstab"],
-    header_libs: ["libbase_headers"],
+    header_libs: [
+        "libbase_headers",
+        "libgsi_headers",
+    ],
+}
+
+cc_binary {
+    name: "remount",
+    defaults: ["fs_mgr_defaults"],
+    static_libs: [
+        "libavb_user",
+    ],
+    shared_libs: [
+        "libbootloader_message",
+        "libbase",
+        "libcutils",
+        "libcrypto",
+        "libext4_utils",
+        "libfec",
+        "libfs_mgr",
+        "liblog",
+        "liblp",
+        "libselinux",
+    ],
+    header_libs: [
+        "libcutils_headers",
+    ],
+    srcs: [
+        "fs_mgr_remount.cpp",
+    ],
+    cppflags: [
+        "-DALLOW_ADBD_DISABLE_VERITY=0",
+    ],
+    product_variables: {
+        debuggable: {
+            cppflags: [
+                "-UALLOW_ADBD_DISABLE_VERITY",
+                "-DALLOW_ADBD_DISABLE_VERITY=1",
+            ],
+        },
+    },
 }
diff --git a/fs_mgr/OWNERS b/fs_mgr/OWNERS
index 817a0b8..cbbd3bc 100644
--- a/fs_mgr/OWNERS
+++ b/fs_mgr/OWNERS
@@ -1,2 +1,3 @@
 bowgotsai@google.com
+dvander@google.com
 tomcherry@google.com
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
new file mode 100644
index 0000000..d204bfd
--- /dev/null
+++ b/fs_mgr/README.overlayfs.md
@@ -0,0 +1,141 @@
+Android Overlayfs integration with adb remount
+==============================================
+
+Introduction
+------------
+
+Users working with userdebug or eng builds expect to be able to
+remount the system partition as read-write and then add or modify
+any number of files without reflashing the system image, which is
+understandably efficient for a development cycle.
+Limited memory systems that chose to use readonly filesystems like
+*squashfs*, or *Logical Resizable Android Partitions* which land
+system partition images right-sized, and with filesystem that have
+been deduped on the block level to compress the content; means that
+either a remount is not possible directly, or when done offers
+little or no utility because of remaining space limitations or
+support logistics.
+
+*Overlayfs* comes to the rescue for these debug scenarios, and logic
+will _automatically_ setup backing storage for a writable filesystem
+as an upper reference, and mount overtop the lower.  These actions
+will be performed in the **adb disable-verity** and **adb remount**
+requests.
+
+Operations
+----------
+
+### Cookbook
+
+The typical action to utilize the remount facility is:
+
+    $ adb root
+    $ adb disable-verity
+    $ adb reboot
+    $ adb wait-for-device
+    $ adb root
+    $ adb remount
+
+Followed by one of the following:
+
+    $ adb stop
+    $ adb sync
+    $ adb start
+    $ adb reboot
+
+*or*
+
+    $ adb push <source> <destination>
+    $ adb reboot
+
+Note that the sequence above:
+
+    $ adb disable-verity
+    $ adb reboot
+
+*or*
+
+    $ adb remount
+
+can be replaced in both places with:
+
+    $ adb remount -R
+
+which will not reboot if everything is already prepared and ready
+to go.
+
+None of this changes if *overlayfs* needs to be engaged.
+The decisions whether to use traditional direct filesystem remount,
+or one wrapped by *overlayfs* is automatically determined based on
+a probe of the filesystem types and space remaining.
+
+### Backing Storage
+
+When *overlayfs* logic is feasible, it will use either the
+**/cache/overlay/** directory for non-A/B devices, or the
+**/mnt/scratch/overlay** directory for A/B devices that have
+access to *Logical Resizable Android Partitions*.
+The backing store is used as soon as possible in the boot
+process and can occur at first stage init, or at the
+mount_all init rc commands.
+
+This early as possible attachment of *overlayfs* means that
+*sepolicy* or *init* itself can also be pushed and used after
+the exec phases that accompany each stage.
+
+Caveats
+-------
+
+- Space used in the backing storage is on a file by file basis
+  and will require more space than if updated in place.  As such
+  it is important to be mindful of any wasted space, for instance
+  **BOARD_<partition>IMAGE_PARTITION_RESERVED_SIZE** being defined
+  will have a negative impact on the overall right-sizing of images
+  and thus free dynamic partition space.
+- Kernel must have CONFIG_OVERLAY_FS=y and will need to be patched
+  with "*overlayfs: override_creds=off option bypass creator_cred*"
+  if kernel is 4.4 or higher.
+  The patch is available on the upstream mailing list and the latest as of
+  Feb 8 2019 is https://lore.kernel.org/patchwork/patch/1009299/.
+  This patch adds an override_creds _mount_ option to overlayfs that
+  permits legacy behavior for systems that do not have overlapping
+  sepolicy rules, principals of least privilege, which is how Android behaves.
+- *adb enable-verity* will free up overlayfs and as a bonus the
+  device will be reverted pristine to before any content was updated.
+  Update engine does not take advantage of this, will perform a full OTA.
+- Update engine may not run if *fs_mgr_overlayfs_is_setup*() reports
+  true as adb remount overrides are incompatible with an OTA resources.
+- For implementation simplicity on retrofit dynamic partition devices,
+  take the whole alternate super (eg: if "*a*" slot, then the whole of
+  "*system_b*").
+  Since landing a filesystem on the alternate super physical device
+  without differentiating if it is setup to support logical or physical,
+  the alternate slot metadata and previous content will be lost.
+- If dynamic partitions runs out of space, resizing a logical
+  partition larger may fail because of the scratch partition.
+  If this happens, either fastboot flashall or adb enable-verity can
+  be used to clear scratch storage to permit the flash.
+  Then reinstate the overrides and continue.
+- File bugs or submit fixes for review.
+- There are other subtle caveats requiring complex logic to solve.
+  Have evaluated them as too complex or not worth the trouble, please
+  File a bug if a use case needs to be covered.
+  - The backing storage is treated fragile, if anything else has
+    issue with the space taken, the backing storage will be cleared
+    out and we reserve the right to not inform, if the layering
+    does not prevent any messaging.
+  - Space remaining threshold is hard coded.  If 1% or more space
+    still remains, overlayfs will not be used, yet that amount of
+    space remaining is problematic.
+  - Flashing a partition via bootloader fastboot, as opposed to user
+    space fastbootd, is not detected, thus a partition may have
+    override content remaining.  adb enable-verity to wipe.
+  - Space is limited, there is near unlimited space on userdata,
+    we have made an architectural decision to not utilize
+    /data/overlay/ at this time.  Acquiring space to use for
+    backing remains an ongoing battle.
+  - First stage init, or ramdisk, can not be overriden.
+  - Backing storage will be discarded or ignored on errors, leading
+    to confusion.  When debugging using **adb remount** it is
+    currently advised to confirm update is present after a reboot
+    to develop confidence.
diff --git a/fs_mgr/file_wait.cpp b/fs_mgr/file_wait.cpp
new file mode 100644
index 0000000..cbf6845
--- /dev/null
+++ b/fs_mgr/file_wait.cpp
@@ -0,0 +1,235 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 <fs_mgr/file_wait.h>
+
+#include <limits.h>
+#if defined(__linux__)
+#include <poll.h>
+#include <sys/inotify.h>
+#endif
+#if defined(WIN32)
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+#include <functional>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fs_mgr {
+
+using namespace std::literals;
+using android::base::unique_fd;
+
+bool PollForFile(const std::string& path, const std::chrono::milliseconds relative_timeout) {
+    auto start_time = std::chrono::steady_clock::now();
+
+    while (true) {
+        if (!access(path.c_str(), F_OK) || errno != ENOENT) return true;
+
+        std::this_thread::sleep_for(50ms);
+
+        auto now = std::chrono::steady_clock::now();
+        auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+        if (time_elapsed > relative_timeout) return false;
+    }
+}
+
+bool PollForFileDeleted(const std::string& path, const std::chrono::milliseconds relative_timeout) {
+    auto start_time = std::chrono::steady_clock::now();
+
+    while (true) {
+        if (access(path.c_str(), F_OK) && errno == ENOENT) return true;
+
+        std::this_thread::sleep_for(50ms);
+
+        auto now = std::chrono::steady_clock::now();
+        auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+        if (time_elapsed > relative_timeout) return false;
+    }
+}
+
+#if defined(__linux__)
+class OneShotInotify {
+  public:
+    OneShotInotify(const std::string& path, uint32_t mask,
+                   const std::chrono::milliseconds relative_timeout);
+
+    bool Wait();
+
+  private:
+    bool CheckCompleted();
+    int64_t RemainingMs() const;
+    bool ConsumeEvents();
+
+    enum class Result { Success, Timeout, Error };
+    Result WaitImpl();
+
+    unique_fd inotify_fd_;
+    std::string path_;
+    uint32_t mask_;
+    std::chrono::time_point<std::chrono::steady_clock> start_time_;
+    std::chrono::milliseconds relative_timeout_;
+    bool finished_;
+};
+
+OneShotInotify::OneShotInotify(const std::string& path, uint32_t mask,
+                               const std::chrono::milliseconds relative_timeout)
+    : path_(path),
+      mask_(mask),
+      start_time_(std::chrono::steady_clock::now()),
+      relative_timeout_(relative_timeout),
+      finished_(false) {
+    // If the condition is already met, don't bother creating an inotify.
+    if (CheckCompleted()) return;
+
+    unique_fd inotify_fd(inotify_init1(IN_CLOEXEC | IN_NONBLOCK));
+    if (inotify_fd < 0) {
+        PLOG(ERROR) << "inotify_init1 failed";
+        return;
+    }
+
+    std::string watch_path;
+    if (mask == IN_CREATE) {
+        watch_path = android::base::Dirname(path);
+    } else {
+        watch_path = path;
+    }
+    if (inotify_add_watch(inotify_fd, watch_path.c_str(), mask) < 0) {
+        PLOG(ERROR) << "inotify_add_watch failed";
+        return;
+    }
+
+    // It's possible the condition was met before the add_watch. Check for
+    // this and abort early if so.
+    if (CheckCompleted()) return;
+
+    inotify_fd_ = std::move(inotify_fd);
+}
+
+bool OneShotInotify::Wait() {
+    Result result = WaitImpl();
+    if (result == Result::Success) return true;
+    if (result == Result::Timeout) return false;
+
+    // Some kind of error with inotify occurred, so fallback to a poll.
+    std::chrono::milliseconds timeout(RemainingMs());
+    if (mask_ == IN_CREATE) {
+        return PollForFile(path_, timeout);
+    } else if (mask_ == IN_DELETE_SELF) {
+        return PollForFileDeleted(path_, timeout);
+    } else {
+        LOG(ERROR) << "Unknown inotify mask: " << mask_;
+        return false;
+    }
+}
+
+OneShotInotify::Result OneShotInotify::WaitImpl() {
+    // If the operation completed super early, we'll never have created an
+    // inotify instance.
+    if (finished_) return Result::Success;
+    if (inotify_fd_ < 0) return Result::Error;
+
+    while (true) {
+        auto remaining_ms = RemainingMs();
+        if (remaining_ms <= 0) return Result::Timeout;
+
+        struct pollfd event = {
+                .fd = inotify_fd_,
+                .events = POLLIN,
+                .revents = 0,
+        };
+        int rv = poll(&event, 1, static_cast<int>(remaining_ms));
+        if (rv <= 0) {
+            if (rv == 0 || errno == EINTR) {
+                continue;
+            }
+            PLOG(ERROR) << "poll for inotify failed";
+            return Result::Error;
+        }
+        if (event.revents & POLLERR) {
+            LOG(ERROR) << "error reading inotify for " << path_;
+            return Result::Error;
+        }
+
+        // Note that we don't bother checking what kind of event it is, since
+        // it's cheap enough to just see if the initial condition is satisified.
+        // If it's not, we consume all the events available and continue.
+        if (CheckCompleted()) return Result::Success;
+        if (!ConsumeEvents()) return Result::Error;
+    }
+}
+
+bool OneShotInotify::CheckCompleted() {
+    if (mask_ == IN_CREATE) {
+        finished_ = !access(path_.c_str(), F_OK) || errno != ENOENT;
+    } else if (mask_ == IN_DELETE_SELF) {
+        finished_ = access(path_.c_str(), F_OK) && errno == ENOENT;
+    } else {
+        LOG(ERROR) << "Unexpected mask: " << mask_;
+    }
+    return finished_;
+}
+
+bool OneShotInotify::ConsumeEvents() {
+    // According to the manpage, this is enough to read at least one event.
+    static constexpr size_t kBufferSize = sizeof(struct inotify_event) + NAME_MAX + 1;
+    char buffer[kBufferSize];
+
+    do {
+        ssize_t rv = TEMP_FAILURE_RETRY(read(inotify_fd_, buffer, sizeof(buffer)));
+        if (rv <= 0) {
+            if (rv == 0 || errno == EAGAIN) {
+                return true;
+            }
+            PLOG(ERROR) << "read inotify failed";
+            return false;
+        }
+    } while (true);
+}
+
+int64_t OneShotInotify::RemainingMs() const {
+    auto remaining = (std::chrono::steady_clock::now() - start_time_);
+    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(remaining);
+    return (relative_timeout_ - elapsed).count();
+}
+#endif
+
+bool WaitForFile(const std::string& path, const std::chrono::milliseconds relative_timeout) {
+#if defined(__linux__)
+    OneShotInotify inotify(path, IN_CREATE, relative_timeout);
+    return inotify.Wait();
+#else
+    return PollForFile(path, relative_timeout);
+#endif
+}
+
+// Wait at most |relative_timeout| milliseconds for |path| to stop existing.
+bool WaitForFileDeleted(const std::string& path, const std::chrono::milliseconds relative_timeout) {
+#if defined(__linux__)
+    OneShotInotify inotify(path, IN_DELETE_SELF, relative_timeout);
+    return inotify.Wait();
+#else
+    return PollForFileDeleted(path, relative_timeout);
+#endif
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 9aab0ba..259f800 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -14,10 +14,13 @@
  * limitations under the License.
  */
 
+#include "fs_mgr.h"
+
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <libgen.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -31,9 +34,12 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <functional>
+#include <map>
 #include <memory>
 #include <string>
 #include <thread>
+#include <utility>
 #include <vector>
 
 #include <android-base/file.h>
@@ -46,20 +52,21 @@
 #include <cutils/partition_utils.h>
 #include <cutils/properties.h>
 #include <ext4_utils/ext4.h>
-#include <ext4_utils/ext4_crypt_init_extensions.h>
 #include <ext4_utils/ext4_sb.h>
 #include <ext4_utils/ext4_utils.h>
 #include <ext4_utils/wipe.h>
+#include <fs_avb/fs_avb.h>
+#include <fs_mgr/file_wait.h>
+#include <fs_mgr_overlayfs.h>
+#include <libdm/dm.h>
+#include <liblp/metadata_format.h>
 #include <linux/fs.h>
 #include <linux/loop.h>
 #include <linux/magic.h>
 #include <log/log_properties.h>
 #include <logwrap/logwrap.h>
 
-#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"
@@ -73,9 +80,24 @@
 
 #define ZRAM_CONF_DEV   "/sys/block/zram0/disksize"
 #define ZRAM_CONF_MCS   "/sys/block/zram0/max_comp_streams"
+#define ZRAM_BACK_DEV   "/sys/block/zram0/backing_dev"
+
+#define SYSFS_EXT4_VERITY "/sys/fs/ext4/features/verity"
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
+using android::base::Basename;
+using android::base::Realpath;
+using android::base::StartsWith;
+using android::base::unique_fd;
+using android::dm::DeviceMapper;
+using android::dm::DmDeviceState;
+
+// Realistically, this file should be part of the android::fs_mgr namespace;
+using namespace android::fs_mgr;
+
+using namespace std::literals;
+
 // record fs stat
 enum FsStatFlags {
     FS_STAT_IS_EXT4 = 0x0001,
@@ -88,34 +110,17 @@
     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_INVALID_MAGIC = 0x0800,
     FS_STAT_TOGGLE_QUOTAS_FAILED = 0x10000,
     FS_STAT_SET_RESERVED_BLOCKS_FAILED = 0x20000,
     FS_STAT_ENABLE_ENCRYPTION_FAILED = 0x40000,
+    FS_STAT_ENABLE_VERITY_FAILED = 0x80000,
 };
 
-// TODO: switch to inotify()
-bool fs_mgr_wait_for_file(const std::string& filename,
-                          const std::chrono::milliseconds relative_timeout) {
-    auto start_time = std::chrono::steady_clock::now();
-
-    while (true) {
-        if (!access(filename.c_str(), F_OK) || errno != ENOENT) {
-            return true;
-        }
-
-        std::this_thread::sleep_for(50ms);
-
-        auto now = std::chrono::steady_clock::now();
-        auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
-        if (time_elapsed > relative_timeout) return false;
-    }
-}
-
-static void log_fs_stat(const char* blk_device, int fs_stat)
-{
+static void log_fs_stat(const std::string& blk_device, int fs_stat) {
     if ((fs_stat & FS_STAT_IS_EXT4) == 0) return; // only log ext4
-    std::string msg = android::base::StringPrintf("\nfs_stat,%s,0x%x\n", blk_device, fs_stat);
+    std::string msg =
+            android::base::StringPrintf("\nfs_stat,%s,0x%x\n", blk_device.c_str(), fs_stat);
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(FSCK_LOG_FILE, O_WRONLY | O_CLOEXEC |
                                                         O_APPEND | O_CREAT, 0664)));
     if (fd == -1 || !android::base::WriteStringToFd(msg, fd)) {
@@ -127,6 +132,18 @@
     return fs_type == "ext4" || fs_type == "ext3" || fs_type == "ext2";
 }
 
+static bool is_f2fs(const std::string& fs_type) {
+    return fs_type == "f2fs";
+}
+
+static std::string realpath(const std::string& blk_device) {
+    std::string real_path;
+    if (!Realpath(blk_device, &real_path)) {
+        real_path = blk_device;
+    }
+    return real_path;
+}
+
 static bool should_force_check(int fs_stat) {
     return fs_stat &
            (FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |
@@ -135,20 +152,21 @@
             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)
-{
+static void check_fs(const std::string& blk_device, const std::string& fs_type,
+                     const std::string& target, int* fs_stat) {
     int status;
     int ret;
     long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID;
-    char tmpmnt_opts[64] = "errors=remount-ro";
-    const char* e2fsck_argv[] = {E2FSCK_BIN, "-y", blk_device};
-    const char* e2fsck_forced_argv[] = {E2FSCK_BIN, "-f", "-y", blk_device};
+    auto tmpmnt_opts = "errors=remount-ro"s;
+    const char* e2fsck_argv[] = {E2FSCK_BIN, "-y", blk_device.c_str()};
+    const char* e2fsck_forced_argv[] = {E2FSCK_BIN, "-f", "-y", blk_device.c_str()};
+
+    if (*fs_stat & FS_STAT_INVALID_MAGIC) {  // will fail, so do not try
+        return;
+    }
 
     /* Check for the types of filesystems we know how to check */
     if (is_extfs(fs_type)) {
-        if (*fs_stat & FS_STAT_EXT4_INVALID_MAGIC) {  // will fail, so do not try
-            return;
-        }
         /*
          * First try to mount and unmount the filesystem.  We do this because
          * the kernel is more efficient than e2fsck in running the journal and
@@ -164,18 +182,19 @@
          */
         if (!(*fs_stat & FS_STAT_FULL_MOUNT_FAILED)) {  // already tried if full mount failed
             errno = 0;
-            if (!strcmp(fs_type, "ext4")) {
+            if (fs_type == "ext4") {
                 // This option is only valid with ext4
-                strlcat(tmpmnt_opts, ",nomblk_io_submit", sizeof(tmpmnt_opts));
+                tmpmnt_opts += ",nomblk_io_submit";
             }
-            ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
+            ret = mount(blk_device.c_str(), target.c_str(), fs_type.c_str(), tmpmnt_flags,
+                        tmpmnt_opts.c_str());
             PINFO << __FUNCTION__ << "(): mount(" << blk_device << "," << target << "," << fs_type
                   << ")=" << ret;
             if (!ret) {
                 bool umounted = false;
                 int retry_count = 5;
                 while (retry_count-- > 0) {
-                    umounted = umount(target) == 0;
+                    umounted = umount(target.c_str()) == 0;
                     if (umounted) {
                         LINFO << __FUNCTION__ << "(): unmount(" << target << ") succeeded";
                         break;
@@ -198,18 +217,18 @@
          * (e.g. recent SDK system images). Detect these and skip the check.
          */
         if (access(E2FSCK_BIN, X_OK)) {
-            LINFO << "Not running " << E2FSCK_BIN << " on " << blk_device
+            LINFO << "Not running " << E2FSCK_BIN << " on " << realpath(blk_device)
                   << " (executable not in system image)";
         } else {
-            LINFO << "Running " << E2FSCK_BIN << " on " << blk_device;
+            LINFO << "Running " << E2FSCK_BIN << " on " << realpath(blk_device);
             if (should_force_check(*fs_stat)) {
                 ret = android_fork_execvp_ext(
                     ARRAY_SIZE(e2fsck_forced_argv), const_cast<char**>(e2fsck_forced_argv), &status,
-                    true, LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), NULL, 0);
+                    true, LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), nullptr, 0);
             } else {
                 ret = android_fork_execvp_ext(
                     ARRAY_SIZE(e2fsck_argv), const_cast<char**>(e2fsck_argv), &status, true,
-                    LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), NULL, 0);
+                    LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), nullptr, 0);
             }
 
             if (ret < 0) {
@@ -221,19 +240,21 @@
                 *fs_stat |= FS_STAT_E2FSCK_FS_FIXED;
             }
         }
-    } else if (!strcmp(fs_type, "f2fs")) {
-            const char *f2fs_fsck_argv[] = {
-                    F2FS_FSCK_BIN,
-                    "-a",
-                    blk_device
-            };
-        LINFO << "Running " << F2FS_FSCK_BIN << " -a " << blk_device;
+    } else if (is_f2fs(fs_type)) {
+        const char* f2fs_fsck_argv[] = {F2FS_FSCK_BIN, "-a", blk_device.c_str()};
+        const char* f2fs_fsck_forced_argv[] = {F2FS_FSCK_BIN, "-f", blk_device.c_str()};
 
-        ret = android_fork_execvp_ext(ARRAY_SIZE(f2fs_fsck_argv),
-                                      const_cast<char **>(f2fs_fsck_argv),
-                                      &status, true, LOG_KLOG | LOG_FILE,
-                                      true, const_cast<char *>(FSCK_LOG_FILE),
-                                      NULL, 0);
+        if (should_force_check(*fs_stat)) {
+            LINFO << "Running " << F2FS_FSCK_BIN << " -f " << realpath(blk_device);
+            ret = android_fork_execvp_ext(
+                ARRAY_SIZE(f2fs_fsck_forced_argv), const_cast<char**>(f2fs_fsck_forced_argv), &status,
+                true, LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), nullptr, 0);
+        } else {
+            LINFO << "Running " << F2FS_FSCK_BIN << " -a " << realpath(blk_device);
+            ret = android_fork_execvp_ext(
+                ARRAY_SIZE(f2fs_fsck_argv), const_cast<char**>(f2fs_fsck_argv), &status, true,
+                LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), nullptr, 0);
+        }
         if (ret < 0) {
             /* No need to check for error in fork, we can't really handle it now */
             LERROR << "Failed trying to run " << F2FS_FSCK_BIN;
@@ -261,16 +282,17 @@
 }
 
 // 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)));
+// false.  If it's not an ext4 filesystem, also set FS_STAT_INVALID_MAGIC.
+static bool read_ext4_superblock(const std::string& blk_device, struct ext4_super_block* sb,
+                                 int* fs_stat) {
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
 
     if (fd < 0) {
         PERROR << "Failed to open '" << blk_device << "'";
         return false;
     }
 
-    if (pread(fd, sb, sizeof(*sb), 1024) != sizeof(*sb)) {
+    if (TEMP_FAILURE_RETRY(pread(fd, sb, sizeof(*sb), 1024)) != sizeof(*sb)) {
         PERROR << "Can't read '" << blk_device << "' superblock";
         return false;
     }
@@ -278,7 +300,7 @@
     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;
+        *fs_stat |= FS_STAT_INVALID_MAGIC;
         return false;
     }
     *fs_stat |= FS_STAT_IS_EXT4;
@@ -289,6 +311,17 @@
     return true;
 }
 
+// exported silent version of the above that just answer the question is_ext4
+bool fs_mgr_is_ext4(const std::string& blk_device) {
+    android::base::ErrnoRestorer restore;
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd < 0) return false;
+    ext4_super_block sb;
+    if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), 1024)) != sizeof(sb)) return false;
+    if (!is_ext4_superblock_valid(&sb)) return false;
+    return true;
+}
+
 // Some system images do not have tune2fs for licensing reasons.
 // Detect these and skip running it.
 static bool tune2fs_available(void) {
@@ -304,10 +337,10 @@
 }
 
 // Enable/disable quota support on the filesystem if needed.
-static void tune_quota(const char* blk_device, const struct fstab_rec* rec,
+static void tune_quota(const std::string& blk_device, const FstabEntry& entry,
                        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;
+    bool want_quota = entry.fs_mgr_flags.quota;
 
     if (has_quota == want_quota) {
         return;
@@ -319,7 +352,7 @@
         return;
     }
 
-    const char* argv[] = {TUNE2FS_BIN, nullptr, nullptr, blk_device};
+    const char* argv[] = {TUNE2FS_BIN, nullptr, nullptr, blk_device.c_str()};
 
     if (want_quota) {
         LINFO << "Enabling quotas on " << blk_device;
@@ -340,16 +373,16 @@
 }
 
 // Set the number of reserved filesystem blocks if needed.
-static void tune_reserved_size(const char* blk_device, const struct fstab_rec* rec,
+static void tune_reserved_size(const std::string& blk_device, const FstabEntry& entry,
                                const struct ext4_super_block* sb, int* fs_stat) {
-    if (!(rec->fs_mgr_flags & MF_RESERVEDSIZE)) {
+    if (entry.reserved_size == 0) {
         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);
+    uint64_t reserved_blocks = entry.reserved_size / EXT4_BLOCK_SIZE(sb);
 
     if (reserved_blocks > max_reserved_blocks) {
         LWARNING << "Reserved blocks " << reserved_blocks << " is too large; "
@@ -372,7 +405,8 @@
     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};
+            TUNE2FS_BIN,       "-r", reserved_blocks_str.c_str(), "-g", reserved_gid_str.c_str(),
+            blk_device.c_str()};
     if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
         LERROR << "Failed to run " TUNE2FS_BIN " to set the number of reserved blocks on "
                << blk_device;
@@ -381,10 +415,10 @@
 }
 
 // Enable file-based encryption if needed.
-static void tune_encrypt(const char* blk_device, const struct fstab_rec* rec,
+static void tune_encrypt(const std::string& blk_device, const FstabEntry& entry,
                          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;
+    bool want_encrypt = entry.fs_mgr_flags.file_encryption;
 
     if (has_encrypt || !want_encrypt) {
         return;
@@ -396,7 +430,7 @@
         return;
     }
 
-    const char* argv[] = {TUNE2FS_BIN, "-Oencrypt", blk_device};
+    const char* argv[] = {TUNE2FS_BIN, "-Oencrypt", blk_device.c_str()};
 
     LINFO << "Enabling ext4 encryption on " << blk_device;
     if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
@@ -406,6 +440,91 @@
     }
 }
 
+// Enable fs-verity if needed.
+static void tune_verity(const std::string& blk_device, const FstabEntry& entry,
+                        const struct ext4_super_block* sb, int* fs_stat) {
+    bool has_verity = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_VERITY)) != 0;
+    bool want_verity = entry.fs_mgr_flags.fs_verity;
+
+    if (has_verity || !want_verity) {
+        return;
+    }
+
+    std::string verity_support;
+    if (!android::base::ReadFileToString(SYSFS_EXT4_VERITY, &verity_support)) {
+        LERROR << "Failed to open " << SYSFS_EXT4_VERITY;
+        return;
+    }
+
+    if (!(android::base::Trim(verity_support) == "supported")) {
+        LERROR << "Current ext4 verity not supported by kernel";
+        return;
+    }
+
+    if (!tune2fs_available()) {
+        LERROR << "Unable to enable ext4 verity on " << blk_device
+               << " because " TUNE2FS_BIN " is missing";
+        return;
+    }
+
+    LINFO << "Enabling ext4 verity on " << blk_device;
+
+    const char* argv[] = {TUNE2FS_BIN, "-O", "verity", blk_device.c_str()};
+    if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+        LERROR << "Failed to run " TUNE2FS_BIN " to enable "
+               << "ext4 verity on " << blk_device;
+        *fs_stat |= FS_STAT_ENABLE_VERITY_FAILED;
+    }
+}
+
+// Read the primary superblock from an f2fs filesystem.  On failure return
+// false.  If it's not an f2fs filesystem, also set FS_STAT_INVALID_MAGIC.
+#define F2FS_BLKSIZE 4096
+#define F2FS_SUPER_OFFSET 1024
+static bool read_f2fs_superblock(const std::string& blk_device, int* fs_stat) {
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
+    __le32 sb1, sb2;
+
+    if (fd < 0) {
+        PERROR << "Failed to open '" << blk_device << "'";
+        return false;
+    }
+
+    if (TEMP_FAILURE_RETRY(pread(fd, &sb1, sizeof(sb1), F2FS_SUPER_OFFSET)) != sizeof(sb1)) {
+        PERROR << "Can't read '" << blk_device << "' superblock1";
+        return false;
+    }
+    if (TEMP_FAILURE_RETRY(pread(fd, &sb2, sizeof(sb2), F2FS_BLKSIZE + F2FS_SUPER_OFFSET)) !=
+        sizeof(sb2)) {
+        PERROR << "Can't read '" << blk_device << "' superblock2";
+        return false;
+    }
+
+    if (sb1 != cpu_to_le32(F2FS_SUPER_MAGIC) && sb2 != cpu_to_le32(F2FS_SUPER_MAGIC)) {
+        LINFO << "Invalid f2fs superblock on '" << blk_device << "'";
+        *fs_stat |= FS_STAT_INVALID_MAGIC;
+        return false;
+    }
+    return true;
+}
+
+// exported silent version of the above that just answer the question is_f2fs
+bool fs_mgr_is_f2fs(const std::string& blk_device) {
+    android::base::ErrnoRestorer restore;
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd < 0) return false;
+    __le32 sb;
+    if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), F2FS_SUPER_OFFSET)) != sizeof(sb)) {
+        return false;
+    }
+    if (sb == cpu_to_le32(F2FS_SUPER_MAGIC)) return true;
+    if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), F2FS_BLKSIZE + F2FS_SUPER_OFFSET)) !=
+        sizeof(sb)) {
+        return false;
+    }
+    return sb == cpu_to_le32(F2FS_SUPER_MAGIC);
+}
+
 //
 // Prepare the filesystem on the given block device to be mounted.
 //
@@ -415,10 +534,10 @@
 // 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) {
+static int prepare_fs_for_mount(const std::string& blk_device, const FstabEntry& entry) {
     int fs_stat = 0;
 
-    if (is_extfs(rec->fs_type)) {
+    if (is_extfs(entry.fs_type)) {
         struct ext4_super_block sb;
 
         if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
@@ -431,60 +550,45 @@
             }
 
             // Note: quotas should be enabled before running fsck.
-            tune_quota(blk_device, rec, &sb, &fs_stat);
+            tune_quota(blk_device, entry, &sb, &fs_stat);
         } else {
             return fs_stat;
         }
+    } else if (is_f2fs(entry.fs_type)) {
+        if (!read_f2fs_superblock(blk_device, &fs_stat)) {
+            return fs_stat;
+        }
     }
 
-    if ((rec->fs_mgr_flags & MF_CHECK) ||
+    if (entry.fs_mgr_flags.check ||
         (fs_stat & (FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED))) {
-        check_fs(blk_device, rec->fs_type, rec->mount_point, &fs_stat);
+        check_fs(blk_device, entry.fs_type, entry.mount_point, &fs_stat);
     }
 
-    if (is_extfs(rec->fs_type) && (rec->fs_mgr_flags & (MF_RESERVEDSIZE | MF_FILEENCRYPTION))) {
+    if (is_extfs(entry.fs_type) &&
+        (entry.reserved_size != 0 || entry.fs_mgr_flags.file_encryption ||
+         entry.fs_mgr_flags.fs_verity)) {
         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);
+            tune_reserved_size(blk_device, entry, &sb, &fs_stat);
+            tune_encrypt(blk_device, entry, &sb, &fs_stat);
+            tune_verity(blk_device, entry, &sb, &fs_stat);
         }
     }
 
     return fs_stat;
 }
 
-static void remove_trailing_slashes(char *n)
-{
-    int len;
-
-    len = strlen(n) - 1;
-    while ((*(n + len) == '/') && len) {
-      *(n + len) = '\0';
-      len--;
-    }
-}
-
-/*
- * Mark the given block device as read-only, using the BLKROSET ioctl.
- * Return 0 on success, and -1 on error.
- */
-int fs_mgr_set_blk_ro(const char *blockdev)
-{
-    int fd;
-    int rc = -1;
-    int ON = 1;
-
-    fd = TEMP_FAILURE_RETRY(open(blockdev, O_RDONLY | O_CLOEXEC));
+// Mark the given block device as read-only, using the BLKROSET ioctl.
+bool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly) {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(blockdev.c_str(), O_RDONLY | O_CLOEXEC)));
     if (fd < 0) {
-        // should never happen
-        return rc;
+        return false;
     }
 
-    rc = ioctl(fd, BLKROSET, &ON);
-    close(fd);
-
-    return rc;
+    int ON = readonly;
+    return ioctl(fd, BLKROSET, &ON) == 0;
 }
 
 // Orange state means the device is unlocked, see the following link for details.
@@ -497,30 +601,42 @@
     return false;
 }
 
-/*
- * __mount(): wrapper around the mount() system call which also
- * sets the underlying block device to read-only if the mount is read-only.
- * See "man 2 mount" for return values.
- */
-static int __mount(const char *source, const char *target, const struct fstab_rec *rec)
-{
-    unsigned long mountflags = rec->flags;
-    int ret;
-    int save_errno;
-
-    /* We need this because sometimes we have legacy symlinks
-     * that are lingering around and need cleaning up.
-     */
+// __mount(): wrapper around the mount() system call which also
+// sets the underlying block device to read-only if the mount is read-only.
+// See "man 2 mount" for return values.
+static int __mount(const std::string& source, const std::string& target, const FstabEntry& entry) {
+    // We need this because sometimes we have legacy symlinks that are
+    // lingering around and need cleaning up.
     struct stat info;
-    if (!lstat(target, &info))
-        if ((info.st_mode & S_IFMT) == S_IFLNK)
-            unlink(target);
-    mkdir(target, 0755);
+    if (lstat(target.c_str(), &info) == 0 && (info.st_mode & S_IFMT) == S_IFLNK) {
+        unlink(target.c_str());
+    }
+    mkdir(target.c_str(), 0755);
     errno = 0;
-    ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options);
-    save_errno = errno;
-    PINFO << __FUNCTION__ << "(source=" << source << ",target=" << target
-          << ",type=" << rec->fs_type << ")=" << ret;
+    unsigned long mountflags = entry.flags;
+    int ret = 0;
+    int save_errno = 0;
+    do {
+        if (save_errno == EAGAIN) {
+            PINFO << "Retrying mount (source=" << source << ",target=" << target
+                  << ",type=" << entry.fs_type << ")=" << ret << "(" << save_errno << ")";
+        }
+        ret = mount(source.c_str(), target.c_str(), entry.fs_type.c_str(), mountflags,
+                    entry.fs_options.c_str());
+        save_errno = errno;
+    } while (ret && save_errno == EAGAIN);
+    const char* target_missing = "";
+    const char* source_missing = "";
+    if (save_errno == ENOENT) {
+        if (access(target.c_str(), F_OK)) {
+            target_missing = "(missing)";
+        } else if (access(source.c_str(), F_OK)) {
+            source_missing = "(missing)";
+        }
+        errno = save_errno;
+    }
+    PINFO << __FUNCTION__ << "(source=" << source << source_missing << ",target=" << target
+          << target_missing << ",type=" << entry.fs_type << ")=" << ret;
     if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
         fs_mgr_set_blk_ro(source);
     }
@@ -528,250 +644,208 @@
     return ret;
 }
 
-static int fs_match(const char *in1, const char *in2)
-{
-    char *n1;
-    char *n2;
-    int ret;
-
-    n1 = strdup(in1);
-    n2 = strdup(in2);
-
-    remove_trailing_slashes(n1);
-    remove_trailing_slashes(n2);
-
-    ret = !strcmp(n1, n2);
-
-    free(n1);
-    free(n2);
-
-    return ret;
-}
-
-/*
- * Tries to mount any of the consecutive fstab entries that match
- * the mountpoint of the one given by fstab->recs[start_idx].
- *
- * end_idx: On return, will be the last rec that was looked at.
- * attempted_idx: On return, will indicate which fstab rec
- *     succeeded. In case of failure, it will be the start_idx.
- * Returns
- *   -1 on failure with errno set to match the 1st mount failure.
- *   0 on success.
- */
-static int mount_with_alternatives(struct fstab *fstab, int start_idx, int *end_idx, int *attempted_idx)
-{
-    int i;
-    int mount_errno = 0;
-    int mounted = 0;
-
-    if (!end_idx || !attempted_idx || start_idx >= fstab->num_entries) {
-      errno = EINVAL;
-      if (end_idx) *end_idx = start_idx;
-      if (attempted_idx) *attempted_idx = start_idx;
-      return -1;
+static bool fs_match(const std::string& in1, const std::string& in2) {
+    if (in1.empty() || in2.empty()) {
+        return false;
     }
 
-    /* Hunt down an fstab entry for the same mount point that might succeed */
+    auto in1_end = in1.size() - 1;
+    while (in1_end > 0 && in1[in1_end] == '/') {
+        in1_end--;
+    }
+
+    auto in2_end = in2.size() - 1;
+    while (in2_end > 0 && in2[in2_end] == '/') {
+        in2_end--;
+    }
+
+    if (in1_end != in2_end) {
+        return false;
+    }
+
+    for (size_t i = 0; i <= in1_end; ++i) {
+        if (in1[i] != in2[i]) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+// Tries to mount any of the consecutive fstab entries that match
+// the mountpoint of the one given by fstab[start_idx].
+//
+// end_idx: On return, will be the last entry that was looked at.
+// attempted_idx: On return, will indicate which fstab entry
+//     succeeded. In case of failure, it will be the start_idx.
+// Sets errno to match the 1st mount failure on failure.
+static bool mount_with_alternatives(const Fstab& fstab, int start_idx, int* end_idx,
+                                    int* attempted_idx) {
+    unsigned long i;
+    int mount_errno = 0;
+    bool mounted = false;
+
+    // Hunt down an fstab entry for the same mount point that might succeed.
     for (i = start_idx;
-         /* We required that fstab entries for the same mountpoint be consecutive */
-         i < fstab->num_entries && !strcmp(fstab->recs[start_idx].mount_point, fstab->recs[i].mount_point);
-         i++) {
-            /*
-             * Don't try to mount/encrypt the same mount point again.
-             * Deal with alternate entries for the same point which are required to be all following
-             * each other.
-             */
-            if (mounted) {
-                LERROR << __FUNCTION__ << "(): skipping fstab dup mountpoint="
-                       << fstab->recs[i].mount_point << " rec[" << i
-                       << "].fs_type=" << fstab->recs[i].fs_type
-                       << " already mounted as "
-                       << fstab->recs[*attempted_idx].fs_type;
-                continue;
-            }
+         // We required that fstab entries for the same mountpoint be consecutive.
+         i < fstab.size() && fstab[start_idx].mount_point == fstab[i].mount_point; i++) {
+        // Don't try to mount/encrypt the same mount point again.
+        // Deal with alternate entries for the same point which are required to be all following
+        // each other.
+        if (mounted) {
+            LERROR << __FUNCTION__ << "(): skipping fstab dup mountpoint=" << fstab[i].mount_point
+                   << " rec[" << i << "].fs_type=" << fstab[i].fs_type << " already mounted as "
+                   << fstab[*attempted_idx].fs_type;
+            continue;
+        }
 
-            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
-                       << "].fs_type=" << fstab->recs[i].fs_type;
-                mount_errno = EINVAL;  // continue bootup for FDE
-                continue;
-            }
+        int fs_stat = prepare_fs_for_mount(fstab[i].blk_device, fstab[i]);
+        if (fs_stat & FS_STAT_INVALID_MAGIC) {
+            LERROR << __FUNCTION__
+                   << "(): skipping mount due to invalid magic, mountpoint=" << fstab[i].mount_point
+                   << " blk_dev=" << realpath(fstab[i].blk_device) << " rec[" << i
+                   << "].fs_type=" << fstab[i].fs_type;
+            mount_errno = EINVAL;  // continue bootup for FDE
+            continue;
+        }
 
-            int retry_count = 2;
-            while (retry_count-- > 0) {
-                if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point,
-                             &fstab->recs[i])) {
-                    *attempted_idx = i;
-                    mounted = 1;
-                    if (i != start_idx) {
-                        LERROR << __FUNCTION__ << "(): Mounted " << fstab->recs[i].blk_device
-                               << " on " << fstab->recs[i].mount_point
-                               << " with fs_type=" << fstab->recs[i].fs_type << " instead of "
-                               << fstab->recs[start_idx].fs_type;
-                    }
-                    fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
-                    mount_errno = 0;
-                    break;
-                } else {
-                    if (retry_count <= 0) break;  // run check_fs only once
-                    fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
-                    /* back up the first errno for crypto decisions */
-                    if (mount_errno == 0) {
-                        mount_errno = errno;
-                    }
-                    // retry after fsck
-                    check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
-                             fstab->recs[i].mount_point, &fs_stat);
+        int retry_count = 2;
+        while (retry_count-- > 0) {
+            if (!__mount(fstab[i].blk_device, fstab[i].mount_point, fstab[i])) {
+                *attempted_idx = i;
+                mounted = true;
+                if (i != start_idx) {
+                    LERROR << __FUNCTION__ << "(): Mounted " << fstab[i].blk_device << " on "
+                           << fstab[i].mount_point << " with fs_type=" << fstab[i].fs_type
+                           << " instead of " << fstab[start_idx].fs_type;
                 }
+                fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
+                mount_errno = 0;
+                break;
+            } else {
+                if (retry_count <= 0) break;  // run check_fs only once
+                fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
+                // back up the first errno for crypto decisions.
+                if (mount_errno == 0) {
+                    mount_errno = errno;
+                }
+                // retry after fsck
+                check_fs(fstab[i].blk_device, fstab[i].fs_type, fstab[i].mount_point, &fs_stat);
             }
-            log_fs_stat(fstab->recs[i].blk_device, fs_stat);
+        }
+        log_fs_stat(fstab[i].blk_device, fs_stat);
     }
 
     /* Adjust i for the case where it was still withing the recs[] */
-    if (i < fstab->num_entries) --i;
+    if (i < fstab.size()) --i;
 
     *end_idx = i;
     if (!mounted) {
         *attempted_idx = start_idx;
         errno = mount_errno;
-        return -1;
+        return false;
     }
-    return 0;
+    return true;
 }
 
-static int translate_ext_labels(struct fstab_rec *rec)
-{
-    DIR *blockdir = NULL;
-    struct dirent *ent;
-    char *label;
-    size_t label_len;
-    int ret = -1;
-
-    if (strncmp(rec->blk_device, "LABEL=", 6))
-        return 0;
-
-    label = rec->blk_device + 6;
-    label_len = strlen(label);
-
-    if (label_len > 16) {
-        LERROR << "FS label is longer than allowed by filesystem";
-        goto out;
+static bool TranslateExtLabels(FstabEntry* entry) {
+    if (!StartsWith(entry->blk_device, "LABEL=")) {
+        return true;
     }
 
+    std::string label = entry->blk_device.substr(6);
+    if (label.size() > 16) {
+        LERROR << "FS label is longer than allowed by filesystem";
+        return false;
+    }
 
-    blockdir = opendir("/dev/block");
+    auto blockdir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/dev/block"), closedir};
     if (!blockdir) {
         LERROR << "couldn't open /dev/block";
-        goto out;
+        return false;
     }
 
-    while ((ent = readdir(blockdir))) {
-        int fd;
-        char super_buf[1024];
-        struct ext4_super_block *sb;
-
+    struct dirent* ent;
+    while ((ent = readdir(blockdir.get()))) {
         if (ent->d_type != DT_BLK)
             continue;
 
-        fd = openat(dirfd(blockdir), ent->d_name, O_RDONLY);
+        unique_fd fd(TEMP_FAILURE_RETRY(
+                openat(dirfd(blockdir.get()), ent->d_name, O_RDONLY | O_CLOEXEC)));
         if (fd < 0) {
             LERROR << "Cannot open block device /dev/block/" << ent->d_name;
-            goto out;
+            return false;
         }
 
+        ext4_super_block super_block;
         if (TEMP_FAILURE_RETRY(lseek(fd, 1024, SEEK_SET)) < 0 ||
-            TEMP_FAILURE_RETRY(read(fd, super_buf, 1024)) != 1024) {
-            /* Probably a loopback device or something else without a readable
-             * superblock.
-             */
-            close(fd);
+            TEMP_FAILURE_RETRY(read(fd, &super_block, sizeof(super_block))) !=
+                    sizeof(super_block)) {
+            // Probably a loopback device or something else without a readable superblock.
             continue;
         }
 
-        sb = (struct ext4_super_block *)super_buf;
-        if (sb->s_magic != EXT4_SUPER_MAGIC) {
+        if (super_block.s_magic != EXT4_SUPER_MAGIC) {
             LINFO << "/dev/block/" << ent->d_name << " not ext{234}";
             continue;
         }
 
-        if (!strncmp(label, sb->s_volume_name, label_len)) {
-            char *new_blk_device;
+        if (label == super_block.s_volume_name) {
+            std::string new_blk_device = "/dev/block/"s + ent->d_name;
 
-            if (asprintf(&new_blk_device, "/dev/block/%s", ent->d_name) < 0) {
-                LERROR << "Could not allocate block device string";
-                goto out;
-            }
+            LINFO << "resolved label " << entry->blk_device << " to " << new_blk_device;
 
-            LINFO << "resolved label " << rec->blk_device << " to "
-                  << new_blk_device;
-
-            free(rec->blk_device);
-            rec->blk_device = new_blk_device;
-            ret = 0;
-            break;
+            entry->blk_device = new_blk_device;
+            return true;
         }
     }
 
-out:
-    closedir(blockdir);
-    return ret;
+    return false;
 }
 
-static bool needs_block_encryption(const struct fstab_rec* rec)
-{
-    if (android::base::GetBoolProperty("ro.vold.forceencryption", false) &&
-        fs_mgr_is_encryptable(rec))
+static bool needs_block_encryption(const FstabEntry& entry) {
+    if (android::base::GetBoolProperty("ro.vold.forceencryption", false) && entry.is_encryptable())
         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 */
-        char convert_fde_name[PATH_MAX];
-        snprintf(convert_fde_name, sizeof(convert_fde_name),
-                 "%s/misc/vold/convert_fde", rec->mount_point);
-        if (access(convert_fde_name, F_OK) == 0) return true;
+    if (entry.fs_mgr_flags.force_crypt) return true;
+    if (entry.fs_mgr_flags.crypt) {
+        // Check for existence of convert_fde breadcrumb file.
+        auto convert_fde_name = entry.mount_point + "/misc/vold/convert_fde";
+        if (access(convert_fde_name.c_str(), F_OK) == 0) return true;
     }
-    if (rec->fs_mgr_flags & MF_FORCEFDEORFBE) {
-        /* Check for absence of convert_fbe breadcrumb file */
-        char convert_fbe_name[PATH_MAX];
-        snprintf(convert_fbe_name, sizeof(convert_fbe_name),
-                 "%s/convert_fbe", rec->mount_point);
-        if (access(convert_fbe_name, F_OK) != 0) return true;
+    if (entry.fs_mgr_flags.force_fde_or_fbe) {
+        // Check for absence of convert_fbe breadcrumb file.
+        auto convert_fbe_name = entry.mount_point + "/convert_fbe";
+        if (access(convert_fbe_name.c_str(), F_OK) != 0) return true;
     }
     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;
+static bool should_use_metadata_encryption(const FstabEntry& entry) {
+    return !entry.key_dir.empty() &&
+           (entry.fs_mgr_flags.file_encryption || entry.fs_mgr_flags.force_fde_or_fbe);
 }
 
 // Check to see if a mountable volume has encryption requirements
-static int handle_encryptable(const struct fstab_rec* rec)
-{
-    /* If this is block encryptable, need to trigger encryption */
-    if (needs_block_encryption(rec)) {
-        if (umount(rec->mount_point) == 0) {
+static int handle_encryptable(const FstabEntry& entry) {
+    // If this is block encryptable, need to trigger encryption.
+    if (needs_block_encryption(entry)) {
+        if (umount(entry.mount_point.c_str()) == 0) {
             return FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;
         } else {
-            PWARNING << "Could not umount " << rec->mount_point
-                     << " - allow continue unencrypted";
+            PWARNING << "Could not umount " << entry.mount_point << " - allow continue unencrypted";
             return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
         }
-    } else if (should_use_metadata_encryption(rec)) {
-        if (umount(rec->mount_point) == 0) {
+    } else if (should_use_metadata_encryption(entry)) {
+        if (umount(entry.mount_point.c_str()) == 0) {
             return FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION;
         } else {
-            PERROR << "Could not umount " << rec->mount_point << " - fail since can't encrypt";
+            PERROR << "Could not umount " << entry.mount_point << " - fail since can't encrypt";
             return FS_MGR_MNTALL_FAIL;
         }
-    } else if (rec->fs_mgr_flags & (MF_FILEENCRYPTION | MF_FORCEFDEORFBE)) {
-        LINFO << rec->mount_point << " is file encrypted";
+    } else if (entry.fs_mgr_flags.file_encryption || entry.fs_mgr_flags.force_fde_or_fbe) {
+        LINFO << entry.mount_point << " is file encrypted";
         return FS_MGR_MNTALL_DEV_FILE_ENCRYPTED;
-    } else if (fs_mgr_is_encryptable(rec)) {
+    } else if (entry.is_encryptable()) {
         return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
     } else {
         return FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
@@ -785,7 +859,8 @@
         argv.emplace_back(arg.c_str());
     }
     LOG(INFO) << "Calling: " << android::base::Join(argv, ' ');
-    int ret = android_fork_execvp(4, const_cast<char**>(argv.data()), nullptr, false, true);
+    int ret =
+            android_fork_execvp(argv.size(), const_cast<char**>(argv.data()), nullptr, false, true);
     if (ret != 0) {
         LOG(ERROR) << "vdc returned error code: " << ret;
         return false;
@@ -794,83 +869,297 @@
     return true;
 }
 
-/* When multiple fstab records share the same mount_point, it will
- * try to mount each one in turn, and ignore any duplicates after a
- * first successful mount.
- * Returns -1 on error, and  FS_MGR_MNTALL_* otherwise.
- */
-int fs_mgr_mount_all(struct fstab *fstab, int mount_mode)
-{
-    int i = 0;
+static bool call_vdc_ret(const std::vector<std::string>& args, int* ret) {
+    std::vector<char const*> argv;
+    argv.emplace_back("/system/bin/vdc");
+    for (auto& arg : args) {
+        argv.emplace_back(arg.c_str());
+    }
+    LOG(INFO) << "Calling: " << android::base::Join(argv, ' ');
+    int err = android_fork_execvp(argv.size(), const_cast<char**>(argv.data()), ret, false, true);
+    if (err != 0) {
+        LOG(ERROR) << "vdc call failed with error code: " << err;
+        return false;
+    }
+    LOG(DEBUG) << "vdc finished successfully";
+    *ret = WEXITSTATUS(*ret);
+    return true;
+}
+
+bool fs_mgr_update_logical_partition(FstabEntry* entry) {
+    // 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 (entry->blk_device[0] == '/') {
+        return true;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    std::string device_name;
+    if (!dm.GetDmDevicePathByName(entry->blk_device, &device_name)) {
+        return false;
+    }
+
+    entry->blk_device = device_name;
+    return true;
+}
+
+class CheckpointManager {
+  public:
+    CheckpointManager(int needs_checkpoint = -1) : needs_checkpoint_(needs_checkpoint) {}
+
+    bool Update(FstabEntry* entry, const std::string& block_device = std::string()) {
+        if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
+            return true;
+        }
+
+        if (entry->fs_mgr_flags.checkpoint_blk) {
+            call_vdc({"checkpoint", "restoreCheckpoint", entry->blk_device});
+        }
+
+        if (needs_checkpoint_ == UNKNOWN &&
+            !call_vdc_ret({"checkpoint", "needsCheckpoint"}, &needs_checkpoint_)) {
+            LERROR << "Failed to find if checkpointing is needed. Assuming no.";
+            needs_checkpoint_ = NO;
+        }
+
+        if (needs_checkpoint_ != YES) {
+            return true;
+        }
+
+        if (!UpdateCheckpointPartition(entry, block_device)) {
+            LERROR << "Could not set up checkpoint partition, skipping!";
+            return false;
+        }
+
+        return true;
+    }
+
+    bool Revert(FstabEntry* entry) {
+        if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
+            return true;
+        }
+
+        if (device_map_.find(entry->blk_device) == device_map_.end()) {
+            return true;
+        }
+
+        std::string bow_device = entry->blk_device;
+        entry->blk_device = device_map_[bow_device];
+        device_map_.erase(bow_device);
+
+        DeviceMapper& dm = DeviceMapper::Instance();
+        if (!dm.DeleteDevice("bow")) {
+            PERROR << "Failed to remove bow device";
+        }
+
+        return true;
+    }
+
+  private:
+    bool UpdateCheckpointPartition(FstabEntry* entry, const std::string& block_device) {
+        if (entry->fs_mgr_flags.checkpoint_fs) {
+            if (is_f2fs(entry->fs_type)) {
+                entry->fs_options += ",checkpoint=disable";
+            } else {
+                LERROR << entry->fs_type << " does not implement checkpoints.";
+            }
+        } else if (entry->fs_mgr_flags.checkpoint_blk) {
+            auto actual_block_device = block_device.empty() ? entry->blk_device : block_device;
+            if (fs_mgr_find_bow_device(actual_block_device).empty()) {
+                unique_fd fd(
+                        TEMP_FAILURE_RETRY(open(entry->blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
+                if (fd < 0) {
+                    PERROR << "Cannot open device " << entry->blk_device;
+                    return false;
+                }
+
+                uint64_t size = get_block_device_size(fd) / 512;
+                if (!size) {
+                    PERROR << "Cannot get device size";
+                    return false;
+                }
+
+                android::dm::DmTable table;
+                if (!table.AddTarget(std::make_unique<android::dm::DmTargetBow>(
+                            0, size, entry->blk_device))) {
+                    LERROR << "Failed to add bow target";
+                    return false;
+                }
+
+                DeviceMapper& dm = DeviceMapper::Instance();
+                if (!dm.CreateDevice("bow", table)) {
+                    PERROR << "Failed to create bow device";
+                    return false;
+                }
+
+                std::string name;
+                if (!dm.GetDmDevicePathByName("bow", &name)) {
+                    PERROR << "Failed to get bow device name";
+                    return false;
+                }
+
+                device_map_[name] = entry->blk_device;
+                entry->blk_device = name;
+            }
+        }
+        return true;
+    }
+
+    enum { UNKNOWN = -1, NO = 0, YES = 1 };
+    int needs_checkpoint_;
+    std::map<std::string, std::string> device_map_;
+};
+
+std::string fs_mgr_find_bow_device(const std::string& block_device) {
+    if (block_device.substr(0, 5) != "/dev/") {
+        LOG(ERROR) << "Expected block device, got " << block_device;
+        return std::string();
+    }
+
+    std::string sys_dir = std::string("/sys/") + block_device.substr(5);
+
+    for (;;) {
+        std::string name;
+        if (!android::base::ReadFileToString(sys_dir + "/dm/name", &name)) {
+            PLOG(ERROR) << block_device << " is not dm device";
+            return std::string();
+        }
+
+        if (name == "bow\n") return sys_dir;
+
+        std::string slaves = sys_dir + "/slaves";
+        std::unique_ptr<DIR, decltype(&closedir)> directory(opendir(slaves.c_str()), closedir);
+        if (!directory) {
+            PLOG(ERROR) << "Can't open slave directory " << slaves;
+            return std::string();
+        }
+
+        int count = 0;
+        for (dirent* entry = readdir(directory.get()); entry; entry = readdir(directory.get())) {
+            if (entry->d_type != DT_LNK) continue;
+
+            if (count == 1) {
+                LOG(ERROR) << "Too many slaves in " << slaves;
+                return std::string();
+            }
+
+            ++count;
+            sys_dir = std::string("/sys/block/") + entry->d_name;
+        }
+
+        if (count != 1) {
+            LOG(ERROR) << "No slave in " << slaves;
+            return std::string();
+        }
+    }
+}
+
+static bool IsMountPointMounted(const std::string& mount_point) {
+    // Check if this is already mounted.
+    Fstab fstab;
+    if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+        return false;
+    }
+    return GetEntryForMountPoint(&fstab, mount_point) != nullptr;
+}
+
+// When multiple fstab records share the same mount_point, it will try to mount each
+// one in turn, and ignore any duplicates after a first successful mount.
+// Returns -1 on error, and  FS_MGR_MNTALL_* otherwise.
+int fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
     int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
     int error_count = 0;
-    int mret = -1;
-    int mount_errno = 0;
-    int attempted_idx = -1;
-    FsManagerAvbUniquePtr avb_handle(nullptr);
+    CheckpointManager checkpoint_manager;
+    AvbUniquePtr avb_handle(nullptr);
 
-    if (!fstab) {
+    if (fstab->empty()) {
         return FS_MGR_MNTALL_FAIL;
     }
 
-    for (i = 0; i < fstab->num_entries; i++) {
-        /* Don't mount entries that are managed by vold or not for the mount mode*/
-        if ((fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) ||
-             ((mount_mode == MOUNT_MODE_LATE) && !fs_mgr_is_latemount(&fstab->recs[i])) ||
-             ((mount_mode == MOUNT_MODE_EARLY) && fs_mgr_is_latemount(&fstab->recs[i]))) {
+    for (size_t i = 0; i < fstab->size(); i++) {
+        auto& current_entry = (*fstab)[i];
+
+        // If a filesystem should have been mounted in the first stage, we
+        // ignore it here. With one exception, if the filesystem is
+        // formattable, then it can only be formatted in the second stage,
+        // so we allow it to mount here.
+        if (current_entry.fs_mgr_flags.first_stage_mount &&
+            (!current_entry.fs_mgr_flags.formattable ||
+             IsMountPointMounted(current_entry.mount_point))) {
             continue;
         }
 
-        /* Skip swap and raw partition entries such as boot, recovery, etc */
-        if (!strcmp(fstab->recs[i].fs_type, "swap") ||
-            !strcmp(fstab->recs[i].fs_type, "emmc") ||
-            !strcmp(fstab->recs[i].fs_type, "mtd")) {
+        // Don't mount entries that are managed by vold or not for the mount mode.
+        if (current_entry.fs_mgr_flags.vold_managed || current_entry.fs_mgr_flags.recovery_only ||
+            ((mount_mode == MOUNT_MODE_LATE) && !current_entry.fs_mgr_flags.late_mount) ||
+            ((mount_mode == MOUNT_MODE_EARLY) && current_entry.fs_mgr_flags.late_mount)) {
             continue;
         }
 
-        /* Skip mounting the root partition, as it will already have been mounted */
-        if (!strcmp(fstab->recs[i].mount_point, "/")) {
-            if ((fstab->recs[i].fs_mgr_flags & MS_RDONLY) != 0) {
-                fs_mgr_set_blk_ro(fstab->recs[i].blk_device);
+        // Skip swap and raw partition entries such as boot, recovery, etc.
+        if (current_entry.fs_type == "swap" || current_entry.fs_type == "emmc" ||
+            current_entry.fs_type == "mtd") {
+            continue;
+        }
+
+        // Skip mounting the root partition, as it will already have been mounted.
+        if (current_entry.mount_point == "/" || current_entry.mount_point == "/system") {
+            if ((current_entry.flags & MS_RDONLY) != 0) {
+                fs_mgr_set_blk_ro(current_entry.blk_device);
             }
             continue;
         }
 
-        /* Translate LABEL= file system labels into block devices */
-        if (is_extfs(fstab->recs[i].fs_type)) {
-            int tret = translate_ext_labels(&fstab->recs[i]);
-            if (tret < 0) {
+        // Translate LABEL= file system labels into block devices.
+        if (is_extfs(current_entry.fs_type)) {
+            if (!TranslateExtLabels(&current_entry)) {
                 LERROR << "Could not translate label to block device";
                 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";
+        if (current_entry.fs_mgr_flags.logical) {
+            if (!fs_mgr_update_logical_partition(&current_entry)) {
+                LERROR << "Could not set up logical partition, skipping!";
+                continue;
+            }
+        }
+
+        if (!checkpoint_manager.Update(&current_entry)) {
             continue;
         }
 
-        if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
+        if (current_entry.fs_mgr_flags.wait && !WaitForFile(current_entry.blk_device, 20s)) {
+            LERROR << "Skipping '" << current_entry.blk_device << "' during mount_all";
+            continue;
+        }
+
+        if (current_entry.fs_mgr_flags.avb) {
             if (!avb_handle) {
-                avb_handle = FsManagerAvbHandle::Open(*fstab);
+                avb_handle = AvbHandle::Open();
                 if (!avb_handle) {
-                    LERROR << "Failed to open FsManagerAvbHandle";
+                    LERROR << "Failed to open AvbHandle";
                     return FS_MGR_MNTALL_FAIL;
                 }
             }
-            if (avb_handle->SetUpAvbHashtree(&fstab->recs[i], true /* wait_for_verity_dev */) ==
-                SetUpAvbHashtreeResult::kFail) {
-                LERROR << "Failed to set up AVB on partition: "
-                       << fstab->recs[i].mount_point << ", skipping!";
-                /* Skips mounting the device. */
+            if (avb_handle->SetUpAvbHashtree(&current_entry, true /* wait_for_verity_dev */) ==
+                AvbHashtreeResult::kFail) {
+                LERROR << "Failed to set up AVB on partition: " << current_entry.mount_point
+                       << ", skipping!";
+                // Skips mounting the device.
                 continue;
             }
-        } 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 ||
-                     rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
+        } else if (!current_entry.avb_keys.empty()) {
+            if (AvbHandle::SetUpStandaloneAvbHashtree(&current_entry) == AvbHashtreeResult::kFail) {
+                LERROR << "Failed to set up AVB on standalone partition: "
+                       << current_entry.mount_point << ", skipping!";
+                // Skips mounting the device.
+                continue;
+            }
+        } else if ((current_entry.fs_mgr_flags.verify)) {
+            int rc = fs_mgr_setup_verity(&current_entry, true);
+            if (rc == FS_MGR_SETUP_VERITY_DISABLED || rc == FS_MGR_SETUP_VERITY_SKIPPED) {
                 LINFO << "Verity disabled";
             } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
                 LERROR << "Could not set up verified partition, skipping!";
@@ -880,17 +1169,19 @@
 
         int last_idx_inspected;
         int top_idx = i;
+        int attempted_idx = -1;
 
-        mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx);
+        bool mret = mount_with_alternatives(*fstab, i, &last_idx_inspected, &attempted_idx);
+        auto& attempted_entry = (*fstab)[attempted_idx];
         i = last_idx_inspected;
-        mount_errno = errno;
+        int mount_errno = errno;
 
-        /* Deal with encryptability. */
-        if (!mret) {
-            int status = handle_encryptable(&fstab->recs[attempted_idx]);
+        // Handle success and deal with encryptability.
+        if (mret) {
+            int status = handle_encryptable(attempted_entry);
 
             if (status == FS_MGR_MNTALL_FAIL) {
-                /* Fatal error - no point continuing */
+                // Fatal error - no point continuing.
                 return status;
             }
 
@@ -901,48 +1192,46 @@
                 }
                 encryptable = status;
                 if (status == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
-                    if (!call_vdc(
-                            {"cryptfs", "encryptFstab", fstab->recs[attempted_idx].mount_point})) {
+                    if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.blk_device,
+                                   attempted_entry.mount_point})) {
                         LERROR << "Encryption failed";
                         return FS_MGR_MNTALL_FAIL;
                     }
                 }
             }
 
-            /* Success!  Go get the next one */
+            // Success!  Go get the next one.
             continue;
         }
 
-        bool wiped = partition_wiped(fstab->recs[top_idx].blk_device);
+        // Mounting failed, understand why and retry.
+        bool wiped = partition_wiped(current_entry.blk_device.c_str());
         bool crypt_footer = false;
-        if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
-            fs_mgr_is_formattable(&fstab->recs[top_idx]) && wiped) {
-            /* top_idx and attempted_idx point at the same partition, but sometimes
-             * at two different lines in the fstab.  Use the top one for formatting
-             * as that is the preferred one.
-             */
-            LERROR << __FUNCTION__ << "(): " << fstab->recs[top_idx].blk_device
-                   << " is wiped and " << fstab->recs[top_idx].mount_point
-                   << " " << fstab->recs[top_idx].fs_type
+        if (mount_errno != EBUSY && mount_errno != EACCES &&
+            current_entry.fs_mgr_flags.formattable && wiped) {
+            // current_entry and attempted_entry point at the same partition, but sometimes
+            // at two different lines in the fstab.  Use current_entry for formatting
+            // as that is the preferred one.
+            LERROR << __FUNCTION__ << "(): " << realpath(current_entry.blk_device)
+                   << " is wiped and " << current_entry.mount_point << " " << current_entry.fs_type
                    << " is formattable. Format it.";
-            if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
-                strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
-                int fd = open(fstab->recs[top_idx].key_loc, O_WRONLY);
+
+            checkpoint_manager.Revert(&current_entry);
+
+            if (current_entry.is_encryptable() && current_entry.key_loc != KEY_IN_FOOTER) {
+                unique_fd fd(TEMP_FAILURE_RETRY(
+                        open(current_entry.key_loc.c_str(), O_WRONLY | O_CLOEXEC)));
                 if (fd >= 0) {
-                    LINFO << __FUNCTION__ << "(): also wipe "
-                          << fstab->recs[top_idx].key_loc;
+                    LINFO << __FUNCTION__ << "(): also wipe " << current_entry.key_loc;
                     wipe_block_device(fd, get_file_size(fd));
-                    close(fd);
                 } else {
-                    PERROR << __FUNCTION__ << "(): "
-                           << fstab->recs[top_idx].key_loc << " wouldn't open";
+                    PERROR << __FUNCTION__ << "(): " << current_entry.key_loc << " wouldn't open";
                 }
-            } else if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
-                !strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
+            } else if (current_entry.is_encryptable() && current_entry.key_loc == KEY_IN_FOOTER) {
                 crypt_footer = true;
             }
-            if (fs_mgr_do_format(&fstab->recs[top_idx], crypt_footer) == 0) {
-                /* Let's replay the mount actions. */
+            if (fs_mgr_do_format(current_entry, crypt_footer) == 0) {
+                // Let's replay the mount actions.
                 i = top_idx - 1;
                 continue;
             } else {
@@ -953,35 +1242,30 @@
             }
         }
 
-        /* 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])) {
+        // mount(2) returned an error, handle the encryptable/formattable case.
+        if (mount_errno != EBUSY && mount_errno != EACCES && attempted_entry.is_encryptable()) {
             if (wiped) {
-                LERROR << __FUNCTION__ << "(): "
-                       << fstab->recs[attempted_idx].blk_device
-                       << " is wiped and "
-                       << fstab->recs[attempted_idx].mount_point << " "
-                       << fstab->recs[attempted_idx].fs_type
+                LERROR << __FUNCTION__ << "(): " << attempted_entry.blk_device << " is wiped and "
+                       << attempted_entry.mount_point << " " << attempted_entry.fs_type
                        << " is encryptable. Suggest recovery...";
                 encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY;
                 continue;
             } else {
-                /* Need to mount a tmpfs at this mountpoint for now, and set
-                 * properties that vold will query later for decrypting
-                 */
+                // Need to mount a tmpfs at this mountpoint for now, and set
+                // properties that vold will query later for decrypting
                 LERROR << __FUNCTION__ << "(): possibly an encryptable blkdev "
-                       << fstab->recs[attempted_idx].blk_device
-                       << " for mount " << fstab->recs[attempted_idx].mount_point
-                       << " type " << fstab->recs[attempted_idx].fs_type;
-                if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) {
+                       << attempted_entry.blk_device << " for mount " << attempted_entry.mount_point
+                       << " type " << attempted_entry.fs_type;
+                if (fs_mgr_do_tmpfs_mount(attempted_entry.mount_point.c_str()) < 0) {
                     ++error_count;
                     continue;
                 }
             }
             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})) {
+        } else if (mount_errno != EBUSY && mount_errno != EACCES &&
+                   should_use_metadata_encryption(attempted_entry)) {
+            if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.blk_device,
+                           attempted_entry.mount_point})) {
                 ++error_count;
             }
             encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
@@ -989,24 +1273,28 @@
         } else {
             // fs_options might be null so we cannot use PERROR << directly.
             // Use StringPrintf to output "(null)" instead.
-            if (fs_mgr_is_nofail(&fstab->recs[attempted_idx])) {
+            if (attempted_entry.fs_mgr_flags.no_fail) {
                 PERROR << android::base::StringPrintf(
-                    "Ignoring failure to mount an un-encryptable or wiped "
-                    "partition on %s at %s options: %s",
-                    fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
-                    fstab->recs[attempted_idx].fs_options);
+                        "Ignoring failure to mount an un-encryptable or wiped "
+                        "partition on %s at %s options: %s",
+                        attempted_entry.blk_device.c_str(), attempted_entry.mount_point.c_str(),
+                        attempted_entry.fs_options.c_str());
             } else {
                 PERROR << android::base::StringPrintf(
-                    "Failed to mount an un-encryptable or wiped partition "
-                    "on %s at %s options: %s",
-                    fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
-                    fstab->recs[attempted_idx].fs_options);
+                        "Failed to mount an un-encryptable or wiped partition "
+                        "on %s at %s options: %s",
+                        attempted_entry.blk_device.c_str(), attempted_entry.mount_point.c_str(),
+                        attempted_entry.fs_options.c_str());
                 ++error_count;
             }
             continue;
         }
     }
 
+#if ALLOW_ADBD_DISABLE_VERITY == 1  // "userdebug" build
+    fs_mgr_overlayfs_mount_all(fstab);
+#endif
+
     if (error_count) {
         return FS_MGR_MNTALL_FAIL;
     } else {
@@ -1014,17 +1302,54 @@
     }
 }
 
-/* wrapper to __mount() and expects a fully prepared fstab_rec,
- * unlike fs_mgr_do_mount which does more things with avb / verity
- * etc.
- */
-int fs_mgr_do_mount_one(struct fstab_rec *rec)
-{
-    if (!rec) {
-        return FS_MGR_DOMNT_FAILED;
-    }
+int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab) {
+    AvbUniquePtr avb_handle(nullptr);
+    int ret = FsMgrUmountStatus::SUCCESS;
+    for (auto& current_entry : *fstab) {
+        if (!IsMountPointMounted(current_entry.mount_point)) {
+            continue;
+        }
 
-    int ret = __mount(rec->blk_device, rec->mount_point, rec);
+        if (umount(current_entry.mount_point.c_str()) == -1) {
+            PERROR << "Failed to umount " << current_entry.mount_point;
+            ret |= FsMgrUmountStatus::ERROR_UMOUNT;
+            continue;
+        }
+
+        if (current_entry.fs_mgr_flags.logical) {
+            if (!fs_mgr_update_logical_partition(&current_entry)) {
+                LERROR << "Could not get logical partition blk_device, skipping!";
+                ret |= FsMgrUmountStatus::ERROR_DEVICE_MAPPER;
+                continue;
+            }
+        }
+
+        if (current_entry.fs_mgr_flags.avb || !current_entry.avb_keys.empty()) {
+            if (!AvbHandle::TearDownAvbHashtree(&current_entry, true /* wait */)) {
+                LERROR << "Failed to tear down AVB on mount point: " << current_entry.mount_point;
+                ret |= FsMgrUmountStatus::ERROR_VERITY;
+                continue;
+            }
+        } else if ((current_entry.fs_mgr_flags.verify)) {
+            if (!fs_mgr_teardown_verity(&current_entry, true /* wait */)) {
+                LERROR << "Failed to tear down verified partition on mount point: "
+                       << current_entry.mount_point;
+                ret |= FsMgrUmountStatus::ERROR_VERITY;
+                continue;
+            }
+        }
+    }
+    return ret;
+}
+
+// wrapper to __mount() and expects a fully prepared fstab_rec,
+// unlike fs_mgr_do_mount which does more things with avb / verity etc.
+int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& mount_point) {
+    // Run fsck if needed
+    prepare_fs_for_mount(entry.blk_device, entry);
+
+    int ret =
+            __mount(entry.blk_device, mount_point.empty() ? entry.mount_point : mount_point, entry);
     if (ret) {
       ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
     }
@@ -1032,67 +1357,82 @@
     return ret;
 }
 
-/* If tmp_mount_point is non-null, mount the filesystem there.  This is for the
- * tmp mount we do to check the user password
- * If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
- * in turn, and stop on 1st success, or no more match.
- */
-int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
-                    char *tmp_mount_point)
-{
-    int i = 0;
+// If tmp_mount_point is non-null, mount the filesystem there.  This is for the
+// tmp mount we do to check the user password
+// If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
+// in turn, and stop on 1st success, or no more match.
+static int fs_mgr_do_mount_helper(Fstab* fstab, const std::string& n_name,
+                                  const std::string& n_blk_device, const char* tmp_mount_point,
+                                  int needs_checkpoint) {
     int mount_errors = 0;
     int first_mount_errno = 0;
-    char* mount_point;
-    FsManagerAvbUniquePtr avb_handle(nullptr);
+    std::string mount_point;
+    CheckpointManager checkpoint_manager(needs_checkpoint);
+    AvbUniquePtr avb_handle(nullptr);
 
     if (!fstab) {
         return FS_MGR_DOMNT_FAILED;
     }
 
-    for (i = 0; i < fstab->num_entries; i++) {
-        if (!fs_match(fstab->recs[i].mount_point, n_name)) {
+    for (auto& fstab_entry : *fstab) {
+        if (!fs_match(fstab_entry.mount_point, n_name)) {
             continue;
         }
 
-        /* We found our match */
-        /* If this swap or a raw partition, report an error */
-        if (!strcmp(fstab->recs[i].fs_type, "swap") ||
-            !strcmp(fstab->recs[i].fs_type, "emmc") ||
-            !strcmp(fstab->recs[i].fs_type, "mtd")) {
-            LERROR << "Cannot mount filesystem of type "
-                   << fstab->recs[i].fs_type << " on " << n_blk_device;
+        // We found our match.
+        // If this swap or a raw partition, report an error.
+        if (fstab_entry.fs_type == "swap" || fstab_entry.fs_type == "emmc" ||
+            fstab_entry.fs_type == "mtd") {
+            LERROR << "Cannot mount filesystem of type " << fstab_entry.fs_type << " on "
+                   << n_blk_device;
             return FS_MGR_DOMNT_FAILED;
         }
 
-        /* First check the filesystem if requested */
-        if (fstab->recs[i].fs_mgr_flags & MF_WAIT && !fs_mgr_wait_for_file(n_blk_device, 20s)) {
+        if (fstab_entry.fs_mgr_flags.logical) {
+            if (!fs_mgr_update_logical_partition(&fstab_entry)) {
+                LERROR << "Could not set up logical partition, skipping!";
+                continue;
+            }
+        }
+
+        if (!checkpoint_manager.Update(&fstab_entry, n_blk_device)) {
+            LERROR << "Could not set up checkpoint partition, skipping!";
+            continue;
+        }
+
+        // First check the filesystem if requested.
+        if (fstab_entry.fs_mgr_flags.wait && !WaitForFile(n_blk_device, 20s)) {
             LERROR << "Skipping mounting '" << n_blk_device << "'";
             continue;
         }
 
-        int fs_stat = prepare_fs_for_mount(n_blk_device, &fstab->recs[i]);
+        int fs_stat = prepare_fs_for_mount(n_blk_device, fstab_entry);
 
-        if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
+        if (fstab_entry.fs_mgr_flags.avb) {
             if (!avb_handle) {
-                avb_handle = FsManagerAvbHandle::Open(*fstab);
+                avb_handle = AvbHandle::Open();
                 if (!avb_handle) {
-                    LERROR << "Failed to open FsManagerAvbHandle";
+                    LERROR << "Failed to open AvbHandle";
                     return FS_MGR_DOMNT_FAILED;
                 }
             }
-            if (avb_handle->SetUpAvbHashtree(&fstab->recs[i], true /* wait_for_verity_dev */) ==
-                SetUpAvbHashtreeResult::kFail) {
-                LERROR << "Failed to set up AVB on partition: "
-                       << fstab->recs[i].mount_point << ", skipping!";
-                /* Skips mounting the device. */
+            if (avb_handle->SetUpAvbHashtree(&fstab_entry, true /* wait_for_verity_dev */) ==
+                AvbHashtreeResult::kFail) {
+                LERROR << "Failed to set up AVB on partition: " << fstab_entry.mount_point
+                       << ", skipping!";
+                // Skips mounting the device.
                 continue;
             }
-        } 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 ||
-                     rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
+        } else if (!fstab_entry.avb_keys.empty()) {
+            if (AvbHandle::SetUpStandaloneAvbHashtree(&fstab_entry) == AvbHashtreeResult::kFail) {
+                LERROR << "Failed to set up AVB on standalone partition: "
+                       << fstab_entry.mount_point << ", skipping!";
+                // Skips mounting the device.
+                continue;
+            }
+        } else if (fstab_entry.fs_mgr_flags.verify) {
+            int rc = fs_mgr_setup_verity(&fstab_entry, true);
+            if (rc == FS_MGR_SETUP_VERITY_DISABLED || rc == FS_MGR_SETUP_VERITY_SKIPPED) {
                 LINFO << "Verity disabled";
             } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
                 LERROR << "Could not set up verified partition, skipping!";
@@ -1100,15 +1440,15 @@
             }
         }
 
-        /* Now mount it where requested */
+        // Now mount it where requested */
         if (tmp_mount_point) {
             mount_point = tmp_mount_point;
         } else {
-            mount_point = fstab->recs[i].mount_point;
+            mount_point = fstab_entry.mount_point;
         }
         int retry_count = 2;
         while (retry_count-- > 0) {
-            if (!__mount(n_blk_device, mount_point, &fstab->recs[i])) {
+            if (!__mount(n_blk_device, mount_point, fstab_entry)) {
                 fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
                 return FS_MGR_DOMNT_SUCCESS;
             } else {
@@ -1117,10 +1457,10 @@
                 mount_errors++;
                 fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
                 // try again after fsck
-                check_fs(n_blk_device, fstab->recs[i].fs_type, fstab->recs[i].mount_point, &fs_stat);
+                check_fs(n_blk_device, fstab_entry.fs_type, fstab_entry.mount_point, &fs_stat);
             }
         }
-        log_fs_stat(fstab->recs[i].blk_device, fs_stat);
+        log_fs_stat(fstab_entry.blk_device, fs_stat);
     }
 
     // Reach here means the mount attempt fails.
@@ -1128,12 +1468,21 @@
         PERROR << "Cannot mount filesystem on " << n_blk_device << " at " << mount_point;
         if (first_mount_errno == EBUSY) return FS_MGR_DOMNT_BUSY;
     } else {
-        /* We didn't find a match, say so and return an error */
+        // We didn't find a match, say so and return an error.
         LERROR << "Cannot find mount point " << n_name << " in fstab";
     }
     return FS_MGR_DOMNT_FAILED;
 }
 
+int fs_mgr_do_mount(Fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point) {
+    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, -1);
+}
+
+int fs_mgr_do_mount(Fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
+                    bool needs_checkpoint) {
+    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, needs_checkpoint);
+}
+
 /*
  * mount a tmpfs filesystem at the given point.
  * return 0 on success, non-zero on failure.
@@ -1142,8 +1491,8 @@
 {
     int ret;
 
-    ret = mount("tmpfs", n_name, "tmpfs",
-                MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS);
+    ret = mount("tmpfs", n_name, "tmpfs", MS_NOATIME | MS_NOSUID | MS_NODEV | MS_NOEXEC,
+                CRYPTO_TMPFS_OPTIONS);
     if (ret < 0) {
         LERROR << "Cannot mount tmpfs filesystem at " << n_name;
         return -1;
@@ -1153,258 +1502,216 @@
     return 0;
 }
 
-int fs_mgr_unmount_all(struct fstab *fstab)
-{
-    int i = 0;
-    int ret = 0;
-
-    if (!fstab) {
-        return -1;
+static bool InstallZramDevice(const std::string& device) {
+    if (!android::base::WriteStringToFile(device, ZRAM_BACK_DEV)) {
+        PERROR << "Cannot write " << device << " in: " << ZRAM_BACK_DEV;
+        return false;
     }
-
-    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;
+    LINFO << "Success to set " << device << " to " << ZRAM_BACK_DEV;
+    return true;
 }
 
-/* This must be called after mount_all, because the mkswap command needs to be
- * available.
- */
-int fs_mgr_swapon_all(struct fstab *fstab)
-{
-    int i = 0;
-    int flags = 0;
-    int err = 0;
-    int ret = 0;
-    int status;
-    const char *mkswap_argv[2] = {
-        MKSWAP_BIN,
-        nullptr
-    };
+static bool PrepareZramDevice(const std::string& loop, off64_t size, const std::string& bdev) {
+    if (loop.empty() && bdev.empty()) return true;
 
-    if (!fstab) {
-        return -1;
+    if (bdev.length()) {
+        return InstallZramDevice(bdev);
     }
 
-    for (i = 0; i < fstab->num_entries; i++) {
-        /* Skip non-swap entries */
-        if (strcmp(fstab->recs[i].fs_type, "swap")) {
+    // Get free loopback
+    unique_fd loop_fd(TEMP_FAILURE_RETRY(open("/dev/loop-control", O_RDWR | O_CLOEXEC)));
+    if (loop_fd.get() == -1) {
+        PERROR << "Cannot open loop-control";
+        return false;
+    }
+
+    int num = ioctl(loop_fd.get(), LOOP_CTL_GET_FREE);
+    if (num == -1) {
+        PERROR << "Cannot get free loop slot";
+        return false;
+    }
+
+    // Prepare target path
+    unique_fd target_fd(TEMP_FAILURE_RETRY(open(loop.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600)));
+    if (target_fd.get() == -1) {
+        PERROR << "Cannot open target path: " << loop;
+        return false;
+    }
+    if (fallocate(target_fd.get(), 0, 0, size) < 0) {
+        PERROR << "Cannot truncate target path: " << loop;
+        return false;
+    }
+
+    // Connect loopback (device_fd) to target path (target_fd)
+    std::string device = android::base::StringPrintf("/dev/block/loop%d", num);
+    unique_fd device_fd(TEMP_FAILURE_RETRY(open(device.c_str(), O_RDWR | O_CLOEXEC)));
+    if (device_fd.get() == -1) {
+        PERROR << "Cannot open /dev/block/loop" << num;
+        return false;
+    }
+
+    if (ioctl(device_fd.get(), LOOP_SET_FD, target_fd.get())) {
+        PERROR << "Cannot set loopback to target path";
+        return false;
+    }
+
+    // set block size & direct IO
+    if (ioctl(device_fd.get(), LOOP_SET_BLOCK_SIZE, 4096)) {
+        PWARNING << "Cannot set 4KB blocksize to /dev/block/loop" << num;
+    }
+    if (ioctl(device_fd.get(), LOOP_SET_DIRECT_IO, 1)) {
+        PWARNING << "Cannot set direct_io to /dev/block/loop" << num;
+    }
+
+    return InstallZramDevice(device);
+}
+
+bool fs_mgr_swapon_all(const Fstab& fstab) {
+    bool ret = true;
+    for (const auto& entry : fstab) {
+        // Skip non-swap entries.
+        if (entry.fs_type != "swap") {
             continue;
         }
 
-        if (fstab->recs[i].zram_size > 0) {
-            /* A zram_size was specified, so we need to configure the
-             * device.  There is no point in having multiple zram devices
-             * on a system (all the memory comes from the same pool) so
-             * we can assume the device number is 0.
-             */
-            FILE *zram_fp;
-            FILE *zram_mcs_fp;
+        if (!PrepareZramDevice(entry.zram_loopback_path, entry.zram_loopback_size, entry.zram_backing_dev_path)) {
+            LERROR << "Skipping losetup for '" << entry.blk_device << "'";
+        }
 
-            if (fstab->recs[i].max_comp_streams >= 0) {
-               zram_mcs_fp = fopen(ZRAM_CONF_MCS, "r+");
-              if (zram_mcs_fp == NULL) {
-                LERROR << "Unable to open zram conf comp device "
-                       << ZRAM_CONF_MCS;
-                ret = -1;
-                continue;
-              }
-              fprintf(zram_mcs_fp, "%d\n", fstab->recs[i].max_comp_streams);
-              fclose(zram_mcs_fp);
+        if (entry.zram_size > 0) {
+            // A zram_size was specified, so we need to configure the
+            // device.  There is no point in having multiple zram devices
+            // on a system (all the memory comes from the same pool) so
+            // we can assume the device number is 0.
+            if (entry.max_comp_streams >= 0) {
+                auto zram_mcs_fp = std::unique_ptr<FILE, decltype(&fclose)>{
+                        fopen(ZRAM_CONF_MCS, "re"), fclose};
+                if (zram_mcs_fp == nullptr) {
+                    LERROR << "Unable to open zram conf comp device " << ZRAM_CONF_MCS;
+                    ret = false;
+                    continue;
+                }
+                fprintf(zram_mcs_fp.get(), "%d\n", entry.max_comp_streams);
             }
 
-            zram_fp = fopen(ZRAM_CONF_DEV, "r+");
-            if (zram_fp == NULL) {
+            auto zram_fp =
+                    std::unique_ptr<FILE, decltype(&fclose)>{fopen(ZRAM_CONF_DEV, "re+"), fclose};
+            if (zram_fp == nullptr) {
                 LERROR << "Unable to open zram conf device " << ZRAM_CONF_DEV;
-                ret = -1;
+                ret = false;
                 continue;
             }
-            fprintf(zram_fp, "%u\n", fstab->recs[i].zram_size);
-            fclose(zram_fp);
+            fprintf(zram_fp.get(), "%" PRId64 "\n", entry.zram_size);
         }
 
-        if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
-            !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
-            LERROR << "Skipping mkswap for '" << fstab->recs[i].blk_device << "'";
-            ret = -1;
+        if (entry.fs_mgr_flags.wait && !WaitForFile(entry.blk_device, 20s)) {
+            LERROR << "Skipping mkswap for '" << entry.blk_device << "'";
+            ret = false;
             continue;
         }
 
-        /* Initialize the swap area */
-        mkswap_argv[1] = fstab->recs[i].blk_device;
-        err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv),
-                                      const_cast<char **>(mkswap_argv),
-                                      &status, true, LOG_KLOG, false, NULL,
-                                      NULL, 0);
+        // Initialize the swap area.
+        const char* mkswap_argv[2] = {
+                MKSWAP_BIN,
+                entry.blk_device.c_str(),
+        };
+        int err = 0;
+        int status;
+        err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv), const_cast<char**>(mkswap_argv),
+                                      &status, true, LOG_KLOG, false, nullptr, nullptr, 0);
         if (err) {
-            LERROR << "mkswap failed for " << fstab->recs[i].blk_device;
-            ret = -1;
+            LERROR << "mkswap failed for " << entry.blk_device;
+            ret = false;
             continue;
         }
 
         /* If -1, then no priority was specified in fstab, so don't set
          * SWAP_FLAG_PREFER or encode the priority */
-        if (fstab->recs[i].swap_prio >= 0) {
-            flags = (fstab->recs[i].swap_prio << SWAP_FLAG_PRIO_SHIFT) &
-                    SWAP_FLAG_PRIO_MASK;
+        int flags = 0;
+        if (entry.swap_prio >= 0) {
+            flags = (entry.swap_prio << SWAP_FLAG_PRIO_SHIFT) & SWAP_FLAG_PRIO_MASK;
             flags |= SWAP_FLAG_PREFER;
         } else {
             flags = 0;
         }
-        err = swapon(fstab->recs[i].blk_device, flags);
+        err = swapon(entry.blk_device.c_str(), flags);
         if (err) {
-            LERROR << "swapon failed for " << fstab->recs[i].blk_device;
-            ret = -1;
+            LERROR << "swapon failed for " << entry.blk_device;
+            ret = false;
         }
     }
 
     return ret;
 }
 
-struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab) {
-    int i;
-
-    if (!fstab) {
-        return NULL;
+bool fs_mgr_is_verity_enabled(const FstabEntry& entry) {
+    if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
+        return false;
     }
 
-    /* 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) &&
-            (fstab->recs[i].fs_mgr_flags &
-             (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE | MF_FILEENCRYPTION))) {
-            return &fstab->recs[i];
+    DeviceMapper& dm = DeviceMapper::Instance();
+
+    std::string mount_point = GetVerityDeviceName(entry);
+    if (dm.GetState(mount_point) == DmDeviceState::INVALID) {
+        return false;
+    }
+
+    const char* status;
+    std::vector<DeviceMapper::TargetInfo> table;
+    if (!dm.GetTableStatus(mount_point, &table) || table.empty() || table[0].data.empty()) {
+        if (!entry.fs_mgr_flags.verify_at_boot) {
+            return false;
         }
+        status = "V";
+    } else {
+        status = table[0].data.c_str();
     }
-    return NULL;
+
+    if (*status == 'C' || *status == 'V') {
+        return true;
+    }
+
+    return false;
 }
 
-/*
- * 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';
+bool fs_mgr_verity_is_check_at_most_once(const android::fs_mgr::FstabEntry& entry) {
+    if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
+        return false;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    std::string device = GetVerityDeviceName(entry);
+
+    std::vector<DeviceMapper::TargetInfo> table;
+    if (dm.GetState(device) == DmDeviceState::INVALID || !dm.GetTableInfo(device, &table)) {
+        return false;
+    }
+    for (const auto& target : table) {
+        if (strcmp(target.spec.target_type, "verity") == 0 &&
+            target.data.find("check_at_most_once") != std::string::npos) {
+            return true;
         }
     }
-    if (real_blk_device) {
-        if (rec) {
-            strlcpy(real_blk_device, rec->blk_device, size);
-        } else {
-            *real_blk_device = '\0';
-        }
-    }
+    return false;
 }
 
-bool fs_mgr_load_verity_state(int* mode) {
-    /* return the default mode, unless any of the verified partitions are in
-     * logging mode, in which case return that */
-    *mode = VERITY_MODE_DEFAULT;
-
-    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                               fs_mgr_free_fstab);
-    if (!fstab) {
-        LERROR << "Failed to read default fstab";
-        return false;
-    }
-
-    for (int i = 0; i < fstab->num_entries; i++) {
-        if (fs_mgr_is_avb(&fstab->recs[i])) {
-            *mode = VERITY_MODE_RESTART;  // avb only supports restart mode.
-            break;
-        } else if (!fs_mgr_is_verified(&fstab->recs[i])) {
-            continue;
+std::string fs_mgr_get_super_partition_name(int slot) {
+    // Devices upgrading to dynamic partitions are allowed to specify a super
+    // partition name. This includes cuttlefish, which is a non-A/B device.
+    std::string super_partition;
+    if (fs_mgr_get_boot_config_from_kernel_cmdline("super_partition", &super_partition)) {
+        if (fs_mgr_get_slot_suffix().empty()) {
+            return super_partition;
         }
-
-        int current;
-        if (load_verity_state(&fstab->recs[i], &current) < 0) {
-            continue;
+        std::string suffix;
+        if (slot == 0) {
+            suffix = "_a";
+        } else if (slot == 1) {
+            suffix = "_b";
+        } else if (slot == -1) {
+            suffix = fs_mgr_get_slot_suffix();
         }
-        if (current != VERITY_MODE_DEFAULT) {
-            *mode = current;
-            break;
-        }
+        return super_partition + suffix;
     }
-
-    return true;
-}
-
-bool fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback) {
-    if (!callback) {
-        return false;
-    }
-
-    int mode;
-    if (!fs_mgr_load_verity_state(&mode)) {
-        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) {
-        LERROR << "Failed to read default fstab";
-        return false;
-    }
-
-    alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
-    struct dm_ioctl* io = (struct dm_ioctl*)buffer;
-    bool system_root = android::base::GetProperty("ro.build.system_root_image", "") == "true";
-
-    for (int i = 0; i < fstab->num_entries; i++) {
-        if (!fs_mgr_is_verified(&fstab->recs[i]) && !fs_mgr_is_avb(&fstab->recs[i])) {
-            continue;
-        }
-
-        std::string mount_point;
-        if (system_root && !strcmp(fstab->recs[i].mount_point, "/")) {
-            // In AVB, the dm device name is vroot instead of system.
-            mount_point = fs_mgr_is_avb(&fstab->recs[i]) ? "vroot" : "system";
-        } else {
-            mount_point = basename(fstab->recs[i].mount_point);
-        }
-
-        fs_mgr_verity_ioctl_init(io, mount_point, 0);
-
-        const char* status;
-        if (ioctl(fd, DM_TABLE_STATUS, io)) {
-            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;
-            }
-        }
-
-        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].
-        if (mount_point == "vroot") mount_point = "system";
-        if (*status == 'C' || *status == 'V') {
-            callback(&fstab->recs[i], mount_point.c_str(), mode, *status);
-        }
-    }
-
-    return true;
+    return LP_METADATA_DEFAULT_PARTITION_NAME;
 }
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
deleted file mode 100644
index 7824cfa..0000000
--- a/fs_mgr/fs_mgr_avb.cpp
+++ /dev/null
@@ -1,610 +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 "fs_mgr_avb.h"
-
-#include <fcntl.h>
-#include <libgen.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <android-base/file.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 <libavb/libavb.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) {
-    FS_MGR_CHECK(value != nullptr);
-
-    switch (c) {
-        case '0' ... '9':
-            *value = c - '0';
-            break;
-        case 'a' ... 'f':
-            *value = c - 'a' + 10;
-            break;
-        case 'A' ... 'F':
-            *value = c - 'A' + 10;
-            break;
-        default:
-            return false;
-    }
-
-    return true;
-}
-
-static bool hex_to_bytes(uint8_t* bytes, size_t bytes_len, const std::string& hex) {
-    FS_MGR_CHECK(bytes != nullptr);
-
-    if (hex.size() % 2 != 0) {
-        return false;
-    }
-    if (hex.size() / 2 > bytes_len) {
-        return false;
-    }
-    for (size_t i = 0, j = 0, n = hex.size(); i < n; i += 2, ++j) {
-        uint8_t high;
-        if (!nibble_value(hex[i], &high)) {
-            return false;
-        }
-        uint8_t low;
-        if (!nibble_value(hex[i + 1], &low)) {
-            return false;
-        }
-        bytes[j] = (high << 4) | low;
-    }
-    return true;
-}
-
-static std::string bytes_to_hex(const uint8_t* bytes, size_t bytes_len) {
-    FS_MGR_CHECK(bytes != nullptr);
-
-    static const char* hex_digits = "0123456789abcdef";
-    std::string hex;
-
-    for (size_t i = 0; i < bytes_len; i++) {
-        hex.push_back(hex_digits[(bytes[i] & 0xF0) >> 4]);
-        hex.push_back(hex_digits[bytes[i] & 0x0F]);
-    }
-    return hex;
-}
-
-template <typename Hasher>
-static std::pair<size_t, bool> verify_vbmeta_digest(const AvbSlotVerifyData& verify_data,
-                                                    const uint8_t* expected_digest) {
-    size_t total_size = 0;
-    Hasher hasher;
-    for (size_t n = 0; n < verify_data.num_vbmeta_images; n++) {
-        hasher.update(verify_data.vbmeta_images[n].vbmeta_data,
-                      verify_data.vbmeta_images[n].vbmeta_size);
-        total_size += verify_data.vbmeta_images[n].vbmeta_size;
-    }
-
-    bool matched = (memcmp(hasher.finalize(), expected_digest, Hasher::DIGEST_SIZE) == 0);
-
-    return std::make_pair(total_size, matched);
-}
-
-// Reads the following values from kernel cmdline and provides the
-// VerifyVbmetaImages() to verify AvbSlotVerifyData.
-//   - androidboot.vbmeta.hash_alg
-//   - androidboot.vbmeta.size
-//   - androidboot.vbmeta.digest
-class FsManagerAvbVerifier {
-  public:
-    // The factory method to return a unique_ptr<FsManagerAvbVerifier>
-    static std::unique_ptr<FsManagerAvbVerifier> Create();
-    bool VerifyVbmetaImages(const AvbSlotVerifyData& verify_data);
-
-  protected:
-    FsManagerAvbVerifier() = default;
-
-  private:
-    enum HashAlgorithm {
-        kInvalid = 0,
-        kSHA256 = 1,
-        kSHA512 = 2,
-    };
-
-    HashAlgorithm hash_alg_;
-    uint8_t digest_[SHA512_DIGEST_LENGTH];
-    size_t vbmeta_size_;
-};
-
-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;
-        }
-    }
-
-    // Reads hash algorithm.
-    size_t expected_digest_size = 0;
-    if (hash_alg == "sha256") {
-        expected_digest_size = SHA256_DIGEST_LENGTH * 2;
-        avb_verifier->hash_alg_ = kSHA256;
-    } else if (hash_alg == "sha512") {
-        expected_digest_size = SHA512_DIGEST_LENGTH * 2;
-        avb_verifier->hash_alg_ = kSHA512;
-    } else {
-        LERROR << "Unknown hash algorithm: " << hash_alg.c_str();
-        return nullptr;
-    }
-
-    // Reads digest.
-    if (digest.size() != expected_digest_size) {
-        LERROR << "Unexpected digest size: " << digest.size()
-               << " (expected: " << expected_digest_size << ")";
-        return nullptr;
-    }
-
-    if (!hex_to_bytes(avb_verifier->digest_, sizeof(avb_verifier->digest_), digest)) {
-        LERROR << "Hash digest contains non-hexidecimal character: " << digest.c_str();
-        return nullptr;
-    }
-
-    return avb_verifier;
-}
-
-bool FsManagerAvbVerifier::VerifyVbmetaImages(const AvbSlotVerifyData& verify_data) {
-    if (verify_data.num_vbmeta_images == 0) {
-        LERROR << "No vbmeta images";
-        return false;
-    }
-
-    size_t total_size = 0;
-    bool digest_matched = false;
-
-    if (hash_alg_ == kSHA256) {
-        std::tie(total_size, digest_matched) =
-            verify_vbmeta_digest<SHA256Hasher>(verify_data, digest_);
-    } else if (hash_alg_ == kSHA512) {
-        std::tie(total_size, digest_matched) =
-            verify_vbmeta_digest<SHA512Hasher>(verify_data, digest_);
-    }
-
-    if (total_size != vbmeta_size_) {
-        LERROR << "total vbmeta size mismatch: " << total_size << " (expected: " << vbmeta_size_
-               << ")";
-        return false;
-    }
-
-    if (!digest_matched) {
-        LERROR << "vbmeta digest mismatch";
-        return false;
-    }
-
-    return true;
-}
-
-// 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) {
-    // Loads androidboot.veritymode from kernel cmdline.
-    std::string verity_mode;
-    if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
-        verity_mode = "enforcing";  // Defaults to enforcing when it's absent.
-    }
-
-    // Converts veritymode to the format used in kernel.
-    std::string dm_verity_mode;
-    if (verity_mode == "enforcing") {
-        dm_verity_mode = "restart_on_corruption";
-    } else if (verity_mode == "logging") {
-        dm_verity_mode = "ignore_corruption";
-    } else if (verity_mode != "eio") {  // Default dm_verity_mode is eio.
-        LERROR << "Unknown androidboot.veritymode: " << verity_mode;
-        return "";
-    }
-
-    // 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;
-
-    // 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>
-    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
-    }
-
-    if (!dm_verity_mode.empty()) {
-        optional_argc += 1;
-        optional_args << dm_verity_mode << " ";
-    }
-
-    // Always use ignore_zero_blocks.
-    optional_argc += 1;
-    optional_args << "ignore_zero_blocks";
-
-    verity_table << " " << optional_argc << " " << optional_args.str();
-    return verity_table.str();
-}
-
-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;
-}
-
-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";
-        return false;
-    }
-
-    // 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)) {
-        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)) {
-        return false;
-    }
-
-    // Marks the underlying block device as read-only.
-    fs_mgr_set_blk_ro(fstab_entry->blk_device);
-
-    // Updates fstab_rec->blk_device to verity device name.
-    free(fstab_entry->blk_device);
-    fstab_entry->blk_device = strdup(verity_blk_name.c_str());
-
-    // Makes sure we've set everything up properly.
-    if (wait_for_verity_dev && !fs_mgr_wait_for_file(verity_blk_name, 1s)) {
-        return false;
-    }
-
-    return true;
-}
-
-static bool get_hashtree_descriptor(const std::string& partition_name,
-                                    const AvbSlotVerifyData& verify_data,
-                                    AvbHashtreeDescriptor* out_hashtree_desc, std::string* out_salt,
-                                    std::string* out_digest) {
-    bool found = false;
-    const uint8_t* desc_partition_name;
-
-    for (size_t i = 0; i < verify_data.num_vbmeta_images && !found; i++) {
-        // Get descriptors from vbmeta_images[i].
-        size_t num_descriptors;
-        std::unique_ptr<const AvbDescriptor* [], decltype(&avb_free)> descriptors(
-            avb_descriptor_get_all(verify_data.vbmeta_images[i].vbmeta_data,
-                                   verify_data.vbmeta_images[i].vbmeta_size, &num_descriptors),
-            avb_free);
-
-        if (!descriptors || num_descriptors < 1) {
-            continue;
-        }
-
-        // Ensures that hashtree descriptor is in /vbmeta or /boot or in
-        // the same partition for verity setup.
-        std::string vbmeta_partition_name(verify_data.vbmeta_images[i].partition_name);
-        if (vbmeta_partition_name != "vbmeta" &&
-            vbmeta_partition_name != "boot" &&  // for legacy device to append top-level vbmeta
-            vbmeta_partition_name != partition_name) {
-            LWARNING << "Skip vbmeta image at " << verify_data.vbmeta_images[i].partition_name
-                     << " for partition: " << partition_name.c_str();
-            continue;
-        }
-
-        for (size_t j = 0; j < num_descriptors && !found; j++) {
-            AvbDescriptor desc;
-            if (!avb_descriptor_validate_and_byteswap(descriptors[j], &desc)) {
-                LWARNING << "Descriptor[" << j << "] is invalid";
-                continue;
-            }
-            if (desc.tag == AVB_DESCRIPTOR_TAG_HASHTREE) {
-                desc_partition_name = (const uint8_t*)descriptors[j] + sizeof(AvbHashtreeDescriptor);
-                if (!avb_hashtree_descriptor_validate_and_byteswap(
-                        (AvbHashtreeDescriptor*)descriptors[j], out_hashtree_desc)) {
-                    continue;
-                }
-                if (out_hashtree_desc->partition_name_len != partition_name.length()) {
-                    continue;
-                }
-                // Notes that desc_partition_name is not NUL-terminated.
-                std::string hashtree_partition_name((const char*)desc_partition_name,
-                                                    out_hashtree_desc->partition_name_len);
-                if (hashtree_partition_name == partition_name) {
-                    found = true;
-                }
-            }
-        }
-    }
-
-    if (!found) {
-        LERROR << "Partition descriptor not found: " << partition_name.c_str();
-        return false;
-    }
-
-    const uint8_t* desc_salt = desc_partition_name + out_hashtree_desc->partition_name_len;
-    *out_salt = bytes_to_hex(desc_salt, out_hashtree_desc->salt_len);
-
-    const uint8_t* desc_digest = desc_salt + out_hashtree_desc->salt_len;
-    *out_digest = bytes_to_hex(desc_digest, out_hashtree_desc->root_digest_len);
-
-    return true;
-}
-
-FsManagerAvbUniquePtr FsManagerAvbHandle::Open(const fstab& fstab) {
-    FsManagerAvbOps avb_ops(fstab);
-    return DoOpen(&avb_ops);
-}
-
-FsManagerAvbUniquePtr FsManagerAvbHandle::Open(ByNameSymlinkMap&& by_name_symlink_map) {
-    if (by_name_symlink_map.empty()) {
-        LERROR << "Empty by_name_symlink_map when opening FsManagerAvbHandle";
-        return nullptr;
-    }
-    FsManagerAvbOps avb_ops(std::move(by_name_symlink_map));
-    return DoOpen(&avb_ops);
-}
-
-FsManagerAvbUniquePtr FsManagerAvbHandle::DoOpen(FsManagerAvbOps* avb_ops) {
-    bool is_device_unlocked = fs_mgr_is_device_unlocked();
-
-    FsManagerAvbUniquePtr avb_handle(new FsManagerAvbHandle());
-    if (!avb_handle) {
-        LERROR << "Failed to allocate FsManagerAvbHandle";
-        return nullptr;
-    }
-
-    AvbSlotVerifyFlags flags = is_device_unlocked ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
-                                                  : AVB_SLOT_VERIFY_FLAGS_NONE;
-    AvbSlotVerifyResult verify_result =
-        avb_ops->AvbSlotVerify(fs_mgr_get_slot_suffix(), flags, &avb_handle->avb_slot_data_);
-
-    // Only allow two verify results:
-    //   - AVB_SLOT_VERIFY_RESULT_OK.
-    //   - AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION (for UNLOCKED state).
-    //     If the device is UNLOCKED, i.e., |allow_verification_error| is true for
-    //     AvbSlotVerify(), then the following return values are all non-fatal:
-    //       * AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION
-    //       * AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED
-    //       * AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX
-    //     The latter two results were checked by bootloader prior to start fs_mgr so
-    //     we just need to handle the first result here. See *dummy* operations in
-    //     FsManagerAvbOps and the comments in external/avb/libavb/avb_slot_verify.h
-    //     for more details.
-    switch (verify_result) {
-        case AVB_SLOT_VERIFY_RESULT_OK:
-            avb_handle->status_ = kAvbHandleSuccess;
-            break;
-        case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
-            if (!is_device_unlocked) {
-                LERROR << "ERROR_VERIFICATION isn't allowed when the device is LOCKED";
-                return nullptr;
-            }
-            avb_handle->status_ = kAvbHandleVerificationError;
-            break;
-        default:
-            LERROR << "avb_slot_verify failed, result: " << verify_result;
-            return nullptr;
-    }
-
-    // Sets the MAJOR.MINOR for init to set it into "ro.boot.avb_version".
-    avb_handle->avb_version_ =
-        android::base::StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
-
-    // Checks whether FLAGS_VERIFICATION_DISABLED is set:
-    //   - Only the top-level vbmeta struct is read.
-    //   - vbmeta struct in other partitions are NOT processed, including AVB HASH descriptor(s)
-    //     and AVB HASHTREE descriptor(s).
-    AvbVBMetaImageHeader vbmeta_header;
-    avb_vbmeta_image_header_to_host_byte_order(
-        (AvbVBMetaImageHeader*)avb_handle->avb_slot_data_->vbmeta_images[0].vbmeta_data,
-        &vbmeta_header);
-    bool verification_disabled =
-        ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
-
-    if (verification_disabled) {
-        avb_handle->status_ = kAvbHandleVerificationDisabled;
-    } else {
-        // Verifies vbmeta structs against the digest passed from bootloader in kernel cmdline.
-        std::unique_ptr<FsManagerAvbVerifier> avb_verifier = FsManagerAvbVerifier::Create();
-        if (!avb_verifier) {
-            LERROR << "Failed to create FsManagerAvbVerifier";
-            return nullptr;
-        }
-        if (!avb_verifier->VerifyVbmetaImages(*avb_handle->avb_slot_data_)) {
-            LERROR << "VerifyVbmetaImages failed";
-            return nullptr;
-        }
-
-        // Checks whether FLAGS_HASHTREE_DISABLED is set.
-        bool hashtree_disabled =
-            ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
-        if (hashtree_disabled) {
-            avb_handle->status_ = kAvbHandleHashtreeDisabled;
-        }
-    }
-
-    LINFO << "Returning avb_handle with status: " << avb_handle->status_;
-    return avb_handle;
-}
-
-SetUpAvbHashtreeResult FsManagerAvbHandle::SetUpAvbHashtree(struct fstab_rec* fstab_entry,
-                                                            bool wait_for_verity_dev) {
-    if (!fstab_entry || status_ == kAvbHandleUninitialized || !avb_slot_data_ ||
-        avb_slot_data_->num_vbmeta_images < 1) {
-        return SetUpAvbHashtreeResult::kFail;
-    }
-
-    if (status_ == kAvbHandleHashtreeDisabled || status_ == kAvbHandleVerificationDisabled) {
-        LINFO << "AVB HASHTREE disabled on: " << fstab_entry->mount_point;
-        return SetUpAvbHashtreeResult::kDisabled;
-    }
-
-    // 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));
-    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) {
-            partition_name.erase(ab_suffix);
-        }
-    }
-
-    AvbHashtreeDescriptor hashtree_descriptor;
-    std::string salt;
-    std::string root_digest;
-    if (!get_hashtree_descriptor(partition_name, *avb_slot_data_, &hashtree_descriptor, &salt,
-                                 &root_digest)) {
-        return SetUpAvbHashtreeResult::kFail;
-    }
-
-    // Converts HASHTREE descriptor to verity_table_params.
-    if (!hashtree_dm_verity_setup(fstab_entry, hashtree_descriptor, salt, root_digest,
-                                  wait_for_verity_dev)) {
-        return SetUpAvbHashtreeResult::kFail;
-    }
-
-    return SetUpAvbHashtreeResult::kSuccess;
-}
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
deleted file mode 100644
index 43879fe..0000000
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2016 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_priv_avb_ops.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <string>
-
-#include <android-base/macros.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <libavb/libavb.h>
-#include <utils/Compat.h>
-
-#include "fs_mgr.h"
-#include "fs_mgr_priv.h"
-
-static AvbIOResult read_from_partition(AvbOps* ops, const char* partition, int64_t offset,
-                                       size_t num_bytes, void* buffer, size_t* out_num_read) {
-    return FsManagerAvbOps::GetInstanceFromAvbOps(ops)->ReadFromPartition(
-        partition, offset, num_bytes, buffer, out_num_read);
-}
-
-static AvbIOResult dummy_read_rollback_index(AvbOps* ops ATTRIBUTE_UNUSED,
-                                             size_t rollback_index_location ATTRIBUTE_UNUSED,
-                                             uint64_t* out_rollback_index) {
-    // rollback_index has been checked in bootloader phase.
-    // In user-space, returns the smallest value 0 to pass the check.
-    *out_rollback_index = 0;
-    return AVB_IO_RESULT_OK;
-}
-
-static AvbIOResult dummy_validate_vbmeta_public_key(
-    AvbOps* ops ATTRIBUTE_UNUSED, const uint8_t* public_key_data ATTRIBUTE_UNUSED,
-    size_t public_key_length ATTRIBUTE_UNUSED, const uint8_t* public_key_metadata ATTRIBUTE_UNUSED,
-    size_t public_key_metadata_length ATTRIBUTE_UNUSED, bool* out_is_trusted) {
-    // vbmeta public key has been checked in bootloader phase.
-    // In user-space, returns true to pass the check.
-    //
-    // Addtionally, user-space should check
-    // androidboot.vbmeta.{hash_alg, size, digest} against the digest
-    // of all vbmeta images after invoking avb_slot_verify().
-    *out_is_trusted = true;
-    return AVB_IO_RESULT_OK;
-}
-
-static AvbIOResult dummy_read_is_device_unlocked(AvbOps* ops ATTRIBUTE_UNUSED,
-                                                 bool* out_is_unlocked) {
-    // The function is for bootloader to update the value into
-    // androidboot.vbmeta.device_state in kernel cmdline.
-    // In user-space, returns true as we don't need to update it anymore.
-    *out_is_unlocked = true;
-    return AVB_IO_RESULT_OK;
-}
-
-static AvbIOResult dummy_get_unique_guid_for_partition(AvbOps* ops ATTRIBUTE_UNUSED,
-                                                       const char* partition ATTRIBUTE_UNUSED,
-                                                       char* guid_buf, size_t guid_buf_size) {
-    // The function is for bootloader to set the correct UUID
-    // for a given partition in kernel cmdline.
-    // In user-space, returns a faking one as we don't need to update
-    // it anymore.
-    snprintf(guid_buf, guid_buf_size, "1234-fake-guid-for:%s", partition);
-    return AVB_IO_RESULT_OK;
-}
-
-static AvbIOResult dummy_get_size_of_partition(AvbOps* ops ATTRIBUTE_UNUSED,
-                                               const char* partition ATTRIBUTE_UNUSED,
-                                               uint64_t* out_size_num_byte) {
-    // The function is for bootloader to load entire content of AVB HASH partitions.
-    // In user-space, returns 0 as we only need to set up AVB HASHTHREE partitions.
-    *out_size_num_byte = 0;
-    return AVB_IO_RESULT_OK;
-}
-
-void FsManagerAvbOps::InitializeAvbOps() {
-    // We only need to provide the implementation of read_from_partition()
-    // operation since that's all what is being used by the avb_slot_verify().
-    // Other I/O operations are only required in bootloader but not in
-    // user-space so we set them as dummy operations. Also zero the entire
-    // struct so operations added in the future will be set to NULL.
-    memset(&avb_ops_, 0, sizeof(AvbOps));
-    avb_ops_.read_from_partition = read_from_partition;
-    avb_ops_.read_rollback_index = dummy_read_rollback_index;
-    avb_ops_.validate_vbmeta_public_key = dummy_validate_vbmeta_public_key;
-    avb_ops_.read_is_device_unlocked = dummy_read_is_device_unlocked;
-    avb_ops_.get_unique_guid_for_partition = dummy_get_unique_guid_for_partition;
-    avb_ops_.get_size_of_partition = dummy_get_size_of_partition;
-
-    // Sets user_data for GetInstanceFromAvbOps() to convert it back to FsManagerAvbOps.
-    avb_ops_.user_data = this;
-}
-
-FsManagerAvbOps::FsManagerAvbOps(std::map<std::string, std::string>&& by_name_symlink_map)
-    : by_name_symlink_map_(std::move(by_name_symlink_map)) {
-    InitializeAvbOps();
-}
-
-FsManagerAvbOps::FsManagerAvbOps(const fstab& fstab) {
-    // Constructs the by-name symlink map for each fstab record.
-    // /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a =>
-    // by_name_symlink_map_["system_a"] = "/dev/block/platform/soc.0/7824900.sdhci/by-name/system_a"
-    for (int i = 0; i < fstab.num_entries; i++) {
-        std::string partition_name = basename(fstab.recs[i].blk_device);
-        by_name_symlink_map_[partition_name] = fstab.recs[i].blk_device;
-    }
-    InitializeAvbOps();
-}
-
-AvbIOResult FsManagerAvbOps::ReadFromPartition(const char* partition, int64_t offset,
-                                               size_t num_bytes, void* buffer,
-                                               size_t* out_num_read) {
-    const auto iter = by_name_symlink_map_.find(partition);
-    if (iter == by_name_symlink_map_.end()) {
-        LERROR << "by-name symlink not found for partition: '" << partition << "'";
-        return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
-    }
-    std::string path = iter->second;
-
-    // Ensures the device path (a symlink created by init) is ready to access.
-    if (!fs_mgr_wait_for_file(path, 1s)) {
-        return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
-    }
-
-    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
-    if (fd < 0) {
-        PERROR << "Failed to open " << path;
-        return AVB_IO_RESULT_ERROR_IO;
-    }
-
-    // If offset is negative, interprets its absolute value as the
-    //  number of bytes from the end of the partition.
-    if (offset < 0) {
-        off64_t total_size = lseek64(fd, 0, SEEK_END);
-        if (total_size == -1) {
-            PERROR << "Failed to lseek64 to end of the partition";
-            return AVB_IO_RESULT_ERROR_IO;
-        }
-        offset = total_size + offset;
-        // Repositions the offset to the beginning.
-        if (lseek64(fd, 0, SEEK_SET) == -1) {
-            PERROR << "Failed to lseek64 to the beginning of the partition";
-            return AVB_IO_RESULT_ERROR_IO;
-        }
-    }
-
-    // On Linux, we never get partial reads from block devices (except
-    // for EOF).
-    ssize_t num_read = TEMP_FAILURE_RETRY(pread64(fd, buffer, num_bytes, offset));
-    if (num_read < 0 || (size_t)num_read != num_bytes) {
-        PERROR << "Failed to read " << num_bytes << " bytes from " << path << " offset " << offset;
-        return AVB_IO_RESULT_ERROR_IO;
-    }
-
-    if (out_num_read != nullptr) {
-        *out_num_read = num_read;
-    }
-
-    return AVB_IO_RESULT_OK;
-}
-
-AvbSlotVerifyResult FsManagerAvbOps::AvbSlotVerify(const std::string& ab_suffix,
-                                                   AvbSlotVerifyFlags flags,
-                                                   AvbSlotVerifyData** out_data) {
-    // Invokes avb_slot_verify() to load and verify all vbmeta images.
-    // Sets requested_partitions to nullptr as it's to copy the contents
-    // of HASH partitions into handle>avb_slot_data_, which is not required as
-    // fs_mgr only deals with HASHTREE partitions.
-    const char* requested_partitions[] = {nullptr};
-    // The |hashtree_error_mode| field doesn't matter as it only
-    // influences the generated kernel cmdline parameters.
-    return avb_slot_verify(&avb_ops_, requested_partitions, ab_suffix.c_str(), flags,
-                           AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, out_data);
-}
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index 9c5d3f3..abece4d 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -14,7 +14,10 @@
  * limitations under the License.
  */
 
+#include <algorithm>
+#include <iterator>
 #include <string>
+#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
@@ -23,46 +26,74 @@
 
 #include "fs_mgr_priv.h"
 
-// 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::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);
 
-    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;
-                }
-            }
+    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 boot config value in properties, kernel cmdline and
-// device tree (in that order).  returns 'true' if successfully found, 'false'
-// otherwise
+// 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;
+    if (!cmdline.empty() && cmdline.back() == '\n') {
+        cmdline.pop_back();
+    }
+    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
-    *out_val = android::base::GetProperty("ro.boot." + key, "");
-    if (!out_val->empty()) {
-        return true;
-    }
-
-    // fallback to kernel cmdline, properties may not be ready yet
-    if (fs_mgr_get_boot_config_from_kernel_cmdline(key, out_val)) {
-        return true;
-    }
-
-    // lastly, check the device tree
+    // 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)) {
@@ -73,5 +104,16 @@
         }
     }
 
+    // next, check if we have "ro.boot" property already
+    *out_val = android::base::GetProperty("ro.boot." + key, "");
+    if (!out_val->empty()) {
+        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..1f21a71
--- /dev/null
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -0,0 +1,222 @@
+/*
+ * 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/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <fs_mgr/file_wait.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 GetPhysicalPartitionDevicePath(const LpMetadata& metadata,
+                                           const LpMetadataBlockDevice& block_device,
+                                           const std::string& super_device,
+                                           std::string* result) {
+    // Note: device-mapper will not accept symlinks, so we must use realpath
+    // here.
+    std::string name = GetBlockDevicePartitionName(block_device);
+    std::string path = "/dev/block/by-name/" + name;
+    // If the super device is the source of this block device's metadata,
+    // make sure we use the correct super device (and not just "super",
+    // which might not exist.)
+    if (GetMetadataSuperBlockDevice(metadata) == &block_device) {
+        path = super_device;
+    }
+    if (!android::base::Realpath(path, result)) {
+        PERROR << "realpath: " << path;
+        return false;
+    }
+    return true;
+}
+
+static bool CreateDmTable(const LpMetadata& metadata, const LpMetadataPartition& partition,
+                          const std::string& super_device, 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: {
+                const auto& block_device = metadata.block_devices[extent.target_source];
+                std::string path;
+                if (!GetPhysicalPartitionDevicePath(metadata, block_device, super_device, &path)) {
+                    LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
+                    return false;
+                }
+                target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, path,
+                                                          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 LpMetadata& metadata, const LpMetadataPartition& partition,
+                                   bool force_writable, const std::chrono::milliseconds& timeout_ms,
+                                   const std::string& super_device, std::string* path) {
+    DeviceMapper& dm = DeviceMapper::Instance();
+
+    DmTable table;
+    if (!CreateDmTable(metadata, partition, super_device, &table)) {
+        return false;
+    }
+    if (force_writable) {
+        table.set_readonly(false);
+    }
+    std::string name = GetPartitionName(partition);
+    if (!dm.CreateDevice(name, table)) {
+        return false;
+    }
+    if (!dm.GetDmDevicePathByName(name, path)) {
+        return false;
+    }
+    if (timeout_ms > std::chrono::milliseconds::zero()) {
+        if (!WaitForFile(*path, timeout_ms)) {
+            DestroyLogicalPartition(name, {});
+            LERROR << "Timed out waiting for device path: " << *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;
+    }
+    return CreateLogicalPartitions(*metadata.get(), block_device);
+}
+
+std::unique_ptr<LpMetadata> ReadCurrentMetadata(const std::string& block_device) {
+    uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
+    return ReadMetadata(block_device.c_str(), slot);
+}
+
+bool CreateLogicalPartitions(const LpMetadata& metadata, const std::string& super_device) {
+    for (const auto& partition : metadata.partitions) {
+        if (!partition.num_extents) {
+            LINFO << "Skipping zero-length logical partition: " << GetPartitionName(partition);
+            continue;
+        }
+        std::string path;
+        if (!CreateLogicalPartition(metadata, partition, false, {}, super_device, &path)) {
+            LERROR << "Could not create logical partition: " << GetPartitionName(partition);
+            return false;
+        }
+    }
+    return true;
+}
+
+bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
+                            const std::string& partition_name, bool force_writable,
+                            const std::chrono::milliseconds& timeout_ms, std::string* path) {
+    for (const auto& partition : metadata.partitions) {
+        if (GetPartitionName(partition) == partition_name) {
+            return CreateLogicalPartition(metadata, partition, force_writable, timeout_ms,
+                                          block_device, path);
+        }
+    }
+    LERROR << "Could not find any partition with name: " << partition_name;
+    return false;
+}
+
+bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot,
+                            const std::string& partition_name, bool force_writable,
+                            const std::chrono::milliseconds& timeout_ms, std::string* path) {
+    auto metadata = ReadMetadata(block_device.c_str(), metadata_slot);
+    if (!metadata) {
+        LOG(ERROR) << "Could not read partition table.";
+        return true;
+    }
+    return CreateLogicalPartition(block_device, *metadata.get(), partition_name, force_writable,
+                                  timeout_ms, path);
+}
+
+bool UnmapDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms) {
+    DeviceMapper& dm = DeviceMapper::Instance();
+    std::string path;
+    if (timeout_ms > std::chrono::milliseconds::zero()) {
+        dm.GetDmDevicePathByName(name, &path);
+    }
+    if (!dm.DeleteDevice(name)) {
+        return false;
+    }
+    if (!path.empty() && !WaitForFileDeleted(path, timeout_ms)) {
+        LERROR << "Timed out waiting for device path to unlink: " << path;
+        return false;
+    }
+    return true;
+}
+
+bool DestroyLogicalPartition(const std::string& name, const std::chrono::milliseconds& timeout_ms) {
+    if (!UnmapDevice(name, timeout_ms)) {
+        return false;
+    }
+    LINFO << "Unmapped logical partition " << name;
+    return true;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 845cca9..1c6652a 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -24,6 +24,7 @@
 #include <cutils/partition_utils.h>
 #include <sys/mount.h>
 
+#include <android-base/unique_fd.h>
 #include <ext4_utils/ext4.h>
 #include <ext4_utils/ext4_utils.h>
 #include <logwrap/logwrap.h>
@@ -34,30 +35,31 @@
 #include "fs_mgr_priv.h"
 #include "cryptfs.h"
 
-static int get_dev_sz(char *fs_blkdev, uint64_t *dev_sz)
-{
-    int fd;
+using android::base::unique_fd;
 
-    if ((fd = open(fs_blkdev, O_RDONLY)) < 0) {
+// Realistically, this file should be part of the android::fs_mgr namespace;
+using namespace android::fs_mgr;
+
+static int get_dev_sz(const std::string& fs_blkdev, uint64_t* dev_sz) {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(fs_blkdev.c_str(), O_RDONLY | O_CLOEXEC)));
+
+    if (fd < 0) {
         PERROR << "Cannot open block device";
         return -1;
     }
 
     if ((ioctl(fd, BLKGETSIZE64, dev_sz)) == -1) {
         PERROR << "Cannot get block device size";
-        close(fd);
         return -1;
     }
 
-    close(fd);
     return 0;
 }
 
-static int format_ext4(char *fs_blkdev, char *fs_mnt_point, bool crypt_footer)
-{
+static int format_ext4(const std::string& fs_blkdev, const std::string& 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) {
@@ -71,7 +73,8 @@
 
     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};
+            "/system/bin/mke2fs", "-t",   "ext4", "-b", "4096", fs_blkdev.c_str(),
+            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);
@@ -81,12 +84,7 @@
     }
 
     const char* const e2fsdroid_args[] = {
-        "/system/bin/e2fsdroid",
-        "-e",
-        "-a",
-        fs_mnt_point,
-        fs_blkdev,
-        nullptr};
+            "/system/bin/e2fsdroid", "-e", "-a", fs_mnt_point.c_str(), fs_blkdev.c_str(), nullptr};
 
     rc = android_fork_execvp_ext(arraysize(e2fsdroid_args), const_cast<char**>(e2fsdroid_args),
                                  NULL, true, LOG_KLOG, true, nullptr, nullptr, 0);
@@ -97,10 +95,7 @@
     return rc;
 }
 
-static int format_f2fs(char *fs_blkdev, uint64_t dev_sz, bool crypt_footer)
-{
-    int status;
-
+static int format_f2fs(const std::string& fs_blkdev, uint64_t dev_sz, bool crypt_footer) {
     if (!dev_sz) {
         int rc = get_dev_sz(fs_blkdev, &dev_sz);
         if (rc) {
@@ -117,13 +112,8 @@
     // clang-format off
     const char* const args[] = {
         "/system/bin/make_f2fs",
-        "-d1",
-        "-f",
-        "-O", "encrypt",
-        "-O", "quota",
-        "-O", "verity",
-        "-w", "4096",
-        fs_blkdev,
+        "-g", "android",
+        fs_blkdev.c_str(),
         size_str.c_str(),
         nullptr
     };
@@ -133,20 +123,15 @@
                                    LOG_KLOG, true, nullptr, nullptr, 0);
 }
 
-int fs_mgr_do_format(struct fstab_rec *fstab, bool crypt_footer)
-{
-    int rc = -EINVAL;
+int fs_mgr_do_format(const FstabEntry& entry, bool crypt_footer) {
+    LERROR << __FUNCTION__ << ": Format " << entry.blk_device << " as '" << entry.fs_type << "'";
 
-    LERROR << __FUNCTION__ << ": Format " << fstab->blk_device
-           << " as '" << fstab->fs_type << "'";
-
-    if (!strncmp(fstab->fs_type, "f2fs", 4)) {
-        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);
+    if (entry.fs_type == "f2fs") {
+        return format_f2fs(entry.blk_device, entry.length, crypt_footer);
+    } else if (entry.fs_type == "ext4") {
+        return format_ext4(entry.blk_device, entry.mount_point, crypt_footer);
     } else {
-        LERROR << "File system type '" << fstab->fs_type << "' is not supported";
+        LERROR << "File system type '" << entry.fs_type << "' is not supported";
+        return -EINVAL;
     }
-
-    return rc;
 }
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index c9fb7aa..31790b1 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -24,140 +24,56 @@
 #include <unistd.h>
 
 #include <algorithm>
+#include <array>
 #include <utility>
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <libgsi/libgsi.h>
 
 #include "fs_mgr_priv.h"
 
+using android::base::ParseByteCount;
+using android::base::ParseInt;
+using android::base::ReadFileToString;
+using android::base::Split;
+using android::base::StartsWith;
+
+namespace android {
+namespace fs_mgr {
+namespace {
+
 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;
-    int swap_prio;
-    int max_comp_streams;
-    unsigned int zram_size;
-    uint64_t reserved_size;
-    unsigned int file_contents_mode;
-    unsigned int file_names_mode;
-    unsigned int erase_blk_size;
-    unsigned int logical_blk_size;
-};
-
-struct flag_list {
+struct FlagList {
     const char *name;
-    unsigned int flag;
+    uint64_t flag;
 };
 
-static struct flag_list 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 },
+FlagList kMountFlagsList[] = {
+        {"noatime", MS_NOATIME},
+        {"noexec", MS_NOEXEC},
+        {"nosuid", MS_NOSUID},
+        {"nodev", MS_NODEV},
+        {"nodiratime", MS_NODIRATIME},
+        {"ro", MS_RDONLY},
+        {"rw", 0},
+        {"sync", MS_SYNCHRONOUS},
+        {"remount", MS_REMOUNT},
+        {"bind", MS_BIND},
+        {"rec", MS_REC},
+        {"unbindable", MS_UNBINDABLE},
+        {"private", MS_PRIVATE},
+        {"slave", MS_SLAVE},
+        {"shared", MS_SHARED},
+        {"defaults", 0},
 };
 
-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},
-    {"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},
-    {0, 0},
-};
-
-#define EM_AES_256_XTS  1
-#define EM_ICE          2
-#define EM_AES_256_CTS  3
-#define EM_AES_256_HEH  4
-
-static const struct flag_list file_contents_encryption_modes[] = {
-    {"aes-256-xts", EM_AES_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},
-    {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;
+off64_t CalculateZramSize(int percentage) {
+    off64_t total;
 
     total  = sysconf(_SC_PHYS_PAGES);
     total *= percentage;
@@ -168,30 +84,12 @@
     return total;
 }
 
-static uint64_t parse_size(const char *arg)
-{
-    char *endptr;
-    uint64_t size = strtoull(arg, &endptr, 10);
-    if (*endptr == 'k' || *endptr == 'K')
-        size *= 1024LL;
-    else if (*endptr == 'm' || *endptr == 'M')
-        size *= 1024LL * 1024LL;
-    else if (*endptr == 'g' || *endptr == 'G')
-        size *= 1024LL * 1024LL * 1024LL;
-
-    return size;
-}
-
-/* fills 'dt_value' with the underlying device tree value string without
- * the trailing '\0'. Returns true if 'dt_value' has a valid string, 'false'
- * otherwise.
- */
-static bool read_dt_file(const std::string& file_name, std::string* dt_value)
-{
+// Fills 'dt_value' with the underlying device tree value string without the trailing '\0'.
+// Returns true if 'dt_value' has a valid string, 'false' otherwise.
+bool ReadDtFile(const std::string& file_name, std::string* dt_value) {
     if (android::base::ReadFileToString(file_name, dt_value)) {
         if (!dt_value->empty()) {
-            // trim the trailing '\0' out, otherwise the comparison
-            // will produce false-negatives.
+            // Trim the trailing '\0' out, otherwise the comparison will produce false-negatives.
             dt_value->resize(dt_value->size() - 1);
             return true;
         }
@@ -200,183 +98,252 @@
     return false;
 }
 
-static int parse_flags(char *flags, struct flag_list *fl,
-                       struct fs_mgr_flag_values *flag_vals,
-                       char *fs_options, int fs_options_len)
-{
-    int f = 0;
-    int i;
-    char *p;
-    char *savep;
+const std::array<const char*, 3> kFileContentsEncryptionMode = {
+        "aes-256-xts",
+        "adiantum",
+        "ice",
+};
 
-    /* initialize flag values.  If we find a relevant flag, we'll
-     * update the value */
-    if (flag_vals) {
-        memset(flag_vals, 0, sizeof(*flag_vals));
-        flag_vals->partnum = -1;
-        flag_vals->swap_prio = -1; /* negative means it wasn't specified. */
+const std::array<const char*, 3> kFileNamesEncryptionMode = {
+        "aes-256-cts",
+        "aes-256-heh",
+        "adiantum",
+};
+
+void ParseFileEncryption(const std::string& arg, FstabEntry* entry) {
+    // 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.
+    entry->fs_mgr_flags.file_encryption = true;
+
+    auto parts = Split(arg, ":");
+    if (parts.empty() || parts.size() > 2) {
+        LWARNING << "Warning: fileencryption= flag malformed: " << arg;
+        return;
     }
 
-    /* initialize fs_options to the null string */
-    if (fs_options && (fs_options_len > 0)) {
-        fs_options[0] = '\0';
+    // Alias for backwards compatibility.
+    if (parts[0] == "software") {
+        parts[0] = "aes-256-xts";
     }
 
-    p = strtok_r(flags, ",", &savep);
-    while (p) {
-        /* Look for the flag "p" in the flag list "fl"
-         * 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))) {
-                f |= fl[i].flag;
-                if ((fl[i].flag == MF_CRYPT) && flag_vals) {
-                    /* 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) {
-                    /* 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) {
-                    /* 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) {
-                    /* 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_contents_mode = EM_AES_256_XTS;
-                    flag_vals->file_names_mode = EM_AES_256_CTS;
-                } else if ((fl[i].flag == MF_FILEENCRYPTION) && flag_vals) {
-                    /* 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.
-                     */
-                    char *mode = strchr(p, '=') + 1;
-                    char *colon = strchr(mode, ':');
-                    if (colon) {
-                        *colon = '\0';
-                    }
-                    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_KEYDIRECTORY) && flag_vals) {
-                    /* The metadata flag is followed by an = and the
-                     * directory for the keys.  Get it and return it.
-                     */
-                    flag_vals->key_dir = strdup(strchr(p, '=') + 1);
-                } else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
-                    /* 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) {
-                    /* 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;
+    if (std::find(kFileContentsEncryptionMode.begin(), kFileContentsEncryptionMode.end(),
+                  parts[0]) == kFileContentsEncryptionMode.end()) {
+        LWARNING << "fileencryption= flag malformed, file contents encryption mode not found: "
+                 << arg;
+        return;
+    }
 
-                    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;
-                        if (!strcmp(part_start, "auto")) {
-                            flag_vals->partnum = -1;
-                        } else {
-                            flag_vals->partnum = strtol(part_start, NULL, 0);
-                        }
-                    } 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);
-                    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) {
-                    /* 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) {
-                    /* 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);
-                    if (val >= 4096 && (val & (val - 1)) == 0)
-                        flag_vals->erase_blk_size = val;
-                } else if ((fl[i].flag == MF_LOGICALBLKSIZE) && flag_vals) {
-                    /* 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);
-                    if (val >= 4096 && (val & (val - 1)) == 0)
-                        flag_vals->logical_blk_size = val;
-                } else if ((fl[i].flag == MF_SYSFS) && flag_vals) {
-                    /* The path to trigger device gc by idle-maint of vold. */
-                    flag_vals->sysfs_path = strdup(strchr(p, '=') + 1);
-                }
-                break;
-            }
+    entry->file_contents_mode = parts[0];
+
+    if (parts.size() == 2) {
+        if (std::find(kFileNamesEncryptionMode.begin(), kFileNamesEncryptionMode.end(), parts[1]) ==
+            kFileNamesEncryptionMode.end()) {
+            LWARNING << "fileencryption= flag malformed, file names encryption mode not found: "
+                     << arg;
+            return;
         }
 
-        if (!fl[i].name) {
-            if (fs_options) {
-                /* It's not a known flag, so it must be a filesystem specific
-                 * option.  Add it to fs_options if it was passed in.
-                 */
-                strlcat(fs_options, p, fs_options_len);
-                strlcat(fs_options, ",", fs_options_len);
-            } else {
-                /* fs_options was not passed in, so if the flag is unknown
-                 * it's an error.
-                 */
-                LERROR << "Warning: unknown flag " << p;
-            }
-        }
-        p = strtok_r(NULL, ",", &savep);
+        entry->file_names_mode = parts[1];
+    } else if (entry->file_contents_mode == "adiantum") {
+        entry->file_names_mode = "adiantum";
+    } else {
+        entry->file_names_mode = "aes-256-cts";
     }
-
-    if (fs_options && fs_options[0]) {
-        /* remove the last trailing comma from the list of options */
-        fs_options[strlen(fs_options) - 1] = '\0';
-    }
-
-    return f;
 }
 
-static std::string init_android_dt_dir() {
+bool SetMountFlag(const std::string& flag, FstabEntry* entry) {
+    for (const auto& [name, value] : kMountFlagsList) {
+        if (flag == name) {
+            entry->flags |= value;
+            return true;
+        }
+    }
+    return false;
+}
+
+void ParseMountFlags(const std::string& flags, FstabEntry* entry) {
+    std::string fs_options;
+    for (const auto& flag : Split(flags, ",")) {
+        if (!SetMountFlag(flag, entry)) {
+            // Unknown flag, so it must be a filesystem specific option.
+            if (!fs_options.empty()) {
+                fs_options.append(",");  // appends a comma if not the first
+            }
+            fs_options.append(flag);
+
+            if (entry->fs_type == "f2fs" && StartsWith(flag, "reserve_root=")) {
+                std::string arg;
+                if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) {
+                    arg = flag.substr(equal_sign + 1);
+                }
+                if (!ParseInt(arg, &entry->reserved_size)) {
+                    LWARNING << "Warning: reserve_root= flag malformed: " << arg;
+                } else {
+                    entry->reserved_size <<= 12;
+                }
+            }
+        }
+    }
+    entry->fs_options = std::move(fs_options);
+}
+
+void ParseFsMgrFlags(const std::string& flags, FstabEntry* entry) {
+    for (const auto& flag : Split(flags, ",")) {
+        if (flag.empty() || flag == "defaults") continue;
+        std::string arg;
+        if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) {
+            arg = flag.substr(equal_sign + 1);
+        }
+
+        // First handle flags that simply set a boolean.
+#define CheckFlag(flag_name, value)       \
+    if (flag == flag_name) {              \
+        entry->fs_mgr_flags.value = true; \
+        continue;                         \
+    }
+
+        CheckFlag("wait", wait);
+        CheckFlag("check", check);
+        CheckFlag("nonremovable", nonremovable);
+        CheckFlag("recoveryonly", recovery_only);
+        CheckFlag("noemulatedsd", no_emulated_sd);
+        CheckFlag("notrim", no_trim);
+        CheckFlag("verify", verify);
+        CheckFlag("formattable", formattable);
+        CheckFlag("slotselect", slot_select);
+        CheckFlag("latemount", late_mount);
+        CheckFlag("nofail", no_fail);
+        CheckFlag("verifyatboot", verify_at_boot);
+        CheckFlag("quota", quota);
+        CheckFlag("avb", avb);
+        CheckFlag("logical", logical);
+        CheckFlag("checkpoint=block", checkpoint_blk);
+        CheckFlag("checkpoint=fs", checkpoint_fs);
+        CheckFlag("first_stage_mount", first_stage_mount);
+        CheckFlag("slotselect_other", slot_select_other);
+        CheckFlag("fsverity", fs_verity);
+
+#undef CheckFlag
+
+        // Then handle flags that take an argument.
+        if (StartsWith(flag, "encryptable=")) {
+            // The encryptable flag is followed by an = and the  location of the keys.
+            entry->fs_mgr_flags.crypt = true;
+            entry->key_loc = arg;
+        } else if (StartsWith(flag, "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
+            entry->fs_mgr_flags.vold_managed = true;
+            auto parts = Split(arg, ":");
+            if (parts.size() != 2) {
+                LWARNING << "Warning: voldmanaged= flag malformed: " << arg;
+                continue;
+            }
+
+            entry->label = std::move(parts[0]);
+            if (parts[1] == "auto") {
+                entry->partnum = -1;
+            } else {
+                if (!ParseInt(parts[1], &entry->partnum)) {
+                    entry->partnum = -1;
+                    LWARNING << "Warning: voldmanaged= flag malformed: " << arg;
+                    continue;
+                }
+            }
+        } else if (StartsWith(flag, "length=")) {
+            // The length flag is followed by an = and the size of the partition.
+            if (!ParseInt(arg, &entry->length)) {
+                LWARNING << "Warning: length= flag malformed: " << arg;
+            }
+        } else if (StartsWith(flag, "swapprio=")) {
+            if (!ParseInt(arg, &entry->swap_prio)) {
+                LWARNING << "Warning: length= flag malformed: " << arg;
+            }
+        } else if (StartsWith(flag, "zramsize=")) {
+            if (!arg.empty() && arg.back() == '%') {
+                arg.pop_back();
+                int val;
+                if (ParseInt(arg, &val, 0, 100)) {
+                    entry->zram_size = CalculateZramSize(val);
+                } else {
+                    LWARNING << "Warning: zramsize= flag malformed: " << arg;
+                }
+            } else {
+                if (!ParseInt(arg, &entry->zram_size)) {
+                    LWARNING << "Warning: zramsize= flag malformed: " << arg;
+                }
+            }
+        } else if (StartsWith(flag, "forceencrypt=")) {
+            // The forceencrypt flag is followed by an = and the location of the keys.
+            entry->fs_mgr_flags.force_crypt = true;
+            entry->key_loc = arg;
+        } else if (StartsWith(flag, "fileencryption=")) {
+            ParseFileEncryption(arg, entry);
+        } else if (StartsWith(flag, "forcefdeorfbe=")) {
+            // The forcefdeorfbe flag is followed by an = and the location of the keys.  Get it and
+            // return it.
+            entry->fs_mgr_flags.force_fde_or_fbe = true;
+            entry->key_loc = arg;
+            entry->file_contents_mode = "aes-256-xts";
+            entry->file_names_mode = "aes-256-cts";
+        } else if (StartsWith(flag, "max_comp_streams=")) {
+            if (!ParseInt(arg, &entry->max_comp_streams)) {
+                LWARNING << "Warning: max_comp_streams= flag malformed: " << arg;
+            }
+        } else if (StartsWith(flag, "reservedsize=")) {
+            // The reserved flag is followed by an = and the reserved size of the partition.
+            uint64_t size;
+            if (!ParseByteCount(arg, &size)) {
+                LWARNING << "Warning: reservedsize= flag malformed: " << arg;
+            } else {
+                entry->reserved_size = static_cast<off64_t>(size);
+            }
+        } else if (StartsWith(flag, "eraseblk=")) {
+            // 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.
+            off64_t val;
+            if (!ParseInt(arg, &val) || val < 4096 || (val & (val - 1)) != 0) {
+                LWARNING << "Warning: eraseblk= flag malformed: " << arg;
+            } else {
+                entry->erase_blk_size = val;
+            }
+        } else if (StartsWith(flag, "logicalblk=")) {
+            // 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.
+            off64_t val;
+            if (!ParseInt(arg, &val) || val < 4096 || (val & (val - 1)) != 0) {
+                LWARNING << "Warning: logicalblk= flag malformed: " << arg;
+            } else {
+                entry->logical_blk_size = val;
+            }
+        } else if (StartsWith(flag, "avb_keys=")) {  // must before the following "avb"
+            entry->avb_keys = arg;
+        } else if (StartsWith(flag, "avb")) {
+            entry->fs_mgr_flags.avb = true;
+            entry->vbmeta_partition = arg;
+        } else if (StartsWith(flag, "keydirectory=")) {
+            // The metadata flag is followed by an = and the directory for the keys.
+            entry->key_dir = arg;
+        } else if (StartsWith(flag, "sysfs_path=")) {
+            // The path to trigger device gc by idle-maint of vold.
+            entry->sysfs_path = arg;
+        } else if (StartsWith(flag, "zram_loopback_path=")) {
+            // The path to use loopback for zram.
+            entry->zram_loopback_path = arg;
+        } else if (StartsWith(flag, "zram_loopback_size=")) {
+            if (!ParseByteCount(arg, &entry->zram_loopback_size)) {
+                LWARNING << "Warning: zram_loopback_size= flag malformed: " << arg;
+            }
+        } else if (StartsWith(flag, "zram_backing_dev_path=")) {
+            entry->zram_backing_dev_path = arg;
+        } else {
+            LWARNING << "Warning: unknown flag: " << flag;
+        }
+    }
+}
+
+std::string InitAndroidDtDir() {
     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)) {
@@ -386,27 +353,23 @@
     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() {
+bool IsDtFstabCompatible() {
     std::string dt_value;
     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;
-        }
+
+    if (ReadDtFile(file_name, &dt_value) && dt_value == "android,fstab") {
+        // If there's no status property or its set to "ok" or "okay", then we use the DT fstab.
+        std::string status_value;
+        std::string status_file_name = get_android_dt_dir() + "/fstab/status";
+        return !ReadDtFile(status_file_name, &status_value) || status_value == "ok" ||
+               status_value == "okay";
     }
 
     return false;
 }
 
-static std::string read_fstab_from_dt() {
-    if (!is_dt_compatible() || !is_dt_fstab_compatible()) {
+std::string ReadFstabFromDt() {
+    if (!is_dt_compatible() || !IsDtFstabCompatible()) {
         return {};
     }
 
@@ -427,7 +390,7 @@
         std::string value;
         // skip a partition entry if the status property is present and not set to ok
         file_name = android::base::StringPrintf("%s/%s/status", fstabdir_name.c_str(), dp->d_name);
-        if (read_dt_file(file_name, &value)) {
+        if (ReadDtFile(file_name, &value)) {
             if (value != "okay" && value != "ok") {
                 LINFO << "dt_fstab: Skip disabled entry for partition " << dp->d_name;
                 continue;
@@ -435,7 +398,7 @@
         }
 
         file_name = android::base::StringPrintf("%s/%s/dev", fstabdir_name.c_str(), dp->d_name);
-        if (!read_dt_file(file_name, &value)) {
+        if (!ReadDtFile(file_name, &value)) {
             LERROR << "dt_fstab: Failed to find device for partition " << dp->d_name;
             return {};
         }
@@ -444,7 +407,7 @@
         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)) {
+        if (ReadDtFile(file_name, &value)) {
             LINFO << "dt_fstab: Using a specified mount point " << value << " for " << dp->d_name;
             mount_point = value;
         } else {
@@ -453,21 +416,21 @@
         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)) {
+        if (!ReadDtFile(file_name, &value)) {
             LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
             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)) {
+        if (!ReadDtFile(file_name, &value)) {
             LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
             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)) {
+        if (!ReadDtFile(file_name, &value)) {
             LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
             return {};
         }
@@ -487,62 +450,33 @@
     return fstab_result;
 }
 
-bool is_dt_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") {
-            return true;
+// Identify path to fstab file. Lookup is based on pattern fstab.<hardware>,
+// fstab.<hardware.platform> in folders /odm/etc, vendor/etc, or /.
+std::string GetFstabPath() {
+    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 false;
+    return "";
 }
 
-static struct fstab *fs_mgr_read_fstab_file(FILE *fstab_file)
-{
-    int cnt, entries;
+bool ReadFstabFile(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out) {
     ssize_t len;
     size_t alloc_len = 0;
     char *line = NULL;
     const char *delim = " \t";
     char *save_ptr, *p;
-    struct fstab *fstab = NULL;
-    struct fs_mgr_flag_values flag_vals;
-#define FS_OPTIONS_LEN 1024
-    char tmp_fs_options[FS_OPTIONS_LEN];
+    Fstab fstab;
 
-    entries = 0;
-    while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
-        /* if the last character is a newline, shorten the string by 1 byte */
-        if (line[len - 1] == '\n') {
-            line[len - 1] = '\0';
-        }
-        /* Skip any leading whitespace */
-        p = line;
-        while (isspace(*p)) {
-            p++;
-        }
-        /* ignore comments or empty lines */
-        if (*p == '#' || *p == '\0')
-            continue;
-        entries++;
-    }
-
-    if (!entries) {
-        LERROR << "No entries found in fstab";
-        goto err;
-    }
-
-    /* Allocate and init the fstab structure */
-    fstab = static_cast<struct fstab *>(calloc(1, sizeof(struct fstab)));
-    fstab->num_entries = entries;
-    fstab->recs = static_cast<struct fstab_rec *>(
-        calloc(fstab->num_entries, sizeof(struct fstab_rec)));
-
-    fseek(fstab_file, 0, SEEK_SET);
-
-    cnt = 0;
     while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
         /* if the last character is a newline, shorten the string by 1 byte */
         if (line[len - 1] == '\n') {
@@ -558,121 +492,67 @@
         if (*p == '#' || *p == '\0')
             continue;
 
-        /* If a non-comment entry is greater than the size we allocated, give an
-         * error and quit.  This can happen in the unlikely case the file changes
-         * between the two reads.
-         */
-        if (cnt >= entries) {
-            LERROR << "Tried to process more entries than counted";
-            break;
-        }
+        FstabEntry entry;
 
         if (!(p = strtok_r(line, delim, &save_ptr))) {
             LERROR << "Error parsing mount source";
             goto err;
         }
-        fstab->recs[cnt].blk_device = strdup(p);
+        entry.blk_device = p;
 
         if (!(p = strtok_r(NULL, delim, &save_ptr))) {
             LERROR << "Error parsing mount_point";
             goto err;
         }
-        fstab->recs[cnt].mount_point = strdup(p);
+        entry.mount_point = p;
 
         if (!(p = strtok_r(NULL, delim, &save_ptr))) {
             LERROR << "Error parsing fs_type";
             goto err;
         }
-        fstab->recs[cnt].fs_type = strdup(p);
+        entry.fs_type = p;
 
         if (!(p = strtok_r(NULL, delim, &save_ptr))) {
             LERROR << "Error parsing mount_flags";
             goto err;
         }
-        tmp_fs_options[0] = '\0';
-        fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL,
-                                       tmp_fs_options, FS_OPTIONS_LEN);
 
-        /* fs_options are optional */
-        if (tmp_fs_options[0]) {
-            fstab->recs[cnt].fs_options = strdup(tmp_fs_options);
-        } else {
-            fstab->recs[cnt].fs_options = NULL;
-        }
+        ParseMountFlags(p, &entry);
 
-        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;
-        fstab->recs[cnt].partnum = flag_vals.partnum;
-        fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
-        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_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;
-        cnt++;
+
+        ParseFsMgrFlags(p, &entry);
+
+        if (entry.fs_mgr_flags.logical) {
+            entry.logical_partition_name = entry.blk_device;
+        }
+
+        fstab.emplace_back(std::move(entry));
     }
+
+    if (fstab.empty()) {
+        LERROR << "No entries found in fstab";
+        goto err;
+    }
+
     /* If an A/B partition, modify block device to be the real block device */
-    if (!fs_mgr_update_for_slotselect(fstab)) {
+    if (!fs_mgr_update_for_slotselect(&fstab)) {
         LERROR << "Error updating for slotselect";
         goto err;
     }
     free(line);
-    return fstab;
+    *fstab_out = std::move(fstab);
+    return true;
 
 err:
     free(line);
-    if (fstab)
-        fs_mgr_free_fstab(fstab);
-    return NULL;
-}
-
-/* merges fstab entries from both a and b, then returns the merged result.
- * note that the caller should only manage the return pointer without
- * doing further memory management for the two inputs, i.e. only need to
- * 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;
-
-    int total_entries = a->num_entries + b->num_entries;
-    a->recs = static_cast<struct fstab_rec *>(realloc(
-        a->recs, total_entries * (sizeof(struct fstab_rec))));
-    if (!a->recs) {
-        LERROR << __FUNCTION__ << "(): failed to allocate fstab recs";
-        // If realloc() fails the original block is left untouched;
-        // it is not freed or moved. So we have to free both a and b here.
-        fs_mgr_free_fstab(a);
-        fs_mgr_free_fstab(b);
-        return nullptr;
-    }
-
-    for (int i = a->num_entries, j = 0; i < total_entries; i++, j++) {
-        // Copy the structs by assignment.
-        a->recs[i] = b->recs[j];
-    }
-
-    // We can't call fs_mgr_free_fstab because a->recs still references the
-    // memory allocated by strdup.
-    free(b->recs);
-    free(b->fstab_filename);
-    free(b);
-
-    a->num_entries = total_entries;
-    return a;
+    return false;
 }
 
 /* Extracts <device>s from the by-name symlinks specified in a fstab:
@@ -685,11 +565,11 @@
  *   /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> ExtraBootDevices(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);
+    for (const auto& entry : fstab) {
+        std::string blk_device = entry.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") ||
@@ -718,279 +598,254 @@
     return boot_devices;
 }
 
-struct fstab *fs_mgr_read_fstab(const char *fstab_path)
-{
-    FILE *fstab_file;
-    struct fstab *fstab;
+FstabEntry BuildGsiUserdataFstabEntry() {
+    constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
 
-    fstab_file = fopen(fstab_path, "r");
-    if (!fstab_file) {
-        PERROR << __FUNCTION__<< "(): cannot open file: '" << fstab_path << "'";
-        return nullptr;
-    }
-
-    fstab = fs_mgr_read_fstab_file(fstab_file);
-    if (fstab) {
-        fstab->fstab_filename = strdup(fstab_path);
-    } else {
-        LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << fstab_path << "'";
-    }
-
-    fclose(fstab_file);
-    return fstab;
+    FstabEntry userdata = {
+            .blk_device = "userdata_gsi",
+            .mount_point = "/data",
+            .fs_type = "ext4",
+            .flags = kFlags,
+            .reserved_size = 128 * 1024 * 1024,
+    };
+    userdata.fs_mgr_flags.wait = true;
+    userdata.fs_mgr_flags.check = true;
+    userdata.fs_mgr_flags.logical = true;
+    userdata.fs_mgr_flags.quota = true;
+    userdata.fs_mgr_flags.late_mount = true;
+    userdata.fs_mgr_flags.formattable = true;
+    return userdata;
 }
 
-/* Returns fstab entries parsed from the device tree if they
- * exist
- */
-struct fstab *fs_mgr_read_fstab_dt()
-{
-    std::string fstab_buf = read_fstab_from_dt();
+bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
+    auto iter = std::remove_if(fstab->begin(), fstab->end(),
+                               [&](const auto& entry) { return entry.mount_point == mount_point; });
+    if (iter != fstab->end()) {
+        fstab->erase(iter, fstab->end());
+        return true;
+    }
+    return false;
+}
+
+void TransformFstabForGsi(Fstab* fstab) {
+    // Inherit fstab properties for userdata.
+    FstabEntry userdata;
+    if (FstabEntry* entry = GetEntryForMountPoint(fstab, "/data")) {
+        userdata = *entry;
+        userdata.blk_device = "userdata_gsi";
+        userdata.fs_mgr_flags.logical = true;
+        userdata.fs_mgr_flags.formattable = true;
+        if (!userdata.key_dir.empty()) {
+            userdata.key_dir += "/gsi";
+        }
+    } else {
+        userdata = BuildGsiUserdataFstabEntry();
+    }
+
+    if (EraseFstabEntry(fstab, "/system")) {
+        fstab->emplace_back(BuildGsiSystemFstabEntry());
+    }
+
+    if (EraseFstabEntry(fstab, "/data")) {
+        fstab->emplace_back(userdata);
+    }
+}
+
+}  // namespace
+
+bool ReadFstabFromFile(const std::string& path, Fstab* fstab) {
+    auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+    if (!fstab_file) {
+        PERROR << __FUNCTION__ << "(): cannot open file: '" << path << "'";
+        return false;
+    }
+
+    bool is_proc_mounts = path == "/proc/mounts";
+
+    if (!ReadFstabFile(fstab_file.get(), is_proc_mounts, fstab)) {
+        LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'";
+        return false;
+    }
+    if (!is_proc_mounts && !access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
+        TransformFstabForGsi(fstab);
+    }
+
+    SkipMountingPartitions(fstab);
+
+    return true;
+}
+
+// Returns fstab entries parsed from the device tree if they exist
+bool ReadFstabFromDt(Fstab* fstab, bool log) {
+    std::string fstab_buf = ReadFstabFromDt();
     if (fstab_buf.empty()) {
-        LINFO << __FUNCTION__ << "(): failed to read fstab from dt";
-        return nullptr;
+        if (log) LINFO << __FUNCTION__ << "(): failed to read fstab from dt";
+        return false;
     }
 
     std::unique_ptr<FILE, decltype(&fclose)> fstab_file(
         fmemopen(static_cast<void*>(const_cast<char*>(fstab_buf.c_str())),
                  fstab_buf.length(), "r"), fclose);
     if (!fstab_file) {
-        PERROR << __FUNCTION__ << "(): failed to create a file stream for fstab dt";
-        return nullptr;
+        if (log) PERROR << __FUNCTION__ << "(): failed to create a file stream for fstab dt";
+        return false;
     }
 
-    struct fstab *fstab = fs_mgr_read_fstab_file(fstab_file.get());
-    if (!fstab) {
-        LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:"
-               << std::endl << fstab_buf;
-    }
-
-    return fstab;
-}
-
-/*
- * 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;
-            }
+    if (!ReadFstabFile(fstab_file.get(), false, fstab)) {
+        if (log) {
+            LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:" << std::endl
+                   << fstab_buf;
         }
+        return false;
     }
 
-    return std::string();
+    SkipMountingPartitions(fstab);
+
+    return true;
 }
 
-/*
- * loads the fstab file and combines with fstab entries passed in from device tree.
- */
-struct fstab *fs_mgr_read_fstab_default()
-{
-    std::string default_fstab;
+// For GSI to skip mounting /product and /product_services, until there are
+// well-defined interfaces between them and /system. Otherwise, the GSI flashed
+// on /system might not be able to work with /product and /product_services.
+// When they're skipped here, /system/product and /system/product_services in
+// GSI will be used.
+bool SkipMountingPartitions(Fstab* fstab) {
+    constexpr const char kSkipMountConfig[] = "/system/etc/init/config/skip_mount.cfg";
 
-    // Use different fstab paths for normal boot and recovery boot, respectively
-    if (access("/sbin/recovery", F_OK) == 0) {
-        default_fstab = "/etc/recovery.fstab";
-    } else {  // normal boot
-        default_fstab = get_fstab_path();
+    std::string skip_config;
+    auto save_errno = errno;
+    if (!ReadFileToString(kSkipMountConfig, &skip_config)) {
+        errno = save_errno;  // missing file is expected
+        return true;
     }
 
-    struct fstab* fstab = nullptr;
-    if (!default_fstab.empty()) {
-        fstab = fs_mgr_read_fstab(default_fstab.c_str());
+    for (const auto& skip_mount_point : Split(skip_config, "\n")) {
+        if (skip_mount_point.empty()) {
+            continue;
+        }
+        auto it = std::remove_if(fstab->begin(), fstab->end(),
+                                 [&skip_mount_point](const auto& entry) {
+                                     return entry.mount_point == skip_mount_point;
+                                 });
+        if (it == fstab->end()) continue;
+        fstab->erase(it, fstab->end());
+        LOG(INFO) << "Skip mounting partition: " << skip_mount_point;
+    }
+
+    return true;
+}
+
+// Loads the fstab file and combines with fstab entries passed in from device tree.
+bool ReadDefaultFstab(Fstab* fstab) {
+    Fstab dt_fstab;
+    ReadFstabFromDt(&dt_fstab, false);
+
+    *fstab = std::move(dt_fstab);
+
+    std::string default_fstab_path;
+    // Use different fstab paths for normal boot and recovery boot, respectively
+    if (access("/system/bin/recovery", F_OK) == 0) {
+        default_fstab_path = "/etc/recovery.fstab";
+    } else {  // normal boot
+        default_fstab_path = GetFstabPath();
+    }
+
+    Fstab default_fstab;
+    if (!default_fstab_path.empty()) {
+        ReadFstabFromFile(default_fstab_path, &default_fstab);
     } 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
-    return in_place_merge(fstab_dt, fstab);
-}
-
-void fs_mgr_free_fstab(struct fstab *fstab)
-{
-    int i;
-
-    if (!fstab) {
-        return;
+    for (auto&& entry : default_fstab) {
+        fstab->emplace_back(std::move(entry));
     }
 
-    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].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);
+    return !fstab->empty();
 }
 
-/* Add an entry to the fstab, and return 0 on success or -1 on error */
-int fs_mgr_add_entry(struct fstab *fstab,
-                     const char *mount_point, const char *fs_type,
-                     const char *blk_device)
-{
-    struct fstab_rec *new_fstab_recs;
-    int n = fstab->num_entries;
-
-    new_fstab_recs = (struct fstab_rec *)
-                     realloc(fstab->recs, sizeof(struct fstab_rec) * (n + 1));
-
-    if (!new_fstab_recs) {
-        return -1;
-    }
-
-    /* A new entry was added, so initialize it */
-     memset(&new_fstab_recs[n], 0, sizeof(struct fstab_rec));
-     new_fstab_recs[n].mount_point = strdup(mount_point);
-     new_fstab_recs[n].fs_type = strdup(fs_type);
-     new_fstab_recs[n].blk_device = strdup(blk_device);
-     new_fstab_recs[n].length = 0;
-
-     /* Update the fstab struct */
-     fstab->recs = new_fstab_recs;
-     fstab->num_entries++;
-
-     return 0;
-}
-
-/*
- * Returns the fstab_rec* whose mount_point is path.
- * Returns nullptr if not found.
- */
-struct fstab_rec* fs_mgr_get_entry_for_mount_point(struct fstab* fstab, const std::string& path) {
-    if (!fstab) {
+FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path) {
+    if (fstab == nullptr) {
         return nullptr;
     }
-    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];
+
+    for (auto& entry : *fstab) {
+        if (entry.mount_point == path) {
+            return &entry;
         }
     }
+
     return nullptr;
 }
 
-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, ",");
+std::set<std::string> GetBootDevices() {
+    // First check the kernel commandline, then try the device tree otherwise
+    std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
+    std::string value;
+    if (fs_mgr_get_boot_config_from_kernel_cmdline("boot_devices", &value) ||
+        ReadDtFile(dt_file_name, &value)) {
+        auto boot_devices = Split(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);
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        return {};
+    }
 
-    return {};
+    return ExtraBootDevices(fstab);
 }
 
-int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_VOLDMANAGED;
+FstabEntry BuildGsiSystemFstabEntry() {
+    // .logical_partition_name is required to look up AVB Hashtree descriptors.
+    FstabEntry system = {
+            .blk_device = "system_gsi",
+            .mount_point = "/system",
+            .fs_type = "ext4",
+            .flags = MS_RDONLY,
+            .fs_options = "barrier=1",
+            // could add more keys separated by ':'.
+            .avb_keys = "/avb/q-gsi.avbpubkey:/avb/r-gsi.avbpubkey:/avb/s-gsi.avbpubkey",
+            .logical_partition_name = "system"};
+    system.fs_mgr_flags.wait = true;
+    system.fs_mgr_flags.logical = true;
+    system.fs_mgr_flags.first_stage_mount = true;
+    return system;
 }
 
-int fs_mgr_is_nonremovable(const struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_NONREMOVABLE;
+std::string GetVerityDeviceName(const FstabEntry& entry) {
+    std::string base_device;
+    if (entry.mount_point == "/") {
+        // When using system-as-root, the device name is fixed as "vroot".
+        if (entry.fs_mgr_flags.avb) {
+            return "vroot";
+        }
+        base_device = "system";
+    } else {
+        base_device = android::base::Basename(entry.mount_point);
+    }
+    return base_device + "-verity";
 }
 
-int fs_mgr_is_verified(const struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_VERIFY;
+}  // namespace fs_mgr
+}  // namespace android
+
+// 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 = android::fs_mgr::InitAndroidDtDir();
+    return kAndroidDtDir;
 }
 
-int fs_mgr_is_avb(const struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_AVB;
-}
+bool is_dt_compatible() {
+    std::string file_name = get_android_dt_dir() + "/compatible";
+    std::string dt_value;
+    if (android::fs_mgr::ReadDtFile(file_name, &dt_value)) {
+        if (dt_value == "android,firmware") {
+            return true;
+        }
+    }
 
-int fs_mgr_is_verifyatboot(const struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_VERIFYATBOOT;
-}
-
-int fs_mgr_is_encryptable(const struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE);
-}
-
-int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_FILEENCRYPTION;
-}
-
-void fs_mgr_get_file_encryption_modes(const struct fstab_rec *fstab,
-                                      const char **contents_mode_ret,
-                                      const char **filenames_mode_ret)
-{
-    *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)
-{
-    return fstab->fs_mgr_flags & MF_FORCEFDEORFBE;
-}
-
-int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
-}
-
-int fs_mgr_is_notrim(const struct fstab_rec* fstab) {
-    return fstab->fs_mgr_flags & MF_NOTRIM;
-}
-
-int fs_mgr_is_formattable(const struct fstab_rec* fstab) {
-    return fstab->fs_mgr_flags & (MF_FORMATTABLE);
-}
-
-int fs_mgr_is_slotselect(const struct fstab_rec* fstab) {
-    return fstab->fs_mgr_flags & MF_SLOTSELECT;
-}
-
-int fs_mgr_is_nofail(const struct fstab_rec* fstab) {
-    return fstab->fs_mgr_flags & MF_NOFAIL;
-}
-
-int fs_mgr_is_latemount(const struct fstab_rec* fstab) {
-    return fstab->fs_mgr_flags & MF_LATEMOUNT;
-}
-
-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;
+    return false;
 }
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
new file mode 100644
index 0000000..05ca5fc
--- /dev/null
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -0,0 +1,1087 @@
+/*
+ * 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 <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <selinux/selinux.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
+#include <fs_mgr.h>
+#include <fs_mgr/file_wait.h>
+#include <fs_mgr_dm_linear.h>
+#include <fs_mgr_overlayfs.h>
+#include <fstab/fstab.h>
+#include <libdm/dm.h>
+#include <libgsi/libgsi.h>
+#include <liblp/builder.h>
+#include <liblp/liblp.h>
+
+#include "fs_mgr_priv.h"
+
+using namespace std::literals;
+using namespace android::dm;
+using namespace android::fs_mgr;
+
+namespace {
+
+bool fs_mgr_access(const std::string& path) {
+    auto save_errno = errno;
+    auto ret = access(path.c_str(), F_OK) == 0;
+    errno = save_errno;
+    return ret;
+}
+
+// determine if a filesystem is available
+bool fs_mgr_overlayfs_filesystem_available(const std::string& filesystem) {
+    std::string filesystems;
+    if (!android::base::ReadFileToString("/proc/filesystems", &filesystems)) return false;
+    return filesystems.find("\t" + filesystem + "\n") != std::string::npos;
+}
+
+}  // namespace
+
+#if ALLOW_ADBD_DISABLE_VERITY == 0  // If we are a user build, provide stubs
+
+Fstab fs_mgr_overlayfs_candidate_list(const Fstab&) {
+    return {};
+}
+
+bool fs_mgr_overlayfs_mount_all(Fstab*) {
+    return false;
+}
+
+std::vector<std::string> fs_mgr_overlayfs_required_devices(Fstab*) {
+    return {};
+}
+
+bool fs_mgr_overlayfs_setup(const char*, const char*, bool* change, bool) {
+    if (change) *change = false;
+    return false;
+}
+
+bool fs_mgr_overlayfs_teardown(const char*, bool* change) {
+    if (change) *change = false;
+    return false;
+}
+
+bool fs_mgr_overlayfs_is_setup() {
+    return false;
+}
+
+#else  // ALLOW_ADBD_DISABLE_VERITY == 0
+
+namespace {
+
+// list of acceptable overlayfs backing storage
+const auto kScratchMountPoint = "/mnt/scratch"s;
+const auto kCacheMountPoint = "/cache"s;
+const std::vector<const std::string> kOverlayMountPoints = {kScratchMountPoint, kCacheMountPoint};
+
+// Return true if everything is mounted, but before adb is started.  Right
+// after 'trigger load_persist_props_action' is done.
+bool fs_mgr_boot_completed() {
+    return android::base::GetBoolProperty("ro.persistent_properties.ready", false);
+}
+
+bool fs_mgr_is_dir(const std::string& path) {
+    struct stat st;
+    return !stat(path.c_str(), &st) && S_ISDIR(st.st_mode);
+}
+
+// Similar test as overlayfs workdir= validation in the kernel for read-write
+// validation, except we use fs_mgr_work.  Covers space and storage issues.
+bool fs_mgr_dir_is_writable(const std::string& path) {
+    auto test_directory = path + "/fs_mgr_work";
+    rmdir(test_directory.c_str());
+    auto ret = !mkdir(test_directory.c_str(), 0700);
+    return ret | !rmdir(test_directory.c_str());
+}
+
+// At less than 1% free space return value of false,
+// means we will try to wrap with overlayfs.
+bool fs_mgr_filesystem_has_space(const std::string& mount_point) {
+    // If we have access issues to find out space remaining, return true
+    // to prevent us trying to override with overlayfs.
+    struct statvfs vst;
+    auto save_errno = errno;
+    if (statvfs(mount_point.c_str(), &vst)) {
+        errno = save_errno;
+        return true;
+    }
+
+    static constexpr int kPercentThreshold = 1;  // 1%
+
+    return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100));
+}
+
+bool fs_mgr_overlayfs_enabled(FstabEntry* entry) {
+    // readonly filesystem, can not be mount -o remount,rw
+    // for squashfs, erofs or if free space is (near) zero making such a remount
+    // virtually useless, or if there are shared blocks that prevent remount,rw
+    if (!fs_mgr_filesystem_has_space(entry->mount_point)) {
+        return true;
+    }
+    if (entry->fs_mgr_flags.logical) {
+        fs_mgr_update_logical_partition(entry);
+    }
+    auto save_errno = errno;
+    errno = 0;
+    auto has_shared_blocks = fs_mgr_has_shared_blocks(entry->mount_point, entry->blk_device);
+    if (!has_shared_blocks && (entry->mount_point == "/system")) {
+        has_shared_blocks = fs_mgr_has_shared_blocks("/", entry->blk_device);
+    }
+    // special case for first stage init for system as root (taimen)
+    if (!has_shared_blocks && (errno == ENOENT) && (entry->blk_device == "/dev/root")) {
+        has_shared_blocks = true;
+    }
+    errno = save_errno;
+    return has_shared_blocks;
+}
+
+bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr, int level = 0) {
+    auto save_errno = errno;
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
+    if (!dir) {
+        if (errno == ENOENT) {
+            errno = save_errno;
+            return true;
+        }
+        PERROR << "opendir " << path << " depth=" << level;
+        if ((errno == EPERM) && (level != 0)) {
+            errno = save_errno;
+            return true;
+        }
+        return false;
+    }
+    dirent* entry;
+    auto ret = true;
+    while ((entry = readdir(dir.get()))) {
+        if (("."s == entry->d_name) || (".."s == entry->d_name)) continue;
+        auto file = path + "/" + entry->d_name;
+        if (entry->d_type == DT_UNKNOWN) {
+            struct stat st;
+            save_errno = errno;
+            if (!lstat(file.c_str(), &st) && (st.st_mode & S_IFDIR)) entry->d_type = DT_DIR;
+            errno = save_errno;
+        }
+        if (entry->d_type == DT_DIR) {
+            ret &= fs_mgr_rm_all(file, change, level + 1);
+            if (!rmdir(file.c_str())) {
+                if (change) *change = true;
+            } else {
+                if (errno != ENOENT) ret = false;
+                PERROR << "rmdir " << file << " depth=" << level;
+            }
+            continue;
+        }
+        if (!unlink(file.c_str())) {
+            if (change) *change = true;
+        } else {
+            if (errno != ENOENT) ret = false;
+            PERROR << "rm " << file << " depth=" << level;
+        }
+    }
+    return ret;
+}
+
+const auto kUpperName = "upper"s;
+const auto kWorkName = "work"s;
+const auto kOverlayTopDir = "/overlay"s;
+
+std::string fs_mgr_get_overlayfs_candidate(const std::string& mount_point) {
+    if (!fs_mgr_is_dir(mount_point)) return "";
+    const auto base = android::base::Basename(mount_point) + "/";
+    for (const auto& overlay_mount_point : kOverlayMountPoints) {
+        auto dir = overlay_mount_point + kOverlayTopDir + "/" + base;
+        auto upper = dir + kUpperName;
+        if (!fs_mgr_is_dir(upper)) continue;
+        auto work = dir + kWorkName;
+        if (!fs_mgr_is_dir(work)) continue;
+        if (!fs_mgr_dir_is_writable(work)) continue;
+        return dir;
+    }
+    return "";
+}
+
+const auto kLowerdirOption = "lowerdir="s;
+const auto kUpperdirOption = "upperdir="s;
+
+// default options for mount_point, returns empty string for none available.
+std::string fs_mgr_get_overlayfs_options(const std::string& mount_point) {
+    auto candidate = fs_mgr_get_overlayfs_candidate(mount_point);
+    if (candidate.empty()) return "";
+    auto ret = kLowerdirOption + mount_point + "," + kUpperdirOption + candidate + kUpperName +
+               ",workdir=" + candidate + kWorkName;
+    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kOverrideCredsRequired) {
+        ret += ",override_creds=off";
+    }
+    return ret;
+}
+
+const std::string fs_mgr_mount_point(const std::string& mount_point) {
+    if ("/"s != mount_point) return mount_point;
+    return "/system";
+}
+
+bool fs_mgr_rw_access(const std::string& path) {
+    if (path.empty()) return false;
+    auto save_errno = errno;
+    auto ret = access(path.c_str(), R_OK | W_OK) == 0;
+    errno = save_errno;
+    return ret;
+}
+
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true) {
+    Fstab fstab;
+    auto save_errno = errno;
+    if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+        return false;
+    }
+    errno = save_errno;
+    const auto lowerdir = kLowerdirOption + mount_point;
+    for (const auto& entry : fstab) {
+        if (overlay_only && "overlay" != entry.fs_type && "overlayfs" != entry.fs_type) continue;
+        if (mount_point != entry.mount_point) continue;
+        if (!overlay_only) return true;
+        const auto options = android::base::Split(entry.fs_options, ",");
+        for (const auto& opt : options) {
+            if (opt == lowerdir) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+bool fs_mgr_wants_overlayfs(FstabEntry* entry) {
+    // Don't check entries that are managed by vold.
+    if (entry->fs_mgr_flags.vold_managed || entry->fs_mgr_flags.recovery_only) return false;
+
+    // *_other doesn't want overlayfs.
+    if (entry->fs_mgr_flags.slot_select_other) return false;
+
+    // Only concerned with readonly partitions.
+    if (!(entry->flags & MS_RDONLY)) return false;
+
+    // If unbindable, do not allow overlayfs as this could expose us to
+    // security issues.  On Android, this could also be used to turn off
+    // the ability to overlay an otherwise acceptable filesystem since
+    // /system and /vendor are never bound(sic) to.
+    if (entry->flags & MS_UNBINDABLE) return false;
+
+    if (!fs_mgr_overlayfs_enabled(entry)) return false;
+
+    return true;
+}
+constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
+
+bool fs_mgr_overlayfs_setup_dir(const std::string& dir, std::string* overlay, bool* change) {
+    auto ret = true;
+    auto top = dir + kOverlayTopDir;
+    if (setfscreatecon(kOverlayfsFileContext)) {
+        ret = false;
+        PERROR << "setfscreatecon " << kOverlayfsFileContext;
+    }
+    auto save_errno = errno;
+    if (!mkdir(top.c_str(), 0755)) {
+        if (change) *change = true;
+    } else if (errno != EEXIST) {
+        ret = false;
+        PERROR << "mkdir " << top;
+    } else {
+        errno = save_errno;
+    }
+    setfscreatecon(nullptr);
+
+    if (overlay) *overlay = std::move(top);
+    return ret;
+}
+
+bool fs_mgr_overlayfs_setup_one(const std::string& overlay, const std::string& mount_point,
+                                bool* change) {
+    auto ret = true;
+    if (fs_mgr_overlayfs_already_mounted(mount_point)) return ret;
+    auto fsrec_mount_point = overlay + "/" + android::base::Basename(mount_point) + "/";
+
+    if (setfscreatecon(kOverlayfsFileContext)) {
+        ret = false;
+        PERROR << "setfscreatecon " << kOverlayfsFileContext;
+    }
+    auto save_errno = errno;
+    if (!mkdir(fsrec_mount_point.c_str(), 0755)) {
+        if (change) *change = true;
+    } else if (errno != EEXIST) {
+        ret = false;
+        PERROR << "mkdir " << fsrec_mount_point;
+    } else {
+        errno = save_errno;
+    }
+
+    save_errno = errno;
+    if (!mkdir((fsrec_mount_point + kWorkName).c_str(), 0755)) {
+        if (change) *change = true;
+    } else if (errno != EEXIST) {
+        ret = false;
+        PERROR << "mkdir " << fsrec_mount_point << kWorkName;
+    } else {
+        errno = save_errno;
+    }
+    setfscreatecon(nullptr);
+
+    auto new_context = fs_mgr_get_context(mount_point);
+    if (!new_context.empty() && setfscreatecon(new_context.c_str())) {
+        ret = false;
+        PERROR << "setfscreatecon " << new_context;
+    }
+    auto upper = fsrec_mount_point + kUpperName;
+    save_errno = errno;
+    if (!mkdir(upper.c_str(), 0755)) {
+        if (change) *change = true;
+    } else if (errno != EEXIST) {
+        ret = false;
+        PERROR << "mkdir " << upper;
+    } else {
+        errno = save_errno;
+    }
+    if (!new_context.empty()) setfscreatecon(nullptr);
+
+    return ret;
+}
+
+uint32_t fs_mgr_overlayfs_slot_number() {
+    return SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
+}
+
+const auto kPhysicalDevice = "/dev/block/by-name/"s;
+
+std::string fs_mgr_overlayfs_super_device(uint32_t slot_number) {
+    return kPhysicalDevice + fs_mgr_get_super_partition_name(slot_number);
+}
+
+bool fs_mgr_overlayfs_has_logical(const Fstab& fstab) {
+    for (const auto& entry : fstab) {
+        if (entry.fs_mgr_flags.logical) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void fs_mgr_overlayfs_umount_scratch() {
+    // Lazy umount will allow us to move on and possibly later
+    // establish a new fresh mount without requiring a reboot should
+    // the developer wish to restart.  Old references should melt
+    // away or have no data.  Main goal is to shut the door on the
+    // current overrides with an expectation of a subsequent reboot,
+    // thus any errors here are ignored.
+    umount2(kScratchMountPoint.c_str(), MNT_DETACH);
+    LINFO << "umount(" << kScratchMountPoint << ")";
+    rmdir(kScratchMountPoint.c_str());
+}
+
+// reduce 'DM_DEV_STATUS failed for scratch: No such device or address' noise
+std::string scratch_device_cache;
+
+bool fs_mgr_overlayfs_teardown_scratch(const std::string& overlay, bool* change) {
+    // umount and delete kScratchMountPoint storage if we have logical partitions
+    if (overlay != kScratchMountPoint) return true;
+    scratch_device_cache.erase();
+    auto slot_number = fs_mgr_overlayfs_slot_number();
+    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+    if (!fs_mgr_rw_access(super_device)) return true;
+
+    auto save_errno = errno;
+    if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
+        fs_mgr_overlayfs_umount_scratch();
+    }
+    auto builder = MetadataBuilder::New(super_device, slot_number);
+    if (!builder) {
+        errno = save_errno;
+        return true;
+    }
+    const auto partition_name = android::base::Basename(kScratchMountPoint);
+    if (builder->FindPartition(partition_name) == nullptr) {
+        errno = save_errno;
+        return true;
+    }
+    builder->RemovePartition(partition_name);
+    auto metadata = builder->Export();
+    if (metadata && UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
+        if (change) *change = true;
+        if (!DestroyLogicalPartition(partition_name, 0s)) return false;
+    } else {
+        LERROR << "delete partition " << overlay;
+        return false;
+    }
+    errno = save_errno;
+    return true;
+}
+
+bool fs_mgr_overlayfs_teardown_one(const std::string& overlay, const std::string& mount_point,
+                                   bool* change) {
+    const auto top = overlay + kOverlayTopDir;
+
+    if (!fs_mgr_access(top)) return fs_mgr_overlayfs_teardown_scratch(overlay, change);
+
+    auto cleanup_all = mount_point.empty();
+    const auto partition_name = android::base::Basename(mount_point);
+    const auto oldpath = top + (cleanup_all ? "" : ("/" + partition_name));
+    const auto newpath = cleanup_all ? overlay + "/." + kOverlayTopDir.substr(1) + ".teardown"
+                                     : top + "/." + partition_name + ".teardown";
+    auto ret = fs_mgr_rm_all(newpath);
+    auto save_errno = errno;
+    if (!rename(oldpath.c_str(), newpath.c_str())) {
+        if (change) *change = true;
+    } else if (errno != ENOENT) {
+        ret = false;
+        PERROR << "mv " << oldpath << " " << newpath;
+    } else {
+        errno = save_errno;
+    }
+    ret &= fs_mgr_rm_all(newpath, change);
+    save_errno = errno;
+    if (!rmdir(newpath.c_str())) {
+        if (change) *change = true;
+    } else if (errno != ENOENT) {
+        ret = false;
+        PERROR << "rmdir " << newpath;
+    } else {
+        errno = save_errno;
+    }
+    if (!cleanup_all) {
+        save_errno = errno;
+        if (!rmdir(top.c_str())) {
+            if (change) *change = true;
+            cleanup_all = true;
+        } else if (errno == ENOTEMPTY) {
+            cleanup_all = true;
+            // cleanup all if the content is all hidden (leading .)
+            std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(top.c_str()), closedir);
+            if (!dir) {
+                PERROR << "opendir " << top;
+            } else {
+                dirent* entry;
+                while ((entry = readdir(dir.get()))) {
+                    if (entry->d_name[0] != '.') {
+                        cleanup_all = false;
+                        break;
+                    }
+                }
+            }
+            errno = save_errno;
+        } else if (errno == ENOENT) {
+            cleanup_all = true;
+            errno = save_errno;
+        } else {
+            ret = false;
+            PERROR << "rmdir " << top;
+        }
+    }
+    if (cleanup_all) ret &= fs_mgr_overlayfs_teardown_scratch(overlay, change);
+    return ret;
+}
+
+bool fs_mgr_overlayfs_mount(const std::string& mount_point) {
+    auto options = fs_mgr_get_overlayfs_options(mount_point);
+    if (options.empty()) return false;
+
+    // hijack __mount() report format to help triage
+    auto report = "__mount(source=overlay,target="s + mount_point + ",type=overlay";
+    const auto opt_list = android::base::Split(options, ",");
+    for (const auto& opt : opt_list) {
+        if (android::base::StartsWith(opt, kUpperdirOption)) {
+            report = report + "," + opt;
+            break;
+        }
+    }
+    report = report + ")=";
+
+    auto ret = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_RELATIME,
+                     options.c_str());
+    if (ret) {
+        PERROR << report << ret;
+        return false;
+    } else {
+        LINFO << report << ret;
+        return true;
+    }
+}
+
+// Mount kScratchMountPoint
+bool fs_mgr_overlayfs_mount_scratch(const std::string& device_path, const std::string mnt_type,
+                                    bool readonly = false) {
+    if (readonly) {
+        if (!fs_mgr_access(device_path)) return false;
+    } else {
+        if (!fs_mgr_rw_access(device_path)) return false;
+    }
+
+    auto f2fs = fs_mgr_is_f2fs(device_path);
+    auto ext4 = fs_mgr_is_ext4(device_path);
+    if (!f2fs && !ext4) return false;
+
+    if (setfscreatecon(kOverlayfsFileContext)) {
+        PERROR << "setfscreatecon " << kOverlayfsFileContext;
+    }
+    if (mkdir(kScratchMountPoint.c_str(), 0755) && (errno != EEXIST)) {
+        PERROR << "create " << kScratchMountPoint;
+    }
+
+    FstabEntry entry;
+    entry.blk_device = device_path;
+    entry.mount_point = kScratchMountPoint;
+    entry.fs_type = mnt_type;
+    if ((mnt_type == "f2fs") && !f2fs) entry.fs_type = "ext4";
+    if ((mnt_type == "ext4") && !ext4) entry.fs_type = "f2fs";
+    entry.flags = MS_RELATIME;
+    if (readonly) {
+        entry.flags |= MS_RDONLY;
+    } else {
+        fs_mgr_set_blk_ro(device_path, false);
+    }
+    auto save_errno = errno;
+    auto mounted = fs_mgr_do_mount_one(entry) == 0;
+    if (!mounted) {
+        if ((entry.fs_type == "f2fs") && ext4) {
+            entry.fs_type = "ext4";
+            mounted = fs_mgr_do_mount_one(entry) == 0;
+        } else if ((entry.fs_type == "ext4") && f2fs) {
+            entry.fs_type = "f2fs";
+            mounted = fs_mgr_do_mount_one(entry) == 0;
+        }
+        if (!mounted) save_errno = errno;
+    }
+    setfscreatecon(nullptr);
+    if (!mounted) rmdir(kScratchMountPoint.c_str());
+    errno = save_errno;
+    return mounted;
+}
+
+const std::string kMkF2fs("/system/bin/make_f2fs");
+const std::string kMkExt4("/system/bin/mke2fs");
+
+// Only a suggestion for _first_ try during mounting
+std::string fs_mgr_overlayfs_scratch_mount_type() {
+    if (!access(kMkF2fs.c_str(), X_OK) && fs_mgr_overlayfs_filesystem_available("f2fs")) {
+        return "f2fs";
+    }
+    if (!access(kMkExt4.c_str(), X_OK) && fs_mgr_overlayfs_filesystem_available("ext4")) {
+        return "ext4";
+    }
+    return "auto";
+}
+
+std::string fs_mgr_overlayfs_scratch_device() {
+    if (!scratch_device_cache.empty()) return scratch_device_cache;
+
+    // Is this a multiple super device (retrofit)?
+    auto slot_number = fs_mgr_overlayfs_slot_number();
+    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+    auto path = fs_mgr_overlayfs_super_device(slot_number == 0);
+    if (super_device == path) {
+        // Create from within single super device;
+        auto& dm = DeviceMapper::Instance();
+        const auto partition_name = android::base::Basename(kScratchMountPoint);
+        if (!dm.GetDmDevicePathByName(partition_name, &path)) {
+            // non-DAP A/B device?
+            if (fs_mgr_access(super_device)) return "";
+            auto other_slot = fs_mgr_get_other_slot_suffix();
+            if (other_slot.empty()) return "";
+            path = kPhysicalDevice + "system" + other_slot;
+        }
+    }
+    return scratch_device_cache = path;
+}
+
+bool fs_mgr_overlayfs_make_scratch(const std::string& scratch_device, const std::string& mnt_type) {
+    // Force mkfs by design for overlay support of adb remount, simplify and
+    // thus do not rely on fsck to correct problems that could creep in.
+    auto command = ""s;
+    if (mnt_type == "f2fs") {
+        command = kMkF2fs + " -w 4096 -f -d1 -l" + android::base::Basename(kScratchMountPoint);
+    } else if (mnt_type == "ext4") {
+        command = kMkExt4 + " -F -b 4096 -t ext4 -m 0 -O has_journal -M " + kScratchMountPoint;
+    } else {
+        errno = ESRCH;
+        LERROR << mnt_type << " has no mkfs cookbook";
+        return false;
+    }
+    command += " " + scratch_device + " >/dev/null 2>/dev/null </dev/null";
+    fs_mgr_set_blk_ro(scratch_device, false);
+    auto ret = system(command.c_str());
+    if (ret) {
+        LERROR << "make " << mnt_type << " filesystem on " << scratch_device << " return=" << ret;
+        return false;
+    }
+    return true;
+}
+
+static void TruncatePartitionsWithSuffix(MetadataBuilder* builder, const std::string& suffix) {
+    auto& dm = DeviceMapper::Instance();
+
+    // Remove <other> partitions
+    for (const auto& group : builder->ListGroups()) {
+        for (const auto& part : builder->ListPartitionsInGroup(group)) {
+            const auto& name = part->name();
+            if (!android::base::EndsWith(name, suffix)) {
+                continue;
+            }
+            if (dm.GetState(name) != DmDeviceState::INVALID && !DestroyLogicalPartition(name, 2s)) {
+                continue;
+            }
+            builder->ResizePartition(builder->FindPartition(name), 0);
+        }
+    }
+}
+
+// This is where we find and steal backing storage from the system.
+bool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,
+                                     bool* partition_exists, bool* change) {
+    *scratch_device = fs_mgr_overlayfs_scratch_device();
+    *partition_exists = fs_mgr_rw_access(*scratch_device);
+    auto partition_create = !*partition_exists;
+    // Do we need to create a logical "scratch" partition?
+    if (!partition_create && android::base::StartsWith(*scratch_device, kPhysicalDevice)) {
+        return true;
+    }
+    auto slot_number = fs_mgr_overlayfs_slot_number();
+    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+    if (!fs_mgr_rw_access(super_device)) return false;
+    if (!fs_mgr_overlayfs_has_logical(fstab)) return false;
+    auto builder = MetadataBuilder::New(super_device, slot_number);
+    if (!builder) {
+        LERROR << "open " << super_device << " metadata";
+        return false;
+    }
+    const auto partition_name = android::base::Basename(kScratchMountPoint);
+    auto partition = builder->FindPartition(partition_name);
+    *partition_exists = partition != nullptr;
+    auto changed = false;
+    if (!*partition_exists) {
+        partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);
+        if (!partition) {
+            LERROR << "create " << partition_name;
+            return false;
+        }
+        changed = true;
+    }
+    // Take half of free space, minimum 512MB or maximum free - margin.
+    static constexpr auto kMinimumSize = uint64_t(512 * 1024 * 1024);
+    if (partition->size() < kMinimumSize) {
+        auto partition_size =
+                builder->AllocatableSpace() - builder->UsedSpace() + partition->size();
+        if ((partition_size > kMinimumSize) || !partition->size()) {
+            // Leave some space for free space jitter of a few erase
+            // blocks, in case they are needed for any individual updates
+            // to any other partition that needs to be flashed while
+            // overlayfs is in force.  Of course if margin_size is not
+            // enough could normally get a flash failure, so
+            // ResizePartition() will delete the scratch partition in
+            // order to fulfill.  Deleting scratch will destroy all of
+            // the adb remount overrides :-( .
+            auto margin_size = uint64_t(3 * 256 * 1024);
+            BlockDeviceInfo info;
+            if (builder->GetBlockDeviceInfo(fs_mgr_get_super_partition_name(slot_number), &info)) {
+                margin_size = 3 * info.logical_block_size;
+            }
+            partition_size = std::max(std::min(kMinimumSize, partition_size - margin_size),
+                                      partition_size / 2);
+            if (partition_size > partition->size()) {
+                if (!builder->ResizePartition(partition, partition_size)) {
+                    // Try to free up space by deallocating partitions in the other slot.
+                    TruncatePartitionsWithSuffix(builder.get(), fs_mgr_get_other_slot_suffix());
+
+                    partition_size =
+                            builder->AllocatableSpace() - builder->UsedSpace() + partition->size();
+                    partition_size = std::max(std::min(kMinimumSize, partition_size - margin_size),
+                                              partition_size / 2);
+                    if (!builder->ResizePartition(partition, partition_size)) {
+                        LERROR << "resize " << partition_name;
+                        return false;
+                    }
+                }
+                if (!partition_create) DestroyLogicalPartition(partition_name, 10s);
+                changed = true;
+                *partition_exists = false;
+            }
+        }
+    }
+    // land the update back on to the partition
+    if (changed) {
+        auto metadata = builder->Export();
+        if (!metadata || !UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
+            LERROR << "add partition " << partition_name;
+            return false;
+        }
+
+        if (change) *change = true;
+    }
+
+    if (changed || partition_create) {
+        if (!CreateLogicalPartition(super_device, slot_number, partition_name, true, 10s,
+                                    scratch_device))
+            return false;
+
+        if (change) *change = true;
+    }
+    return true;
+}
+
+// Create and mount kScratchMountPoint storage if we have logical partitions
+bool fs_mgr_overlayfs_setup_scratch(const Fstab& fstab, bool* change) {
+    if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;
+
+    std::string scratch_device;
+    bool partition_exists;
+    if (!fs_mgr_overlayfs_create_scratch(fstab, &scratch_device, &partition_exists, change)) {
+        return false;
+    }
+
+    // If the partition exists, assume first that it can be mounted.
+    auto mnt_type = fs_mgr_overlayfs_scratch_mount_type();
+    if (partition_exists) {
+        if (fs_mgr_overlayfs_mount_scratch(scratch_device, mnt_type)) {
+            if (!fs_mgr_access(kScratchMountPoint + kOverlayTopDir) &&
+                !fs_mgr_filesystem_has_space(kScratchMountPoint)) {
+                // declare it useless, no overrides and no free space
+                fs_mgr_overlayfs_umount_scratch();
+            } else {
+                if (change) *change = true;
+                return true;
+            }
+        }
+        // partition existed, but was not initialized; fall through to make it.
+        errno = 0;
+    }
+
+    if (!fs_mgr_overlayfs_make_scratch(scratch_device, mnt_type)) return false;
+
+    if (change) *change = true;
+
+    return fs_mgr_overlayfs_mount_scratch(scratch_device, mnt_type);
+}
+
+bool fs_mgr_overlayfs_scratch_can_be_mounted(const std::string& scratch_device) {
+    if (scratch_device.empty()) return false;
+    if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return false;
+    if (android::base::StartsWith(scratch_device, kPhysicalDevice)) return true;
+    if (fs_mgr_rw_access(scratch_device)) return true;
+    auto slot_number = fs_mgr_overlayfs_slot_number();
+    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+    if (!fs_mgr_rw_access(super_device)) return false;
+    auto builder = MetadataBuilder::New(super_device, slot_number);
+    if (!builder) return false;
+    return builder->FindPartition(android::base::Basename(kScratchMountPoint)) != nullptr;
+}
+
+bool fs_mgr_overlayfs_invalid() {
+    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return true;
+
+    // in recovery, fastbootd, or gsi mode, not allowed!
+    if (fs_mgr_access("/system/bin/recovery")) return true;
+    auto save_errno = errno;
+    auto ret = android::gsi::IsGsiRunning();
+    errno = save_errno;
+    return ret;
+}
+
+}  // namespace
+
+Fstab fs_mgr_overlayfs_candidate_list(const Fstab& fstab) {
+    Fstab candidates;
+    for (const auto& entry : fstab) {
+        FstabEntry new_entry = entry;
+        if (!fs_mgr_overlayfs_already_mounted(entry.mount_point) &&
+            !fs_mgr_wants_overlayfs(&new_entry)) {
+            continue;
+        }
+        auto new_mount_point = fs_mgr_mount_point(entry.mount_point);
+        auto duplicate_or_more_specific = false;
+        for (auto it = candidates.begin(); it != candidates.end();) {
+            auto it_mount_point = fs_mgr_mount_point(it->mount_point);
+            if ((it_mount_point == new_mount_point) ||
+                (android::base::StartsWith(new_mount_point, it_mount_point + "/"))) {
+                duplicate_or_more_specific = true;
+                break;
+            }
+            if (android::base::StartsWith(it_mount_point, new_mount_point + "/")) {
+                it = candidates.erase(it);
+            } else {
+                ++it;
+            }
+        }
+        if (!duplicate_or_more_specific) candidates.emplace_back(std::move(new_entry));
+    }
+    return candidates;
+}
+
+bool fs_mgr_overlayfs_mount_all(Fstab* fstab) {
+    auto ret = false;
+    if (fs_mgr_overlayfs_invalid()) return ret;
+
+    auto scratch_can_be_mounted = true;
+    for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
+        if (fs_mgr_is_verity_enabled(entry)) continue;
+        auto mount_point = fs_mgr_mount_point(entry.mount_point);
+        if (fs_mgr_overlayfs_already_mounted(mount_point)) {
+            ret = true;
+            continue;
+        }
+        if (scratch_can_be_mounted) {
+            scratch_can_be_mounted = false;
+            auto scratch_device = fs_mgr_overlayfs_scratch_device();
+            if (fs_mgr_overlayfs_scratch_can_be_mounted(scratch_device) &&
+                WaitForFile(scratch_device, 10s)) {
+                const auto mount_type = fs_mgr_overlayfs_scratch_mount_type();
+                if (fs_mgr_overlayfs_mount_scratch(scratch_device, mount_type,
+                                                   true /* readonly */)) {
+                    auto has_overlayfs_dir = fs_mgr_access(kScratchMountPoint + kOverlayTopDir);
+                    fs_mgr_overlayfs_umount_scratch();
+                    if (has_overlayfs_dir) {
+                        fs_mgr_overlayfs_mount_scratch(scratch_device, mount_type);
+                    }
+                }
+            }
+        }
+        if (fs_mgr_overlayfs_mount(mount_point)) ret = true;
+    }
+    return ret;
+}
+
+std::vector<std::string> fs_mgr_overlayfs_required_devices(Fstab* fstab) {
+    if (fs_mgr_overlayfs_invalid()) return {};
+
+    if (GetEntryForMountPoint(fstab, kScratchMountPoint) != nullptr) {
+        return {};
+    }
+
+    for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
+        if (fs_mgr_is_verity_enabled(entry)) continue;
+        if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) continue;
+        auto device = fs_mgr_overlayfs_scratch_device();
+        if (!fs_mgr_overlayfs_scratch_can_be_mounted(device)) break;
+        return {device};
+    }
+    return {};
+}
+
+// Returns false if setup not permitted, errno set to last error.
+// If something is altered, set *change.
+bool fs_mgr_overlayfs_setup(const char* backing, const char* mount_point, bool* change,
+                            bool force) {
+    if (change) *change = false;
+    auto ret = false;
+    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return ret;
+    if (!fs_mgr_boot_completed()) {
+        errno = EBUSY;
+        PERROR << "setup";
+        return ret;
+    }
+
+    auto save_errno = errno;
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        return false;
+    }
+    errno = save_errno;
+    auto candidates = fs_mgr_overlayfs_candidate_list(fstab);
+    for (auto it = candidates.begin(); it != candidates.end();) {
+        if (mount_point &&
+            (fs_mgr_mount_point(it->mount_point) != fs_mgr_mount_point(mount_point))) {
+            it = candidates.erase(it);
+            continue;
+        }
+        save_errno = errno;
+        auto verity_enabled = !force && fs_mgr_is_verity_enabled(*it);
+        if (errno == ENOENT || errno == ENXIO) errno = save_errno;
+        if (verity_enabled) {
+            it = candidates.erase(it);
+            continue;
+        }
+        ++it;
+    }
+
+    if (candidates.empty()) return ret;
+
+    std::string dir;
+    for (const auto& overlay_mount_point : kOverlayMountPoints) {
+        if (backing && backing[0] && (overlay_mount_point != backing)) continue;
+        if (overlay_mount_point == kScratchMountPoint) {
+            if (!fs_mgr_overlayfs_setup_scratch(fstab, change)) continue;
+        } else {
+            if (GetEntryForMountPoint(&fstab, overlay_mount_point) == nullptr) {
+                continue;
+            }
+        }
+        dir = overlay_mount_point;
+        break;
+    }
+    if (dir.empty()) {
+        if (change && *change) errno = ESRCH;
+        if (errno == EPERM) errno = save_errno;
+        return ret;
+    }
+
+    std::string overlay;
+    ret |= fs_mgr_overlayfs_setup_dir(dir, &overlay, change);
+    for (const auto& entry : candidates) {
+        ret |= fs_mgr_overlayfs_setup_one(overlay, fs_mgr_mount_point(entry.mount_point), change);
+    }
+    return ret;
+}
+
+// Returns false if teardown not permitted, errno set to last error.
+// If something is altered, set *change.
+bool fs_mgr_overlayfs_teardown(const char* mount_point, bool* change) {
+    if (change) *change = false;
+    auto ret = true;
+    // If scratch exists, but is not mounted, lets gain access to clean
+    // specific override entries.
+    auto mount_scratch = false;
+    if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
+        auto scratch_device = fs_mgr_overlayfs_scratch_device();
+        if (scratch_device.empty()) {
+            auto slot_number = fs_mgr_overlayfs_slot_number();
+            auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+            const auto partition_name = android::base::Basename(kScratchMountPoint);
+            CreateLogicalPartition(super_device, slot_number, partition_name, true, 10s,
+                                   &scratch_device);
+        }
+        mount_scratch = fs_mgr_overlayfs_mount_scratch(scratch_device,
+                                                       fs_mgr_overlayfs_scratch_mount_type());
+    }
+    for (const auto& overlay_mount_point : kOverlayMountPoints) {
+        ret &= fs_mgr_overlayfs_teardown_one(
+                overlay_mount_point, mount_point ? fs_mgr_mount_point(mount_point) : "", change);
+    }
+    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
+        // After obligatory teardown to make sure everything is clean, but if
+        // we didn't want overlayfs in the the first place, we do not want to
+        // waste time on a reboot (or reboot request message).
+        if (change) *change = false;
+    }
+    // And now that we did what we could, lets inform
+    // caller that there may still be more to do.
+    if (!fs_mgr_boot_completed()) {
+        errno = EBUSY;
+        PERROR << "teardown";
+        ret = false;
+    }
+    if (mount_scratch) fs_mgr_overlayfs_umount_scratch();
+
+    return ret;
+}
+
+bool fs_mgr_overlayfs_is_setup() {
+    if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        return false;
+    }
+    if (fs_mgr_overlayfs_invalid()) return false;
+    for (const auto& entry : fs_mgr_overlayfs_candidate_list(fstab)) {
+        if (fs_mgr_is_verity_enabled(entry)) continue;
+        if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) return true;
+    }
+    return false;
+}
+
+#endif  // ALLOW_ADBD_DISABLE_VERITY != 0
+
+bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev) {
+    struct statfs fs;
+    if ((statfs((mount_point + "/lost+found").c_str(), &fs) == -1) ||
+        (fs.f_type != EXT4_SUPER_MAGIC)) {
+        return false;
+    }
+
+    android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_CLOEXEC));
+    if (fd < 0) return false;
+
+    struct ext4_super_block sb;
+    if ((TEMP_FAILURE_RETRY(lseek64(fd, 1024, SEEK_SET)) < 0) ||
+        (TEMP_FAILURE_RETRY(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;
+}
+
+std::string fs_mgr_get_context(const std::string& mount_point) {
+    char* ctx = nullptr;
+    if (getfilecon(mount_point.c_str(), &ctx) == -1) {
+        return "";
+    }
+
+    std::string context(ctx);
+    free(ctx);
+    return context;
+}
+
+OverlayfsValidResult fs_mgr_overlayfs_valid() {
+    // Overlayfs available in the kernel, and patched for override_creds?
+    if (fs_mgr_access("/sys/module/overlay/parameters/override_creds")) {
+        return OverlayfsValidResult::kOverrideCredsRequired;
+    }
+    if (!fs_mgr_overlayfs_filesystem_available("overlay")) {
+        return OverlayfsValidResult::kNotSupported;
+    }
+    struct utsname uts;
+    if (uname(&uts) == -1) {
+        return OverlayfsValidResult::kNotSupported;
+    }
+    int major, minor;
+    if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
+        return OverlayfsValidResult::kNotSupported;
+    }
+    if (major < 4) {
+        return OverlayfsValidResult::kOk;
+    }
+    if (major > 4) {
+        return OverlayfsValidResult::kNotSupported;
+    }
+    if (minor > 3) {
+        return OverlayfsValidResult::kNotSupported;
+    }
+    return OverlayfsValidResult::kOk;
+}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 9011bb3..3a33cf3 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef __CORE_FS_MGR_PRIV_H
-#define __CORE_FS_MGR_PRIV_H
+#pragma once
 
 #include <chrono>
 #include <string>
 
 #include <android-base/logging.h>
+#include <fs_mgr.h>
+#include <fstab/fstab.h>
 
-#include "fs_mgr.h"
 #include "fs_mgr_priv_boot_config.h"
 
 /* The CHECK() in logging.h will use program invocation name as the tag.
@@ -39,11 +39,13 @@
 #define LINFO    LOG(INFO) << FS_MGR_TAG
 #define LWARNING LOG(WARNING) << FS_MGR_TAG
 #define LERROR   LOG(ERROR) << FS_MGR_TAG
+#define LFATAL LOG(FATAL) << FS_MGR_TAG
 
 // Logs a message with strerror(errno) at the end
 #define PINFO    PLOG(INFO) << FS_MGR_TAG
 #define PWARNING PLOG(WARNING) << FS_MGR_TAG
 #define PERROR   PLOG(ERROR) << FS_MGR_TAG
+#define PFATAL PLOG(FATAL) << FS_MGR_TAG
 
 #define CRYPTO_TMPFS_OPTIONS "size=512m,mode=0771,uid=1000,gid=1000"
 
@@ -82,47 +84,23 @@
  *
  */
 
-#define MF_WAIT                  0x1
-#define MF_CHECK                 0x2
-#define MF_CRYPT                 0x4
-#define MF_NONREMOVABLE          0x8
-#define MF_VOLDMANAGED          0x10
-#define MF_LENGTH               0x20
-#define MF_RECOVERYONLY         0x40
-#define MF_SWAPPRIO             0x80
-#define MF_ZRAMSIZE            0x100
-#define MF_VERIFY              0x200
-#define MF_FORCECRYPT          0x400
-#define MF_NOEMULATEDSD        0x800 /* no emulated sdcard daemon, sd card is the only
-                                        external storage */
-#define MF_NOTRIM             0x1000
-#define MF_FILEENCRYPTION     0x2000
-#define MF_FORMATTABLE        0x4000
-#define MF_SLOTSELECT         0x8000
-#define MF_FORCEFDEORFBE     0x10000
-#define MF_LATEMOUNT         0x20000
-#define MF_NOFAIL            0x40000
-#define MF_VERIFYATBOOT      0x80000
-#define MF_MAX_COMP_STREAMS 0x100000
-#define MF_RESERVEDSIZE     0x200000
-#define MF_QUOTA            0x400000
-#define MF_ERASEBLKSIZE     0x800000
-#define MF_LOGICALBLKSIZE  0X1000000
-#define MF_AVB             0X2000000
-#define MF_KEYDIRECTORY    0X4000000
-#define MF_SYSFS           0X8000000
-
 #define DM_BUF_SIZE 4096
 
 using namespace std::chrono_literals;
 
-int fs_mgr_set_blk_ro(const char *blockdev);
-bool fs_mgr_wait_for_file(const std::string& filename,
-                          const std::chrono::milliseconds relative_timeout);
-bool fs_mgr_update_for_slotselect(struct fstab *fstab);
+bool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly = true);
+bool fs_mgr_update_for_slotselect(android::fs_mgr::Fstab* fstab);
 bool fs_mgr_is_device_unlocked();
 const std::string& get_android_dt_dir();
 bool is_dt_compatible();
-int load_verity_state(struct fstab_rec* fstab, int* mode);
 
-#endif /* __CORE_FS_MGR_PRIV_H */
+bool fs_mgr_is_ext4(const std::string& blk_device);
+bool fs_mgr_is_f2fs(const std::string& blk_device);
+
+bool fs_mgr_teardown_verity(android::fs_mgr::FstabEntry* fstab, bool wait);
+
+namespace android {
+namespace fs_mgr {
+bool UnmapDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms);
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/fs_mgr_priv_avb_ops.h b/fs_mgr/fs_mgr_priv_avb_ops.h
deleted file mode 100644
index d1ef2e9..0000000
--- a/fs_mgr/fs_mgr_priv_avb_ops.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2016 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_PRIV_AVB_OPS_H
-#define __CORE_FS_MGR_PRIV_AVB_OPS_H
-
-#include <map>
-#include <string>
-
-#include <libavb/libavb.h>
-
-#include "fs_mgr.h"
-
-// This class provides C++ bindings to interact with libavb, a small
-// self-contained piece of code that's intended to be used in bootloaders.
-// It mainly contains two functions:
-//   - ReadFromPartition(): to read AVB metadata from a given partition.
-//     It provides the implementation of AvbOps.read_from_partition() when
-//     reading metadata through libavb.
-//   - AvbSlotVerify(): the C++ binding of libavb->avb_slot_verify() to
-//     read and verify the metadata and store it into the out_data parameter.
-//     The caller MUST check the integrity of metadata against the
-//     androidboot.vbmeta.{hash_alg, size, digest} values from /proc/cmdline.
-//     e.g., see class FsManagerAvbVerifier for more details.
-//
-class FsManagerAvbOps {
-  public:
-    FsManagerAvbOps(const fstab& fstab);
-    FsManagerAvbOps(std::map<std::string, std::string>&& by_name_symlink_map);
-
-    static FsManagerAvbOps* GetInstanceFromAvbOps(AvbOps* ops) {
-        return reinterpret_cast<FsManagerAvbOps*>(ops->user_data);
-    }
-
-    AvbIOResult ReadFromPartition(const char* partition, int64_t offset, size_t num_bytes,
-                                  void* buffer, size_t* out_num_read);
-
-    AvbSlotVerifyResult AvbSlotVerify(const std::string& ab_suffix, AvbSlotVerifyFlags flags,
-                                      AvbSlotVerifyData** out_data);
-
-  private:
-    void InitializeAvbOps();
-
-    AvbOps avb_ops_;
-    std::map<std::string, std::string> by_name_symlink_map_;
-};
-#endif /* __CORE_FS_MGR_PRIV_AVB_OPS_H */
diff --git a/fs_mgr/fs_mgr_priv_boot_config.h b/fs_mgr/fs_mgr_priv_boot_config.h
index d98dc02..417fb38 100644
--- a/fs_mgr/fs_mgr_priv_boot_config.h
+++ b/fs_mgr/fs_mgr_priv_boot_config.h
@@ -19,7 +19,13 @@
 
 #include <sys/cdefs.h>
 #include <string>
+#include <utility>
+#include <vector>
 
+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);
 
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_priv_sha.h b/fs_mgr/fs_mgr_priv_sha.h
deleted file mode 100644
index 5b53eea..0000000
--- a/fs_mgr/fs_mgr_priv_sha.h
+++ /dev/null
@@ -1,62 +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_SHA_H
-#define __CORE_FS_MGR_PRIV_SHA_H
-
-#include <openssl/sha.h>
-
-class SHA256Hasher {
-  private:
-    SHA256_CTX sha256_ctx;
-    uint8_t hash[SHA256_DIGEST_LENGTH];
-
-  public:
-    enum { DIGEST_SIZE = SHA256_DIGEST_LENGTH };
-
-    SHA256Hasher() { SHA256_Init(&sha256_ctx); }
-
-    void update(const uint8_t* data, size_t data_size) {
-        SHA256_Update(&sha256_ctx, data, data_size);
-    }
-
-    const uint8_t* finalize() {
-        SHA256_Final(hash, &sha256_ctx);
-        return hash;
-    }
-};
-
-class SHA512Hasher {
-  private:
-    SHA512_CTX sha512_ctx;
-    uint8_t hash[SHA512_DIGEST_LENGTH];
-
-  public:
-    enum { DIGEST_SIZE = SHA512_DIGEST_LENGTH };
-
-    SHA512Hasher() { SHA512_Init(&sha512_ctx); }
-
-    void update(const uint8_t* data, size_t data_size) {
-        SHA512_Update(&sha512_ctx, data, data_size);
-    }
-
-    const uint8_t* finalize() {
-        SHA512_Final(hash, &sha512_ctx);
-        return hash;
-    }
-};
-
-#endif /* __CORE_FS_MGR_PRIV_SHA_H */
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
new file mode 100644
index 0000000..149bee3
--- /dev/null
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <getopt.h>
+#include <libavb_user/libavb_user.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <bootloader_message/bootloader_message.h>
+#include <cutils/android_reboot.h>
+#include <fec/io.h>
+#include <fs_mgr_overlayfs.h>
+#include <fs_mgr_priv.h>
+#include <fstab/fstab.h>
+
+namespace {
+
+[[noreturn]] void usage(int exit_status) {
+    LOG(INFO) << getprogname()
+              << " [-h] [-R] [-T fstab_file] [partition]...\n"
+                 "\t-h --help\tthis help\n"
+                 "\t-R --reboot\tdisable verity & reboot to facilitate remount\n"
+                 "\t-T --fstab\tcustom fstab file location\n"
+                 "\tpartition\tspecific partition(s) (empty does all)\n"
+                 "\n"
+                 "Remount specified partition(s) read-write, by name or mount point.\n"
+                 "-R notwithstanding, verity must be disabled on partition(s).";
+
+    ::exit(exit_status);
+}
+
+bool remountable_partition(const android::fs_mgr::FstabEntry& entry) {
+    if (entry.fs_mgr_flags.vold_managed) return false;
+    if (entry.fs_mgr_flags.recovery_only) return false;
+    if (entry.fs_mgr_flags.slot_select_other) return false;
+    if (!(entry.flags & MS_RDONLY)) return false;
+    if (entry.fs_type == "vfat") return false;
+    return true;
+}
+
+const std::string system_mount_point(const android::fs_mgr::FstabEntry& entry) {
+    if (entry.mount_point == "/") return "/system";
+    return entry.mount_point;
+}
+
+const android::fs_mgr::FstabEntry* is_wrapped(const android::fs_mgr::Fstab& overlayfs_candidates,
+                                              const android::fs_mgr::FstabEntry& entry) {
+    auto mount_point = system_mount_point(entry);
+    auto it = std::find_if(overlayfs_candidates.begin(), overlayfs_candidates.end(),
+                           [&mount_point](const auto& entry) {
+                               return android::base::StartsWith(mount_point,
+                                                                system_mount_point(entry) + "/");
+                           });
+    if (it == overlayfs_candidates.end()) return nullptr;
+    return &(*it);
+}
+
+void MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
+              const char* file, unsigned int line, const char* message) {
+    static const char log_characters[] = "VD\0WEFF";
+    if (severity < sizeof(log_characters)) {
+        auto severity_char = log_characters[severity];
+        if (severity_char) fprintf(stderr, "%c ", severity_char);
+    }
+    fprintf(stderr, "%s\n", message);
+
+    static auto logd = android::base::LogdLogger();
+    logd(id, severity, tag, file, line, message);
+}
+
+[[noreturn]] void reboot(bool overlayfs = false) {
+    if (overlayfs) {
+        LOG(INFO) << "Successfully setup overlayfs\nrebooting device";
+    } else {
+        LOG(INFO) << "Successfully disabled verity\nrebooting device";
+    }
+    ::sync();
+    android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,remount");
+    ::sleep(60);
+    ::exit(0);  // SUCCESS
+}
+
+}  // namespace
+
+int main(int argc, char* argv[]) {
+    android::base::InitLogging(argv, MyLogger);
+
+    enum {
+        SUCCESS,
+        NOT_USERDEBUG,
+        BADARG,
+        NOT_ROOT,
+        NO_FSTAB,
+        UNKNOWN_PARTITION,
+        INVALID_PARTITION,
+        VERITY_PARTITION,
+        BAD_OVERLAY,
+        NO_MOUNTS,
+        REMOUNT_FAILED,
+    } retval = SUCCESS;
+
+    // If somehow this executable is delivered on a "user" build, it can
+    // not function, so providing a clear message to the caller rather than
+    // letting if fall through and provide a lot of confusing failure messages.
+    if (!ALLOW_ADBD_DISABLE_VERITY || (android::base::GetProperty("ro.debuggable", "0") != "1")) {
+        LOG(ERROR) << "only functions on userdebug or eng builds";
+        return NOT_USERDEBUG;
+    }
+
+    const char* fstab_file = nullptr;
+    auto can_reboot = false;
+
+    struct option longopts[] = {
+            {"fstab", required_argument, nullptr, 'T'},
+            {"help", no_argument, nullptr, 'h'},
+            {"reboot", no_argument, nullptr, 'R'},
+            {0, 0, nullptr, 0},
+    };
+    for (int opt; (opt = ::getopt_long(argc, argv, "hRT:", longopts, nullptr)) != -1;) {
+        switch (opt) {
+            case 'R':
+                can_reboot = true;
+                break;
+            case 'T':
+                if (fstab_file) {
+                    LOG(ERROR) << "Cannot supply two fstabs: -T " << fstab_file << " -T" << optarg;
+                    usage(BADARG);
+                }
+                fstab_file = optarg;
+                break;
+            default:
+                LOG(ERROR) << "Bad Argument -" << char(opt);
+                usage(BADARG);
+                break;
+            case 'h':
+                usage(SUCCESS);
+                break;
+        }
+    }
+
+    // Make sure we are root.
+    if (::getuid() != 0) {
+        LOG(ERROR) << "must be run as root";
+        return NOT_ROOT;
+    }
+
+    // Read the selected fstab.
+    android::fs_mgr::Fstab fstab;
+    auto fstab_read = false;
+    if (fstab_file) {
+        fstab_read = android::fs_mgr::ReadFstabFromFile(fstab_file, &fstab);
+    } else {
+        fstab_read = android::fs_mgr::ReadDefaultFstab(&fstab);
+        // Manufacture a / entry from /proc/mounts if missing.
+        if (!GetEntryForMountPoint(&fstab, "/system") && !GetEntryForMountPoint(&fstab, "/")) {
+            android::fs_mgr::Fstab mounts;
+            if (android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts)) {
+                if (auto entry = GetEntryForMountPoint(&mounts, "/")) {
+                    if (entry->fs_type != "rootfs") fstab.emplace_back(*entry);
+                }
+            }
+        }
+    }
+    if (!fstab_read || fstab.empty()) {
+        PLOG(ERROR) << "Failed to read fstab";
+        return NO_FSTAB;
+    }
+
+    // Generate the list of supported overlayfs mount points.
+    auto overlayfs_candidates = fs_mgr_overlayfs_candidate_list(fstab);
+
+    // Generate the all remountable partitions sub-list
+    android::fs_mgr::Fstab all;
+    for (auto const& entry : fstab) {
+        if (!remountable_partition(entry)) continue;
+        if (overlayfs_candidates.empty() ||
+            GetEntryForMountPoint(&overlayfs_candidates, entry.mount_point) ||
+            (is_wrapped(overlayfs_candidates, entry) == nullptr)) {
+            all.emplace_back(entry);
+        }
+    }
+
+    // Parse the unique list of valid partition arguments.
+    android::fs_mgr::Fstab partitions;
+    for (; argc > optind; ++optind) {
+        auto partition = std::string(argv[optind]);
+        if (partition.empty()) continue;
+        if (partition == "/") partition = "/system";
+        auto find_part = [&partition](const auto& entry) {
+            const auto mount_point = system_mount_point(entry);
+            if (partition == mount_point) return true;
+            if (partition == android::base::Basename(mount_point)) return true;
+            return false;
+        };
+        // Do we know about the partition?
+        auto it = std::find_if(fstab.begin(), fstab.end(), find_part);
+        if (it == fstab.end()) {
+            LOG(ERROR) << "Unknown partition " << argv[optind] << ", skipping";
+            retval = UNKNOWN_PARTITION;
+            continue;
+        }
+        // Is that one covered by an existing overlayfs?
+        auto wrap = is_wrapped(overlayfs_candidates, *it);
+        if (wrap) {
+            LOG(INFO) << "partition " << argv[optind] << " covered by overlayfs for "
+                      << wrap->mount_point << ", switching";
+            partition = system_mount_point(*wrap);
+        }
+        // Is it a remountable partition?
+        it = std::find_if(all.begin(), all.end(), find_part);
+        if (it == all.end()) {
+            LOG(ERROR) << "Invalid partition " << argv[optind] << ", skipping";
+            retval = INVALID_PARTITION;
+            continue;
+        }
+        if (GetEntryForMountPoint(&partitions, it->mount_point) == nullptr) {
+            partitions.emplace_back(*it);
+        }
+    }
+
+    if (partitions.empty() && !retval) {
+        partitions = all;
+    }
+
+    // Check verity and optionally setup overlayfs backing.
+    auto reboot_later = false;
+    auto user_please_reboot_later = false;
+    auto setup_overlayfs = false;
+    auto just_disabled_verity = false;
+    for (auto it = partitions.begin(); it != partitions.end();) {
+        auto& entry = *it;
+        auto& mount_point = entry.mount_point;
+        if (fs_mgr_is_verity_enabled(entry)) {
+            retval = VERITY_PARTITION;
+            auto ret = false;
+            if (android::base::GetProperty("ro.boot.vbmeta.device_state", "") != "locked") {
+                if (AvbOps* ops = avb_ops_user_new()) {
+                    ret = avb_user_verity_set(
+                            ops, android::base::GetProperty("ro.boot.slot_suffix", "").c_str(),
+                            false);
+                    avb_ops_user_free(ops);
+                }
+                if (!ret && fs_mgr_set_blk_ro(entry.blk_device, false)) {
+                    fec::io fh(entry.blk_device.c_str(), O_RDWR);
+                    ret = fh && fh.set_verity_status(false);
+                }
+                if (ret) {
+                    LOG(WARNING) << "Disabling verity for " << mount_point;
+                    just_disabled_verity = true;
+                    reboot_later = can_reboot;
+                    user_please_reboot_later = true;
+                }
+            }
+            if (!ret) {
+                LOG(ERROR) << "Skipping " << mount_point << " for remount";
+                it = partitions.erase(it);
+                continue;
+            }
+        }
+
+        auto change = false;
+        errno = 0;
+        if (fs_mgr_overlayfs_setup(nullptr, mount_point.c_str(), &change, just_disabled_verity)) {
+            if (change) {
+                LOG(INFO) << "Using overlayfs for " << mount_point;
+                reboot_later = can_reboot;
+                user_please_reboot_later = true;
+                setup_overlayfs = true;
+            }
+        } else if (errno) {
+            PLOG(ERROR) << "Overlayfs setup for " << mount_point << " failed, skipping";
+            retval = BAD_OVERLAY;
+            it = partitions.erase(it);
+            continue;
+        }
+        ++it;
+    }
+
+    if (partitions.empty() || just_disabled_verity) {
+        if (reboot_later) reboot(setup_overlayfs);
+        if (user_please_reboot_later) {
+            LOG(INFO) << "Now reboot your device for settings to take effect";
+            return 0;
+        }
+        LOG(WARNING) << "No partitions to remount";
+        return retval;
+    }
+
+    // Mount overlayfs.
+    errno = 0;
+    if (!fs_mgr_overlayfs_mount_all(&partitions) && errno) {
+        retval = BAD_OVERLAY;
+        PLOG(ERROR) << "Can not mount overlayfs for partitions";
+    }
+
+    // Get actual mounts _after_ overlayfs has been added.
+    android::fs_mgr::Fstab mounts;
+    if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts) || mounts.empty()) {
+        PLOG(ERROR) << "Failed to read /proc/mounts";
+        retval = NO_MOUNTS;
+    }
+
+    // Remount selected partitions.
+    for (auto& entry : partitions) {
+        // unlock the r/o key for the mount point device
+        if (entry.fs_mgr_flags.logical) {
+            fs_mgr_update_logical_partition(&entry);
+        }
+        auto blk_device = entry.blk_device;
+        auto mount_point = entry.mount_point;
+
+        for (auto it = mounts.rbegin(); it != mounts.rend(); ++it) {
+            auto& rentry = *it;
+            if (mount_point == rentry.mount_point) {
+                blk_device = rentry.blk_device;
+                break;
+            }
+            // Find overlayfs mount point?
+            if ((mount_point == "/") && (rentry.mount_point == "/system")) {
+                blk_device = rentry.blk_device;
+                mount_point = "/system";
+                break;
+            }
+        }
+        if (blk_device == "/dev/root") {
+            auto from_fstab = GetEntryForMountPoint(&fstab, mount_point);
+            if (from_fstab) blk_device = from_fstab->blk_device;
+        }
+        fs_mgr_set_blk_ro(blk_device, false);
+
+        // Find system-as-root mount point?
+        if ((mount_point == "/system") && !GetEntryForMountPoint(&mounts, mount_point) &&
+            GetEntryForMountPoint(&mounts, "/")) {
+            mount_point = "/";
+        }
+
+        // Now remount!
+        if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
+                    nullptr) == 0) {
+            continue;
+        }
+        if ((errno == EINVAL) && (mount_point != entry.mount_point)) {
+            mount_point = entry.mount_point;
+            if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
+                        nullptr) == 0) {
+                continue;
+            }
+        }
+        PLOG(ERROR) << "failed to remount partition dev:" << blk_device << " mnt:" << mount_point;
+        // If errno is EROFS at this point, we are dealing with r/o
+        // filesystem types like squashfs, erofs or ext4 dedupe. We will
+        // consider such a device that does not have CONFIG_OVERLAY_FS
+        // in the kernel as a misconfigured.
+        if (errno == EROFS) {
+            LOG(ERROR) << "Consider providing all the dependencies to enable overlayfs";
+        }
+        retval = REMOUNT_FAILED;
+    }
+
+    if (reboot_later) reboot(setup_overlayfs);
+    if (user_please_reboot_later) {
+        LOG(INFO) << "Now reboot your device for settings to take effect";
+        return 0;
+    }
+
+    return retval;
+}
diff --git a/fs_mgr/fs_mgr_roots.cpp b/fs_mgr/fs_mgr_roots.cpp
new file mode 100644
index 0000000..1e65587
--- /dev/null
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -0,0 +1,191 @@
+/*
+ * 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 "fs_mgr/roots.h"
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "fs_mgr.h"
+#include "fs_mgr_dm_linear.h"
+#include "fs_mgr_priv.h"
+
+namespace android {
+namespace fs_mgr {
+
+static constexpr const char* kSystemRoot = "/system";
+
+static bool gDidMapLogicalPartitions = false;
+
+FstabEntry* GetEntryForPath(Fstab* fstab, const std::string& path) {
+    if (path.empty()) return nullptr;
+    std::string str(path);
+    while (true) {
+        auto entry = GetEntryForMountPoint(fstab, str);
+        if (entry != nullptr) return entry;
+        if (str == "/") break;
+        auto slash = str.find_last_of('/');
+        if (slash == std::string::npos) break;
+        if (slash == 0) {
+            str = "/";
+        } else {
+            str = str.substr(0, slash);
+        }
+    }
+    return nullptr;
+}
+
+enum class MountState {
+    ERROR = -1,
+    NOT_MOUNTED = 0,
+    MOUNTED = 1,
+};
+
+static MountState GetMountState(const std::string& mount_point) {
+    Fstab mounted_fstab;
+    if (!ReadFstabFromFile("/proc/mounts", &mounted_fstab)) {
+        LERROR << "Failed to scan mounted volumes";
+        return MountState::ERROR;
+    }
+
+    auto mv = GetEntryForMountPoint(&mounted_fstab, mount_point);
+    if (mv != nullptr) {
+        return MountState::MOUNTED;
+    }
+    return MountState::NOT_MOUNTED;
+}
+
+bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_pt) {
+    auto rec = GetEntryForPath(fstab, path);
+    if (rec == nullptr) {
+        LERROR << "unknown volume for path [" << path << "]";
+        return false;
+    }
+    if (rec->fs_type == "ramdisk") {
+        // The ramdisk is always mounted.
+        return true;
+    }
+
+    // If we can't acquire the block device for a logical partition, it likely
+    // was never created. In that case we try to create it.
+    if (rec->fs_mgr_flags.logical && !fs_mgr_update_logical_partition(rec)) {
+        if (gDidMapLogicalPartitions) {
+            LERROR << "Failed to find block device for partition";
+            return false;
+        }
+        std::string super_name = fs_mgr_get_super_partition_name();
+        if (!android::fs_mgr::CreateLogicalPartitions("/dev/block/by-name/" + super_name)) {
+            LERROR << "Failed to create logical partitions";
+            return false;
+        }
+        gDidMapLogicalPartitions = true;
+        if (!fs_mgr_update_logical_partition(rec)) {
+            LERROR << "Failed to find block device for partition";
+            return false;
+        }
+    }
+
+    const std::string mount_point = mount_pt.empty() ? rec->mount_point : mount_pt;
+
+    auto mounted = GetMountState(mount_point);
+    if (mounted == MountState::ERROR) {
+        return false;
+    }
+    if (mounted == MountState::MOUNTED) {
+        return true;
+    }
+
+    static const std::vector<std::string> supported_fs{"ext4", "squashfs", "vfat", "f2fs", "none"};
+    if (std::find(supported_fs.begin(), supported_fs.end(), rec->fs_type) == supported_fs.end()) {
+        LERROR << "unknown fs_type \"" << rec->fs_type << "\" for " << mount_point;
+        return false;
+    }
+
+    int result = fs_mgr_do_mount_one(*rec, mount_point);
+    if (result == -1 && rec->fs_mgr_flags.formattable) {
+        PERROR << "Failed to mount " << mount_point << "; formatting";
+        bool crypt_footer = rec->is_encryptable() && rec->key_loc == "footer";
+        if (fs_mgr_do_format(*rec, crypt_footer) != 0) {
+            PERROR << "Failed to format " << mount_point;
+            return false;
+        }
+        result = fs_mgr_do_mount_one(*rec, mount_point);
+    }
+
+    if (result == -1) {
+        PERROR << "Failed to mount " << mount_point;
+        return false;
+    }
+    return true;
+}
+
+bool EnsurePathUnmounted(Fstab* fstab, const std::string& path) {
+    auto rec = GetEntryForPath(fstab, path);
+    if (rec == nullptr) {
+        LERROR << "unknown volume for path [" << path << "]";
+        return false;
+    }
+    if (rec->fs_type == "ramdisk") {
+        // The ramdisk is always mounted; you can't unmount it.
+        return false;
+    }
+
+    Fstab mounted_fstab;
+    if (!ReadFstabFromFile("/proc/mounts", &mounted_fstab)) {
+        LERROR << "Failed to scan mounted volumes";
+        return false;
+    }
+
+    auto mounted = GetMountState(rec->mount_point);
+    if (mounted == MountState::ERROR) {
+        return false;
+    }
+    if (mounted == MountState::NOT_MOUNTED) {
+        return true;
+    }
+
+    int result = umount(rec->mount_point.c_str());
+    if (result == -1) {
+        PWARNING << "Failed to umount " << rec->mount_point;
+        return false;
+    }
+    return true;
+}
+
+std::string GetSystemRoot() {
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        LERROR << "Failed to read default fstab";
+        return "";
+    }
+
+    auto entry = GetEntryForMountPoint(&fstab, kSystemRoot);
+    if (entry == nullptr) {
+        return "/";
+    }
+
+    return kSystemRoot;
+}
+
+bool LogicalPartitionsMapped() {
+    return gDidMapLogicalPartitions;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index 0a113b4..09c1b7e 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -21,6 +21,28 @@
 #include "fs_mgr.h"
 #include "fs_mgr_priv.h"
 
+// Realistically, this file should be part of the android::fs_mgr namespace;
+using namespace android::fs_mgr;
+
+// https://source.android.com/devices/tech/ota/ab/ab_implement#partitions
+// All partitions that are A/B-ed should be named as follows (slots are always
+// named a, b, etc.): boot_a, boot_b, system_a, system_b, vendor_a, vendor_b.
+static std::string other_suffix(const std::string& slot_suffix) {
+    if (slot_suffix == "_a") {
+        return "_b";
+    }
+    if (slot_suffix == "_b") {
+        return "_a";
+    }
+    return "";
+}
+
+// Returns "_b" or "_a", which is *the other* slot of androidboot.slot_suffix
+// in kernel cmdline, or an empty string if that parameter does not exist.
+std::string fs_mgr_get_other_slot_suffix() {
+    return other_suffix(fs_mgr_get_slot_suffix());
+}
+
 // 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() {
@@ -31,25 +53,24 @@
 }
 
 // Updates |fstab| for slot_suffix. Returns true on success, false on error.
-bool fs_mgr_update_for_slotselect(struct fstab *fstab) {
-    int n;
+bool fs_mgr_update_for_slotselect(Fstab* fstab) {
     std::string ab_suffix;
 
-    for (n = 0; n < fstab->num_entries; n++) {
-        if (fstab->recs[n].fs_mgr_flags & MF_SLOTSELECT) {
-            char *tmp;
-            if (ab_suffix.empty()) {
-                ab_suffix = fs_mgr_get_slot_suffix();
-                // 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 {
-                return false;
-            }
+    for (auto& entry : *fstab) {
+        if (!entry.fs_mgr_flags.slot_select && !entry.fs_mgr_flags.slot_select_other) {
+            continue;
         }
+
+        if (ab_suffix.empty()) {
+            ab_suffix = fs_mgr_get_slot_suffix();
+            // Return false if failed to get ab_suffix when MF_SLOTSELECT is specified.
+            if (ab_suffix.empty()) return false;
+        }
+
+        const auto& update_suffix =
+                entry.fs_mgr_flags.slot_select ? ab_suffix : other_suffix(ab_suffix);
+        entry.blk_device = entry.blk_device + update_suffix;
+        entry.logical_partition_name = entry.logical_partition_name + update_suffix;
     }
     return true;
 }
diff --git a/fs_mgr/fs_mgr_vendor_overlay.cpp b/fs_mgr/fs_mgr_vendor_overlay.cpp
new file mode 100644
index 0000000..830f0dd
--- /dev/null
+++ b/fs_mgr/fs_mgr_vendor_overlay.cpp
@@ -0,0 +1,137 @@
+/*
+ * 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 <dirent.h>
+#include <selinux/selinux.h>
+#include <sys/mount.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <fs_mgr_overlayfs.h>
+#include <fs_mgr_vendor_overlay.h>
+#include <fstab/fstab.h>
+
+#include "fs_mgr_priv.h"
+
+using namespace std::literals;
+
+namespace {
+
+// The order of the list means the priority to show the files in the directory.
+// The last one has the highest priority.
+const std::vector<const std::string> kVendorOverlaySourceDirs = {
+        "/system/vendor_overlay/",
+        "/product/vendor_overlay/",
+};
+const auto kVndkVersionPropertyName = "ro.vndk.version"s;
+const auto kVendorTopDir = "/vendor/"s;
+const auto kLowerdirOption = "lowerdir="s;
+
+std::vector<std::pair<std::string, std::string>> fs_mgr_get_vendor_overlay_dirs(
+        const std::string& vndk_version) {
+    std::vector<std::pair<std::string, std::string>> vendor_overlay_dirs;
+    for (const auto& vendor_overlay_source : kVendorOverlaySourceDirs) {
+        const auto overlay_top = vendor_overlay_source + vndk_version;
+        std::unique_ptr<DIR, decltype(&closedir)> vendor_overlay_top(opendir(overlay_top.c_str()),
+                                                                     closedir);
+        if (!vendor_overlay_top) continue;
+
+        // Vendor overlay root for current vendor version found!
+        LINFO << "vendor overlay root: " << overlay_top;
+
+        struct dirent* dp;
+        while ((dp = readdir(vendor_overlay_top.get())) != nullptr) {
+            if (dp->d_type != DT_DIR || dp->d_name[0] == '.') {
+                continue;
+            }
+            vendor_overlay_dirs.emplace_back(overlay_top, dp->d_name);
+        }
+    }
+    return vendor_overlay_dirs;
+}
+
+bool fs_mgr_vendor_overlay_mount(const std::pair<std::string, std::string>& mount_point) {
+    const auto [overlay_top, mount_dir] = mount_point;
+    const auto vendor_mount_point = kVendorTopDir + mount_dir;
+    LINFO << "vendor overlay mount on " << vendor_mount_point;
+
+    const auto target_context = fs_mgr_get_context(vendor_mount_point);
+    if (target_context.empty()) {
+        PERROR << " failed: cannot find the target vendor mount point";
+        return false;
+    }
+    const auto source_directory = overlay_top + "/" + mount_dir;
+    const auto source_context = fs_mgr_get_context(source_directory);
+    if (target_context != source_context) {
+        LERROR << " failed: source and target contexts do not match (source:" << source_context
+               << ", target:" << target_context << ")";
+        return false;
+    }
+
+    auto options = kLowerdirOption + source_directory + ":" + vendor_mount_point;
+    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kOverrideCredsRequired) {
+        options += ",override_creds=off";
+    }
+    auto report = "__mount(source=overlay,target="s + vendor_mount_point + ",type=overlay," +
+                  options + ")=";
+    auto ret = mount("overlay", vendor_mount_point.c_str(), "overlay", MS_RDONLY | MS_RELATIME,
+                     options.c_str());
+    if (ret) {
+        PERROR << report << ret;
+        return false;
+    } else {
+        LINFO << report << ret;
+        return true;
+    }
+}
+
+}  // namespace
+
+// Since the vendor overlay requires to know the version of the vendor partition,
+// it is not possible to mount vendor overlay at the first stage that cannot
+// initialize properties.
+// To read the properties, vendor overlay must be mounted at the second stage, right
+// after "property_load_boot_defaults()" is called.
+bool fs_mgr_vendor_overlay_mount_all() {
+    // To read the property, it must be called at the second init stage after the default
+    // properties are loaded.
+    static const auto vndk_version = android::base::GetProperty(kVndkVersionPropertyName, "");
+    if (vndk_version.empty()) {
+        LINFO << "vendor overlay: vndk version not defined";
+        return false;
+    }
+
+    const auto vendor_overlay_dirs = fs_mgr_get_vendor_overlay_dirs(vndk_version);
+    if (vendor_overlay_dirs.empty()) return true;
+    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
+        LINFO << "vendor overlay: kernel does not support overlayfs";
+        return false;
+    }
+
+    // Mount each directory in /(system|product)/vendor_overlay/<ver> on /vendor
+    auto ret = true;
+    for (const auto& vendor_overlay_dir : vendor_overlay_dirs) {
+        if (!fs_mgr_vendor_overlay_mount(vendor_overlay_dir)) {
+            ret = false;
+        }
+    }
+    return ret;
+}
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 896b603..be8077b 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -35,6 +35,8 @@
 #include <android-base/unique_fd.h>
 #include <crypto_utils/android_pubkey.h>
 #include <cutils/properties.h>
+#include <fs_mgr/file_wait.h>
+#include <libdm/dm.h>
 #include <logwrap/logwrap.h>
 #include <openssl/obj_mac.h>
 #include <openssl/rsa.h>
@@ -43,8 +45,11 @@
 #include "fec/io.h"
 
 #include "fs_mgr.h"
+#include "fs_mgr_dm_linear.h"
 #include "fs_mgr_priv.h"
-#include "fs_mgr_priv_dm_ioctl.h"
+
+// Realistically, this file should be part of the android::fs_mgr namespace;
+using namespace android::fs_mgr;
 
 #define VERITY_TABLE_RSA_KEY "/verity_key"
 #define VERITY_TABLE_HASH_IDX 8
@@ -89,24 +94,21 @@
 {
     uint8_t key_data[ANDROID_PUBKEY_ENCODED_SIZE];
 
-    FILE* f = fopen(path, "r");
+    auto f = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path, "re"), fclose};
     if (!f) {
         LERROR << "Can't open " << path;
-        return NULL;
+        return nullptr;
     }
 
-    if (!fread(key_data, sizeof(key_data), 1, f)) {
+    if (!fread(key_data, sizeof(key_data), 1, f.get())) {
         LERROR << "Could not read key!";
-        fclose(f);
-        return NULL;
+        return nullptr;
     }
 
-    fclose(f);
-
-    RSA* key = NULL;
+    RSA* key = nullptr;
     if (!android_pubkey_decode(key_data, sizeof(key_data), &key)) {
         LERROR << "Could not parse key!";
-        return NULL;
+        return nullptr;
     }
 
     return key;
@@ -250,304 +252,30 @@
     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;
 }
 
-static int check_verity_restart(const char *fname)
-{
-    char buffer[VERITY_KMSG_BUFSIZE + 1];
-    int fd;
-    int rc = 0;
-    ssize_t size;
-    struct stat s;
-
-    fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
-
-    if (fd == -1) {
-        if (errno != ENOENT) {
-            PERROR << "Failed to open " << fname;
-        }
-        goto out;
-    }
-
-    if (fstat(fd, &s) == -1) {
-        PERROR << "Failed to fstat " << fname;
-        goto out;
-    }
-
-    size = VERITY_KMSG_BUFSIZE;
-
-    if (size > s.st_size) {
-        size = s.st_size;
-    }
-
-    if (lseek(fd, s.st_size - size, SEEK_SET) == -1) {
-        PERROR << "Failed to lseek " << (intmax_t)(s.st_size - size) << " " << fname;
-        goto out;
-    }
-
-    if (!android::base::ReadFully(fd, buffer, size)) {
-        PERROR << "Failed to read " << size << " bytes from " << fname;
-        goto out;
-    }
-
-    buffer[size] = '\0';
-
-    if (strstr(buffer, VERITY_KMSG_RESTART) != NULL) {
-        rc = 1;
-    }
-
-out:
-    if (fd != -1) {
-        close(fd);
-    }
-
-    return rc;
-}
-
-static int was_verity_restart()
-{
-    static const char* files[] = {
-        // clang-format off
-        "/sys/fs/pstore/console-ramoops-0",
-        "/sys/fs/pstore/console-ramoops",
-        "/proc/last_kmsg",
-        NULL
-        // clang-format on
-    };
-    int i;
-
-    for (i = 0; files[i]; ++i) {
-        if (check_verity_restart(files[i])) {
-            return 1;
-        }
-    }
-
-    return 0;
-}
-
-static int metadata_add(FILE *fp, long start, const char *tag,
-        unsigned int length, off64_t *offset)
-{
-    if (fseek(fp, start, SEEK_SET) < 0 ||
-        fprintf(fp, "%s %u\n", tag, length) < 0) {
-        return -1;
-    }
-
-    *offset = ftell(fp);
-
-    if (fseek(fp, length, SEEK_CUR) < 0 ||
-        fprintf(fp, METADATA_EOD " 0\n") < 0) {
-        return -1;
-    }
-
-    return 0;
-}
-
-static int metadata_find(const char *fname, const char *stag,
-        unsigned int slength, off64_t *offset)
-{
-    FILE *fp = NULL;
-    char tag[METADATA_TAG_MAX_LENGTH + 1];
-    int rc = -1;
-    int n;
-    long start = 0x4000; /* skip cryptfs metadata area */
-    uint32_t magic;
-    unsigned int length = 0;
-
-    if (!fname) {
-        return -1;
-    }
-
-    fp = fopen(fname, "r+");
-
-    if (!fp) {
-        PERROR << "Failed to open " << fname;
-        goto out;
-    }
-
-    /* check magic */
-    if (fseek(fp, start, SEEK_SET) < 0 ||
-        fread(&magic, sizeof(magic), 1, fp) != 1) {
-        PERROR << "Failed to read magic from " << fname;
-        goto out;
-    }
-
-    if (magic != METADATA_MAGIC) {
-        magic = METADATA_MAGIC;
-
-        if (fseek(fp, start, SEEK_SET) < 0 ||
-            fwrite(&magic, sizeof(magic), 1, fp) != 1) {
-            PERROR << "Failed to write magic to " << fname;
-            goto out;
-        }
-
-        rc = metadata_add(fp, start + sizeof(magic), stag, slength, offset);
-        if (rc < 0) {
-            PERROR << "Failed to add metadata to " << fname;
-        }
-
-        goto out;
-    }
-
-    start += sizeof(magic);
-
-    while (1) {
-        n = fscanf(fp, "%" STRINGIFY(METADATA_TAG_MAX_LENGTH) "s %u\n",
-                tag, &length);
-
-        if (n == 2 && strcmp(tag, METADATA_EOD)) {
-            /* found a tag */
-            start = ftell(fp);
-
-            if (!strcmp(tag, stag) && length == slength) {
-                *offset = start;
-                rc = 0;
-                goto out;
-            }
-
-            start += length;
-
-            if (fseek(fp, length, SEEK_CUR) < 0) {
-                PERROR << "Failed to seek " << fname;
-                goto out;
-            }
-        } else {
-            rc = metadata_add(fp, start, stag, slength, offset);
-            if (rc < 0) {
-                PERROR << "Failed to write metadata to " << fname;
-            }
-            goto out;
-        }
-   }
-
-out:
-    if (fp) {
-        fflush(fp);
-        fclose(fp);
-    }
-
-    return rc;
-}
-
-static int write_verity_state(const char *fname, off64_t offset, int32_t mode)
-{
-    int fd;
-    int rc = -1;
-    struct verity_state s = { VERITY_STATE_HEADER, VERITY_STATE_VERSION, mode };
-
-    fd = TEMP_FAILURE_RETRY(open(fname, O_WRONLY | O_SYNC | O_CLOEXEC));
-
-    if (fd == -1) {
-        PERROR << "Failed to open " << fname;
-        goto out;
-    }
-
-    if (TEMP_FAILURE_RETRY(pwrite64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
-        PERROR << "Failed to write " << sizeof(s) << " bytes to " << fname
-               << " to offset " << offset;
-        goto out;
-    }
-
-    rc = 0;
-
-out:
-    if (fd != -1) {
-        close(fd);
-    }
-
-    return rc;
-}
-
-static int read_verity_state(const char *fname, off64_t offset, int *mode)
-{
-    int fd = -1;
-    int rc = -1;
-    struct verity_state s;
-
-    fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
-
-    if (fd == -1) {
-        PERROR << "Failed to open " << fname;
-        goto out;
-    }
-
-    if (TEMP_FAILURE_RETRY(pread64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
-        PERROR << "Failed to read " <<  sizeof(s) << " bytes from " << fname
-               << " offset " << offset;
-        goto out;
-    }
-
-    if (s.header != VERITY_STATE_HEADER) {
-        /* space allocated, but no state written. write default state */
-        *mode = VERITY_MODE_DEFAULT;
-        rc = write_verity_state(fname, offset, *mode);
-        goto out;
-    }
-
-    if (s.version != VERITY_STATE_VERSION) {
-        LERROR << "Unsupported verity state version (" << s.version << ")";
-        goto out;
-    }
-
-    if (s.mode < VERITY_MODE_EIO ||
-        s.mode > VERITY_MODE_LAST) {
-        LERROR << "Unsupported verity mode (" << s.mode << ")";
-        goto out;
-    }
-
-    *mode = s.mode;
-    rc = 0;
-
-out:
-    if (fd != -1) {
-        close(fd);
-    }
-
-    return rc;
-}
-
 static int read_partition(const char *path, uint64_t size)
 {
     char buf[READ_BUF_SIZE];
@@ -571,125 +299,23 @@
     return 0;
 }
 
-static int compare_last_signature(struct fstab_rec *fstab, int *match)
-{
-    char tag[METADATA_TAG_MAX_LENGTH + 1];
-    int fd = -1;
-    int rc = -1;
-    off64_t offset = 0;
-    struct fec_handle *f = NULL;
-    struct fec_verity_metadata verity;
-    uint8_t curr[SHA256_DIGEST_LENGTH];
-    uint8_t prev[SHA256_DIGEST_LENGTH];
-
-    *match = 1;
-
-    if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
-            FEC_DEFAULT_ROOTS) == -1) {
-        PERROR << "Failed to open '" << fstab->blk_device << "'";
-        return rc;
-    }
-
-    // read verity metadata
-    if (fec_verity_get_metadata(f, &verity) == -1) {
-        PERROR << "Failed to get verity metadata '" << fstab->blk_device << "'";
-        goto out;
-    }
-
-    SHA256(verity.signature, sizeof(verity.signature), curr);
-
-    if (snprintf(tag, sizeof(tag), VERITY_LASTSIG_TAG "_%s",
-            basename(fstab->mount_point)) >= (int)sizeof(tag)) {
-        LERROR << "Metadata tag name too long for " << fstab->mount_point;
-        goto out;
-    }
-
-    if (metadata_find(fstab->verity_loc, tag, SHA256_DIGEST_LENGTH,
-            &offset) < 0) {
-        goto out;
-    }
-
-    fd = TEMP_FAILURE_RETRY(open(fstab->verity_loc, O_RDWR | O_SYNC | O_CLOEXEC));
-
-    if (fd == -1) {
-        PERROR << "Failed to open " << fstab->verity_loc;
-        goto out;
-    }
-
-    if (TEMP_FAILURE_RETRY(pread64(fd, prev, sizeof(prev),
-            offset)) != sizeof(prev)) {
-        PERROR << "Failed to read " << sizeof(prev) << " bytes from "
-               << fstab->verity_loc << " offset " << offset;
-        goto out;
-    }
-
-    *match = !memcmp(curr, prev, SHA256_DIGEST_LENGTH);
-
-    if (!*match) {
-        /* update current signature hash */
-        if (TEMP_FAILURE_RETRY(pwrite64(fd, curr, sizeof(curr),
-                offset)) != sizeof(curr)) {
-            PERROR << "Failed to write " << sizeof(curr) << " bytes to "
-                   << fstab->verity_loc << " offset " << offset;
-            goto out;
-        }
-    }
-
-    rc = 0;
-
-out:
-    fec_close(f);
-    return rc;
-}
-
-static int get_verity_state_offset(struct fstab_rec *fstab, off64_t *offset)
-{
-    char tag[METADATA_TAG_MAX_LENGTH + 1];
-
-    if (snprintf(tag, sizeof(tag), VERITY_STATE_TAG "_%s",
-            basename(fstab->mount_point)) >= (int)sizeof(tag)) {
-        LERROR << "Metadata tag name too long for " << fstab->mount_point;
-        return -1;
-    }
-
-    return metadata_find(fstab->verity_loc, tag, sizeof(struct verity_state),
-                offset);
-}
-
-int load_verity_state(struct fstab_rec* fstab, int* mode) {
-    int match = 0;
-    off64_t offset = 0;
-
-    /* unless otherwise specified, use EIO mode */
+bool fs_mgr_load_verity_state(int* mode) {
+    // unless otherwise specified, use EIO mode.
     *mode = VERITY_MODE_EIO;
 
-    /* use the kernel parameter if set */
-    std::string veritymode;
-    if (fs_mgr_get_boot_config("veritymode", &veritymode)) {
-        if (veritymode == "enforcing") {
-            *mode = VERITY_MODE_DEFAULT;
-        }
-        return 0;
+    // The bootloader communicates verity mode via the kernel commandline
+    std::string verity_mode;
+    if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
+        return false;
     }
 
-    if (get_verity_state_offset(fstab, &offset) < 0) {
-        /* fall back to stateless behavior */
-        return 0;
-    }
-
-    if (was_verity_restart()) {
-        /* device was restarted after dm-verity detected a corrupted
-         * block, so use EIO mode */
-        return write_verity_state(fstab->verity_loc, offset, *mode);
-    }
-
-    if (!compare_last_signature(fstab, &match) && !match) {
-        /* partition has been reflashed, reset dm-verity state */
+    if (verity_mode == "enforcing") {
         *mode = VERITY_MODE_DEFAULT;
-        return write_verity_state(fstab->verity_loc, offset, *mode);
+    } else if (verity_mode == "logging") {
+        *mode = VERITY_MODE_LOGGING;
     }
 
-    return read_verity_state(fstab->verity_loc, offset, mode);
+    return true;
 }
 
 // Update the verity table using the actual block device path.
@@ -751,8 +377,7 @@
 // prepares the verity enabled (MF_VERIFY / MF_VERIFYATBOOT) fstab record for
 // mount. The 'wait_for_verity_dev' parameter makes this function wait for the
 // verity device to get created before return
-int fs_mgr_setup_verity(struct fstab_rec *fstab, bool wait_for_verity_dev)
-{
+int fs_mgr_setup_verity(FstabEntry* entry, bool wait_for_verity_dev) {
     int retval = FS_MGR_SETUP_VERITY_FAIL;
     int fd = -1;
     std::string verity_blk_name;
@@ -760,20 +385,20 @@
     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));
+    const std::string mount_point(basename(entry->mount_point.c_str()));
     bool verified_at_boot = false;
 
-    if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
-            FEC_DEFAULT_ROOTS) < 0) {
-        PERROR << "Failed to open '" << fstab->blk_device << "'";
+    android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
+
+    if (fec_open(&f, entry->blk_device.c_str(), O_RDONLY, FEC_VERITY_DISABLE, FEC_DEFAULT_ROOTS) <
+        0) {
+        PERROR << "Failed to open '" << entry->blk_device << "'";
         return retval;
     }
 
     // read verity metadata
     if (fec_verity_get_metadata(f, &verity) < 0) {
-        PERROR << "Failed to get verity metadata '" << fstab->blk_device << "'";
+        PERROR << "Failed to get verity metadata '" << entry->blk_device << "'";
         // Allow verity disabled when the device is unlocked without metadata
         if (fs_mgr_is_device_unlocked()) {
             retval = FS_MGR_SETUP_VERITY_SKIPPED;
@@ -795,27 +420,9 @@
         params.ecc.valid = false;
     }
 
-    params.ecc_dev = fstab->blk_device;
+    params.ecc_dev = entry->blk_device.c_str();
 
-    // 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 (!fs_mgr_load_verity_state(&params.mode)) {
         /* 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
          * restart loop, and no corrupted data will be exposed to userspace
@@ -856,12 +463,11 @@
           << " (mode " << params.mode << ")";
 
     // Update the verity params using the actual block device path
-    update_verity_table_blk_device(fstab->blk_device, &params.table,
-                                   fstab->fs_mgr_flags & MF_SLOTSELECT);
+    update_verity_table_blk_device(entry->blk_device, &params.table,
+                                   entry->fs_mgr_flags.slot_select);
 
     // 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;
     }
 
@@ -870,15 +476,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;
     }
 
@@ -887,8 +492,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;
         }
     }
@@ -897,38 +502,35 @@
     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;
     }
 
     // mark the underlying block device as read-only
-    fs_mgr_set_blk_ro(fstab->blk_device);
+    fs_mgr_set_blk_ro(entry->blk_device);
 
     // Verify the entire partition in one go
     // If there is an error, allow it to mount as a normal verity partition.
-    if (fstab->fs_mgr_flags & MF_VERIFYATBOOT) {
-        LINFO << "Verifying partition " << fstab->blk_device << " at boot";
+    if (entry->fs_mgr_flags.verify_at_boot) {
+        LINFO << "Verifying partition " << entry->blk_device << " at boot";
         int err = read_partition(verity_blk_name.c_str(), verity.data_size);
         if (!err) {
-            LINFO << "Verified verity partition "
-                  << fstab->blk_device << " at boot";
+            LINFO << "Verified verity partition " << entry->blk_device << " at boot";
             verified_at_boot = true;
         }
     }
 
     // assign the new verity block device as the block device
     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)) {
+        entry->blk_device = verity_blk_name;
+    } else if (!dm.DeleteDevice(mount_point)) {
         LERROR << "Failed to remove verity device " << mount_point.c_str();
         goto out;
     }
 
     // make sure we've set everything up properly
-    if (wait_for_verity_dev && !fs_mgr_wait_for_file(fstab->blk_device, 1s)) {
+    if (wait_for_verity_dev && !WaitForFile(entry->blk_device, 1s)) {
         goto out;
     }
 
@@ -944,3 +546,12 @@
 
     return retval;
 }
+
+bool fs_mgr_teardown_verity(FstabEntry* entry, bool wait) {
+    const std::string mount_point(basename(entry->mount_point.c_str()));
+    if (!android::fs_mgr::UnmapDevice(mount_point, wait ? 1000ms : 0ms)) {
+        return false;
+    }
+    LINFO << "Unmapped verity device " << mount_point;
+    return true;
+}
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 653d8fa..bdec7be 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -14,14 +14,16 @@
  * limitations under the License.
  */
 
-#ifndef __CORE_FS_MGR_H
-#define __CORE_FS_MGR_H
+#pragma once
 
 #include <stdio.h>
 #include <stdint.h>
 #include <stdbool.h>
 #include <linux/dm-ioctl.h>
 
+#include <functional>
+#include <string>
+
 #include <fstab/fstab.h>
 
 // Magic number at start of verity metadata
@@ -47,10 +49,6 @@
     MOUNT_MODE_LATE = 2
 };
 
-// Callback function for verity 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
@@ -60,29 +58,53 @@
 #define FS_MGR_MNTALL_DEV_NOT_ENCRYPTED 1
 #define FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE 0
 #define FS_MGR_MNTALL_FAIL (-1)
-int fs_mgr_mount_all(struct fstab *fstab, int mount_mode);
+// fs_mgr_mount_all() updates fstab entries that reference device-mapper.
+int fs_mgr_mount_all(android::fs_mgr::Fstab* fstab, int mount_mode);
 
 #define FS_MGR_DOMNT_FAILED (-1)
 #define FS_MGR_DOMNT_BUSY (-2)
 #define FS_MGR_DOMNT_SUCCESS 0
-
-int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
-                    char *tmp_mount_point);
-int fs_mgr_do_mount_one(struct fstab_rec *rec);
+int fs_mgr_do_mount(android::fs_mgr::Fstab* fstab, const char* n_name, char* n_blk_device,
+                    char* tmp_mount_point);
+int fs_mgr_do_mount(android::fs_mgr::Fstab* fstab, const char* n_name, char* n_blk_device,
+                    char* tmp_mount_point, bool need_cp);
+int fs_mgr_do_mount_one(const android::fs_mgr::FstabEntry& entry,
+                        const std::string& mount_point = "");
 int fs_mgr_do_tmpfs_mount(const char *n_name);
-int fs_mgr_unmount_all(struct fstab *fstab);
-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);
-int fs_mgr_swapon_all(struct fstab *fstab);
+// Returns true if verity is enabled on this particular FstabEntry.
+bool fs_mgr_is_verity_enabled(const android::fs_mgr::FstabEntry& entry);
+bool fs_mgr_swapon_all(const android::fs_mgr::Fstab& fstab);
+bool fs_mgr_update_logical_partition(android::fs_mgr::FstabEntry* entry);
 
-int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer);
+// Returns true if the given fstab entry has verity enabled, *and* the verity
+// device is in "check_at_most_once" mode.
+bool fs_mgr_verity_is_check_at_most_once(const android::fs_mgr::FstabEntry& entry);
+
+int fs_mgr_do_format(const android::fs_mgr::FstabEntry& entry, bool reserve_footer);
 
 #define FS_MGR_SETUP_VERITY_SKIPPED  (-3)
 #define FS_MGR_SETUP_VERITY_DISABLED (-2)
 #define FS_MGR_SETUP_VERITY_FAIL (-1)
 #define FS_MGR_SETUP_VERITY_SUCCESS 0
-int fs_mgr_setup_verity(struct fstab_rec *fstab, bool wait_for_verity_dev);
+int fs_mgr_setup_verity(android::fs_mgr::FstabEntry* fstab, bool wait_for_verity_dev);
 
-#endif /* __CORE_FS_MGR_H */
+// Return the name of the super partition if it exists. If a slot number is
+// specified, the super partition for the corresponding metadata slot will be
+// returned. Otherwise, it will use the current slot.
+std::string fs_mgr_get_super_partition_name(int slot = -1);
+
+enum FsMgrUmountStatus : int {
+    SUCCESS = 0,
+    ERROR_UNKNOWN = 1 << 0,
+    ERROR_UMOUNT = 1 << 1,
+    ERROR_VERITY = 1 << 2,
+    ERROR_DEVICE_MAPPER = 1 << 3,
+};
+// fs_mgr_umount_all() is the reverse of fs_mgr_mount_all. In particular,
+// it destroys verity devices from device mapper after the device is unmounted.
+int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab);
+
+// Finds the dm_bow device on which this block device is stacked, or returns
+// empty string
+std::string fs_mgr_find_bow_device(const std::string& block_device);
diff --git a/fs_mgr/include/fs_mgr/file_wait.h b/fs_mgr/include/fs_mgr/file_wait.h
new file mode 100644
index 0000000..74d160e
--- /dev/null
+++ b/fs_mgr/include/fs_mgr/file_wait.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 <chrono>
+#include <string>
+
+namespace android {
+namespace fs_mgr {
+
+// Wait at most |relative_timeout| milliseconds for |path| to exist. dirname(path)
+// must already exist. For example, to wait on /dev/block/dm-6, /dev/block must
+// be a valid directory.
+bool WaitForFile(const std::string& path, const std::chrono::milliseconds relative_timeout);
+
+// Wait at most |relative_timeout| milliseconds for |path| to stop existing.
+// Note that this only returns true if the inode itself no longer exists, i.e.,
+// all outstanding file descriptors have been closed.
+bool WaitForFileDeleted(const std::string& path, const std::chrono::milliseconds relative_timeout);
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/include/fs_mgr/roots.h b/fs_mgr/include/fs_mgr/roots.h
new file mode 100644
index 0000000..65c59cf
--- /dev/null
+++ b/fs_mgr/include/fs_mgr/roots.h
@@ -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.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <fs_mgr.h>
+
+namespace android {
+namespace fs_mgr {
+
+// Finds the volume specified by the given path. fs_mgr_get_entry_for_mount_point() does exact match
+// only, so it attempts the prefixes recursively (e.g. "/cache/recovery/last_log",
+// "/cache/recovery", "/cache", "/" for a given path of "/cache/recovery/last_log") and returns the
+// first match or nullptr.
+FstabEntry* GetEntryForPath(Fstab* fstab, const std::string& path);
+
+// Make sure that the volume 'path' is on is mounted.
+// * If 'mount_point' is nullptr, use mount point in fstab. Caller can call
+//   fs_mgr_ensure_path_unmounted() with the same 'path' argument to unmount.
+// * If 'mount_point' is not nullptr, the mount point is overridden. Caller can
+//   call umount(mount_point) to unmount.
+// Returns true on success (volume is mounted).
+bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_point = "");
+
+// Make sure that the volume 'path' is on is unmounted.  Returns true on
+// success (volume is unmounted).
+bool EnsurePathUnmounted(Fstab* fstab, const std::string& path);
+
+// Return "/system" if it is in default fstab, otherwise "/".
+std::string GetSystemRoot();
+
+// Return true iff logical partitions are mapped when partitions are mounted via ensure_path_mounted
+// functions.
+bool LogicalPartitionsMapped();
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/include/fs_mgr_avb.h b/fs_mgr/include/fs_mgr_avb.h
deleted file mode 100644
index 73a22c8..0000000
--- a/fs_mgr/include/fs_mgr_avb.h
+++ /dev/null
@@ -1,131 +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 __CORE_FS_MGR_AVB_H
-#define __CORE_FS_MGR_AVB_H
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include <libavb/libavb.h>
-
-#include "fs_mgr.h"
-
-enum class SetUpAvbHashtreeResult {
-    kSuccess = 0,
-    kFail,
-    kDisabled,
-};
-
-class FsManagerAvbOps;
-
-class FsManagerAvbHandle;
-using FsManagerAvbUniquePtr = std::unique_ptr<FsManagerAvbHandle>;
-
-using ByNameSymlinkMap = std::map<std::string, std::string>;
-
-// Provides a factory method to return a unique_ptr pointing to itself and the
-// SetUpAvbHashtree() function to extract dm-verity parameters from AVB HASHTREE
-// descriptors to load verity table into kernel through ioctl.
-class FsManagerAvbHandle {
-  public:
-    // The factory method to return a FsManagerAvbUniquePtr that holds
-    // the verified AVB (external/avb) metadata of all verified partitions
-    // in avb_slot_data_.vbmeta_images[].
-    //
-    // The metadata is checked against the following values from /proc/cmdline.
-    //   - androidboot.vbmeta.{hash_alg, size, digest}.
-    //
-    // A typical usage will be:
-    //   - FsManagerAvbUniquePtr handle = FsManagerAvbHandle::Open();
-    //
-    // There are two overloaded Open() functions with a single parameter.
-    // The argument can be a ByNameSymlinkMap describing the mapping from partition
-    // name to by-name symlink, or a fstab file to which the ByNameSymlinkMap is
-    // constructed from. e.g.,
-    //   - /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a ->
-    //   - ByNameSymlinkMap["system_a"] = "/dev/block/platform/soc.0/7824900.sdhci/by-name/system_a"
-    //
-    // Possible return values:
-    //   - nullptr: any error when reading and verifying the metadata,
-    //     e.g., I/O error, digest value mismatch, size mismatch, etc.
-    //
-    //   - a valid unique_ptr with status kAvbHandleHashtreeDisabled:
-    //     to support the existing 'adb disable-verity' feature in Android.
-    //     It's very helpful for developers to make the filesystem writable to
-    //     allow replacing binaries on the device.
-    //
-    //   - a valid unique_ptr with status kAvbHandleVerificationDisabled:
-    //     to support 'avbctl disable-verification': only the top-level
-    //     vbmeta is read, vbmeta structs in other partitions are not processed.
-    //     It's needed to bypass AVB when using the generic system.img to run
-    //     VTS for project Treble.
-    //
-    //   - a valid unique_ptr with status kAvbHandleVerificationError:
-    //     there is verification error when libavb loads vbmeta from each
-    //     partition. This is only allowed when the device is unlocked.
-    //
-    //   - a valid unique_ptr with status kAvbHandleSuccess: the metadata
-    //     is verified and can be trusted.
-    //
-    static FsManagerAvbUniquePtr Open(const fstab& fstab);
-    static FsManagerAvbUniquePtr Open(ByNameSymlinkMap&& by_name_symlink_map);
-
-    // Sets up dm-verity on the given fstab entry.
-    // The 'wait_for_verity_dev' parameter makes this function wait for the
-    // verity device to get created before return.
-    //
-    // Return value:
-    //   - kSuccess: successfully loads dm-verity table into kernel.
-    //   - kFailed: failed to setup dm-verity, e.g., vbmeta verification error,
-    //     failed to get the HASHTREE descriptor, runtime error when set up
-    //     device-mapper, etc.
-    //   - kDisabled: hashtree is disabled.
-    SetUpAvbHashtreeResult SetUpAvbHashtree(fstab_rec* fstab_entry, bool wait_for_verity_dev);
-
-    const std::string& avb_version() const { return avb_version_; }
-
-    FsManagerAvbHandle(const FsManagerAvbHandle&) = delete;             // no copy
-    FsManagerAvbHandle& operator=(const FsManagerAvbHandle&) = delete;  // no assignment
-
-    FsManagerAvbHandle(FsManagerAvbHandle&&) noexcept = delete;             // no move
-    FsManagerAvbHandle& operator=(FsManagerAvbHandle&&) noexcept = delete;  // no move assignment
-
-    ~FsManagerAvbHandle() {
-        if (avb_slot_data_) {
-            avb_slot_verify_data_free(avb_slot_data_);
-        }
-    };
-
-  private:
-    enum AvbHandleStatus {
-        kAvbHandleSuccess = 0,
-        kAvbHandleUninitialized,
-        kAvbHandleHashtreeDisabled,
-        kAvbHandleVerificationDisabled,
-        kAvbHandleVerificationError,
-    };
-
-    FsManagerAvbHandle() : avb_slot_data_(nullptr), status_(kAvbHandleUninitialized) {}
-    static FsManagerAvbUniquePtr DoOpen(FsManagerAvbOps* avb_ops);
-
-    AvbSlotVerifyData* avb_slot_data_;
-    AvbHandleStatus status_;
-    std::string avb_version_;
-};
-
-#endif /* __CORE_FS_MGR_AVB_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..f33fc02
--- /dev/null
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -0,0 +1,76 @@
+/*
+ * 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 <chrono>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <libdm/dm.h>
+#include <liblp/liblp.h>
+
+namespace android {
+namespace fs_mgr {
+
+// Read metadata from the current slot.
+std::unique_ptr<LpMetadata> ReadCurrentMetadata(const std::string& block_device);
+
+// Create block devices for all logical partitions in the given metadata. The
+// metadata must have been read from the current slot.
+bool CreateLogicalPartitions(const LpMetadata& metadata, const std::string& block_device);
+
+// Create block devices for all logical partitions. This is a convenience
+// method for ReadMetadata and CreateLogicalPartitions.
+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. If |force_writable| is true, the "readonly" flag will be ignored
+// so the partition can be flashed.
+//
+// If |timeout_ms| is non-zero, then CreateLogicalPartition will block for the
+// given amount of time until the path returned in |path| is available.
+bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot,
+                            const std::string& partition_name, bool force_writable,
+                            const std::chrono::milliseconds& timeout_ms, std::string* path);
+
+// Same as above, but with a given metadata object. Care should be taken that
+// the metadata represents a valid partition layout.
+bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
+                            const std::string& partition_name, bool force_writable,
+                            const std::chrono::milliseconds& timeout_ms, std::string* path);
+
+// Destroy the block device for a logical partition, by name. If |timeout_ms|
+// is non-zero, then this will block until the device path has been unlinked.
+bool DestroyLogicalPartition(const std::string& name, const std::chrono::milliseconds& timeout_ms);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif  // __CORE_FS_MGR_DM_LINEAR_H
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
new file mode 100644
index 0000000..9a7381f
--- /dev/null
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -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.
+ */
+
+#pragma once
+
+#include <fstab/fstab.h>
+
+#include <string>
+#include <vector>
+
+android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
+
+bool fs_mgr_overlayfs_mount_all(android::fs_mgr::Fstab* fstab);
+std::vector<std::string> fs_mgr_overlayfs_required_devices(android::fs_mgr::Fstab* fstab);
+bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
+                            bool* change = nullptr, bool force = true);
+bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
+bool fs_mgr_overlayfs_is_setup();
+bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev);
+std::string fs_mgr_get_context(const std::string& mount_point);
+
+enum class OverlayfsValidResult {
+    kNotSupported = 0,
+    kOk,
+    kOverrideCredsRequired,
+};
+OverlayfsValidResult fs_mgr_overlayfs_valid();
diff --git a/fs_mgr/include/fs_mgr_vendor_overlay.h b/fs_mgr/include/fs_mgr_vendor_overlay.h
new file mode 100644
index 0000000..9771a0c
--- /dev/null
+++ b/fs_mgr/include/fs_mgr_vendor_overlay.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 <fstab/fstab.h>
+
+bool fs_mgr_vendor_overlay_mount_all();
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index e8da2ac..c7193ab 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -14,80 +14,103 @@
  * limitations under the License.
  */
 
-#ifndef __CORE_FS_TAB_H
-#define __CORE_FS_TAB_H
+#pragma once
 
-#include <linux/dm-ioctl.h>
-#include <stdbool.h>
 #include <stdint.h>
-#include <stdio.h>
+#include <sys/types.h>
 
 #include <set>
 #include <string>
-
-/*
- * The entries must be kept in the same order as they were seen in the fstab.
- * Unless explicitly requested, a lookup on mount point should always
- * return the 1st one.
- */
-struct fstab {
-    int num_entries;
-    struct fstab_rec* recs;
-    char* fstab_filename;
-};
-
-struct fstab_rec {
-    char* blk_device;
-    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;
-    int partnum;
-    int swap_prio;
-    int max_comp_streams;
-    unsigned int zram_size;
-    uint64_t reserved_size;
-    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();
-struct fstab* fs_mgr_read_fstab_dt();
-struct fstab* fs_mgr_read_fstab(const char* fstab_path);
-void fs_mgr_free_fstab(struct fstab* fstab);
-
-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 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);
-int fs_mgr_is_verifyatboot(const struct fstab_rec* fstab);
-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);
-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(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_has_sysfs_path(const struct fstab_rec* fstab);
+#include <vector>
 
 std::string fs_mgr_get_slot_suffix();
-std::set<std::string> fs_mgr_get_boot_devices();
+std::string fs_mgr_get_other_slot_suffix();
 
-#endif /* __CORE_FS_TAB_H */
+namespace android {
+namespace fs_mgr {
+
+struct FstabEntry {
+    std::string blk_device;
+    std::string logical_partition_name;
+    std::string mount_point;
+    std::string fs_type;
+    unsigned long flags = 0;
+    std::string fs_options;
+    std::string key_loc;
+    std::string key_dir;
+    off64_t length = 0;
+    std::string label;
+    int partnum = -1;
+    int swap_prio = -1;
+    int max_comp_streams = 0;
+    off64_t zram_size = 0;
+    off64_t reserved_size = 0;
+    std::string file_contents_mode;
+    std::string file_names_mode;
+    off64_t erase_blk_size = 0;
+    off64_t logical_blk_size = 0;
+    std::string sysfs_path;
+    std::string vbmeta_partition;
+    std::string zram_loopback_path;
+    uint64_t zram_loopback_size = 512 * 1024 * 1024;  // 512MB by default;
+    std::string zram_backing_dev_path;
+    std::string avb_keys;
+
+    struct FsMgrFlags {
+        bool wait : 1;
+        bool check : 1;
+        bool crypt : 1;
+        bool nonremovable : 1;
+        bool vold_managed : 1;
+        bool recovery_only : 1;
+        bool verify : 1;
+        bool force_crypt : 1;
+        bool no_emulated_sd : 1;  // No emulated sdcard daemon; sd card is the only external
+                                  // storage.
+        bool no_trim : 1;
+        bool file_encryption : 1;
+        bool formattable : 1;
+        bool slot_select : 1;
+        bool force_fde_or_fbe : 1;
+        bool late_mount : 1;
+        bool no_fail : 1;
+        bool verify_at_boot : 1;
+        bool quota : 1;
+        bool avb : 1;
+        bool logical : 1;
+        bool checkpoint_blk : 1;
+        bool checkpoint_fs : 1;
+        bool first_stage_mount : 1;
+        bool slot_select_other : 1;
+        bool fs_verity : 1;
+    } fs_mgr_flags = {};
+
+    bool is_encryptable() const {
+        return fs_mgr_flags.crypt || fs_mgr_flags.force_crypt || fs_mgr_flags.force_fde_or_fbe;
+    }
+};
+
+// An Fstab is a collection of FstabEntry structs.
+// The entries must be kept in the same order as they were seen in the fstab.
+// Unless explicitly requested, a lookup on mount point should always return the 1st one.
+using Fstab = std::vector<FstabEntry>;
+
+bool ReadFstabFromFile(const std::string& path, Fstab* fstab);
+bool ReadFstabFromDt(Fstab* fstab, bool log = true);
+bool ReadDefaultFstab(Fstab* fstab);
+bool SkipMountingPartitions(Fstab* fstab);
+
+FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
+
+// Helper method to build a GSI fstab entry for mounting /system.
+FstabEntry BuildGsiSystemFstabEntry();
+
+std::set<std::string> GetBootDevices();
+
+// Return the name of the dm-verity device for the given fstab entry. This does
+// not check whether the device is valid or exists; it merely returns the
+// expected name.
+std::string GetVerityDeviceName(const FstabEntry& entry);
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
new file mode 100644
index 0000000..21255df
--- /dev/null
+++ b/fs_mgr/libdm/Android.bp
@@ -0,0 +1,57 @@
+//
+// 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,
+    host_supported: true,
+
+    export_include_dirs: ["include"],
+
+    srcs: [
+        "dm_table.cpp",
+        "dm_target.cpp",
+        "dm.cpp",
+        "loop_control.cpp",
+    ],
+
+    header_libs: [
+        "libbase_headers",
+        "liblog_headers",
+    ],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+}
+
+cc_test {
+    name: "libdm_test",
+    defaults: ["fs_mgr_defaults"],
+    static_libs: [
+        "libdm",
+        "libbase",
+        "libfs_mgr",
+        "liblog",
+    ],
+    srcs: [
+        "dm_test.cpp",
+        "loop_control_test.cpp",
+        "test_util.cpp",
+    ]
+}
diff --git a/fs_mgr/libdm/Android.mk b/fs_mgr/libdm/Android.mk
new file mode 100644
index 0000000..6aedc25
--- /dev/null
+++ b/fs_mgr/libdm/Android.mk
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := VtsKernelLibdmTest
+-include test/vts/tools/build/Android.host_config.mk
diff --git a/fs_mgr/libdm/AndroidTest.xml b/fs_mgr/libdm/AndroidTest.xml
new file mode 100644
index 0000000..b4e0c23
--- /dev/null
+++ b/fs_mgr/libdm/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for VTS VtsKernelLibdmTest">
+    <option name="config-descriptor:metadata" key="plan" value="vts-kernel" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+        <option name="abort-on-push-failure" value="false"/>
+        <option name="push-group" value="HostDrivenTest.push"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+      <option name="test-module-name" value="VtsKernelLibdmTest"/>
+        <option name="binary-test-source" value="_32bit::DATA/nativetest/libdm_test/libdm_test" />
+        <option name="binary-test-source" value="_64bit::DATA/nativetest64/libdm_test/libdm_test" />
+        <option name="binary-test-type" value="gtest"/>
+        <option name="test-timeout" value="1m"/>
+        <option name="precondition-first-api-level" value="29" />
+    </test>
+</configuration>
+
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
new file mode 100644
index 0000000..d54b6ef
--- /dev/null
+++ b/fs_mgr/libdm/dm.cpp
@@ -0,0 +1,400 @@
+/*
+ * 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/logging.h>
+#include <android-base/macros.h>
+
+namespace android {
+namespace dm {
+
+DeviceMapper::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";
+    }
+}
+
+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::GetState(const std::string& name) const {
+    struct dm_ioctl io;
+    InitIo(&io, name);
+    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+        return DmDeviceState::INVALID;
+    }
+    if ((io.flags & DM_ACTIVE_PRESENT_FLAG) && !(io.flags & DM_SUSPEND_FLAG)) {
+        return DmDeviceState::ACTIVE;
+    }
+    return DmDeviceState::SUSPENDED;
+}
+
+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::GetTargetByName(const std::string& name, DmTargetTypeInfo* info) {
+    std::vector<DmTargetTypeInfo> targets;
+    if (!GetAvailableTargets(&targets)) {
+        return false;
+    }
+    for (const auto& target : targets) {
+        if (target.name() == name) {
+            if (info) *info = target;
+            return true;
+        }
+    }
+    return false;
+}
+
+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(WARNING) << "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::GetDeviceNumber(const std::string& name, dev_t* dev) {
+    struct dm_ioctl io;
+    InitIo(&io, name);
+    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+        PLOG(WARNING) << "DM_DEV_STATUS failed for " << name;
+        return false;
+    }
+    *dev = io.dev;
+    return true;
+}
+
+bool DeviceMapper::GetDeviceString(const std::string& name, std::string* dev) {
+    dev_t num;
+    if (!GetDeviceNumber(name, &num)) {
+        return false;
+    }
+    *dev = std::to_string(major(num)) + ":" + std::to_string(minor(num));
+    return true;
+}
+
+bool DeviceMapper::GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {
+    return GetTable(name, 0, table);
+}
+
+bool DeviceMapper::GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) {
+    return GetTable(name, DM_STATUS_TABLE_FLAG, table);
+}
+
+// private methods of DeviceMapper
+bool DeviceMapper::GetTable(const std::string& name, uint32_t flags,
+                            std::vector<TargetInfo>* table) {
+    std::vector<char> buffer;
+    struct dm_ioctl* io = nullptr;
+
+    for (buffer.resize(4096);; buffer.resize(buffer.size() * 2)) {
+        io = reinterpret_cast<struct dm_ioctl*>(&buffer[0]);
+
+        InitIo(io, name);
+        io->data_size = buffer.size();
+        io->data_start = sizeof(*io);
+        io->flags = flags;
+        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)) break;
+    }
+
+    uint32_t cursor = io->data_start;
+    uint32_t data_end = std::min(io->data_size, uint32_t(buffer.size()));
+    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;
+}
+
+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()) {
+        snprintf(io->name, sizeof(io->name), "%s", name.c_str());
+    }
+}
+
+std::string DeviceMapper::GetTargetType(const struct dm_target_spec& spec) {
+    if (const void* p = memchr(spec.target_type, '\0', sizeof(spec.target_type))) {
+        ptrdiff_t length = reinterpret_cast<const char*>(p) - spec.target_type;
+        return std::string{spec.target_type, static_cast<size_t>(length)};
+    }
+    return std::string{spec.target_type, sizeof(spec.target_type)};
+}
+
+}  // 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..da1013e
--- /dev/null
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -0,0 +1,225 @@
+/*
+ * 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 <stdio.h>
+#include <sys/types.h>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/parseint.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();
+    snprintf(spec->target_type, sizeof(spec->target_type), "%s", name().c_str());
+    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_;
+}
+
+std::string DmTargetSnapshot::name() const {
+    if (mode_ == SnapshotStorageMode::Merge) {
+        return "snapshot-merge";
+    }
+    return "snapshot";
+}
+
+std::string DmTargetSnapshot::GetParameterString() const {
+    std::string mode;
+    switch (mode_) {
+        case SnapshotStorageMode::Persistent:
+        case SnapshotStorageMode::Merge:
+            // Note: "O" lets us query for overflow in the status message. This
+            // is only supported on kernels 4.4+. On earlier kernels, an overflow
+            // will be reported as "Invalid" in the status string.
+            mode = "P";
+            if (ReportsOverflow(name())) {
+                mode += "O";
+            }
+            break;
+        case SnapshotStorageMode::Transient:
+            mode = "N";
+            break;
+        default:
+            LOG(ERROR) << "DmTargetSnapshot unknown mode";
+            break;
+    }
+    return base_device_ + " " + cow_device_ + " " + mode + " " + std::to_string(chunk_size_);
+}
+
+bool DmTargetSnapshot::ReportsOverflow(const std::string& target_type) {
+    DeviceMapper& dm = DeviceMapper::Instance();
+    DmTargetTypeInfo info;
+    if (!dm.GetTargetByName(target_type, &info)) {
+        return false;
+    }
+    if (target_type == "snapshot") {
+        return info.IsAtLeast(1, 15, 0);
+    }
+    if (target_type == "snapshot-merge") {
+        return info.IsAtLeast(1, 4, 0);
+    }
+    return false;
+}
+
+bool DmTargetSnapshot::ParseStatusText(const std::string& text, Status* status) {
+    auto sections = android::base::Split(text, " ");
+    if (sections.size() == 1) {
+        // This is probably an error code, "Invalid" is possible as is "Overflow"
+        // on 4.4+.
+        status->error = text;
+        return true;
+    }
+    if (sections.size() != 2) {
+        LOG(ERROR) << "snapshot status should have two components";
+        return false;
+    }
+    auto sector_info = android::base::Split(sections[0], "/");
+    if (sector_info.size() != 2) {
+        LOG(ERROR) << "snapshot sector info should have two components";
+        return false;
+    }
+    if (!android::base::ParseUint(sections[1], &status->metadata_sectors)) {
+        LOG(ERROR) << "could not parse metadata sectors";
+        return false;
+    }
+    if (!android::base::ParseUint(sector_info[0], &status->sectors_allocated)) {
+        LOG(ERROR) << "could not parse sectors allocated";
+        return false;
+    }
+    if (!android::base::ParseUint(sector_info[1], &status->total_sectors)) {
+        LOG(ERROR) << "could not parse total sectors";
+        return false;
+    }
+    return true;
+}
+
+std::string DmTargetCrypt::GetParameterString() const {
+    std::vector<std::string> argv = {
+            cipher_,
+            key_,
+            std::to_string(iv_sector_offset_),
+            device_,
+            std::to_string(device_sector_),
+    };
+
+    std::vector<std::string> extra_argv;
+    if (allow_discards_) extra_argv.emplace_back("allow_discards");
+    if (allow_encrypt_override_) extra_argv.emplace_back("allow_encrypt_override");
+    if (iv_large_sectors_) extra_argv.emplace_back("iv_large_sectors");
+    if (sector_size_) extra_argv.emplace_back("sector_size:" + std::to_string(sector_size_));
+
+    if (!extra_argv.empty()) argv.emplace_back(std::to_string(extra_argv.size()));
+
+    argv.insert(argv.end(), extra_argv.begin(), extra_argv.end());
+    return android::base::Join(argv, " ");
+}
+
+std::string DmTargetDefaultKey::GetParameterString() const {
+    return cipher_ + " " + key_ + " " + blockdev_ + " " + std::to_string(start_sector_);
+}
+
+}  // 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..c5881dd
--- /dev/null
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -0,0 +1,476 @@
+/*
+ * 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 <iostream>
+#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 std::chrono_literals;
+using namespace android::dm;
+using unique_fd = android::base::unique_fd;
+
+TEST(libdm, HasMinimumTargets) {
+    DmTargetTypeInfo info;
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    ASSERT_TRUE(dm.GetTargetByName("linear", &info));
+}
+
+// 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) noexcept
+        : 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) noexcept {
+        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.Emplace<DmTargetLinear>(0, 1, loop_a.device(), 0));
+    ASSERT_TRUE(table.Emplace<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());
+
+    auto& dm = DeviceMapper::Instance();
+
+    dev_t dev_number;
+    ASSERT_TRUE(dm.GetDeviceNumber(dev.name(), &dev_number));
+    ASSERT_NE(dev_number, 0);
+
+    std::string dev_string;
+    ASSERT_TRUE(dm.GetDeviceString(dev.name(), &dev_string));
+    ASSERT_FALSE(dev_string.empty());
+
+    // 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.
+    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);
+
+    // Test GetTargetType().
+    EXPECT_EQ(DeviceMapper::GetTargetType(targets[0].spec), std::string{"linear"});
+    EXPECT_EQ(DeviceMapper::GetTargetType(targets[1].spec), std::string{"linear"});
+
+    // 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);
+}
+
+TEST(libdm, DmSnapshotArgs) {
+    DmTargetSnapshot target1(0, 512, "base", "cow", SnapshotStorageMode::Persistent, 8);
+    if (DmTargetSnapshot::ReportsOverflow("snapshot")) {
+        EXPECT_EQ(target1.GetParameterString(), "base cow PO 8");
+    } else {
+        EXPECT_EQ(target1.GetParameterString(), "base cow P 8");
+    }
+    EXPECT_EQ(target1.name(), "snapshot");
+
+    DmTargetSnapshot target2(0, 512, "base", "cow", SnapshotStorageMode::Transient, 8);
+    EXPECT_EQ(target2.GetParameterString(), "base cow N 8");
+    EXPECT_EQ(target2.name(), "snapshot");
+
+    DmTargetSnapshot target3(0, 512, "base", "cow", SnapshotStorageMode::Merge, 8);
+    if (DmTargetSnapshot::ReportsOverflow("snapshot-merge")) {
+        EXPECT_EQ(target3.GetParameterString(), "base cow PO 8");
+    } else {
+        EXPECT_EQ(target3.GetParameterString(), "base cow P 8");
+    }
+    EXPECT_EQ(target3.name(), "snapshot-merge");
+}
+
+TEST(libdm, DmSnapshotOriginArgs) {
+    DmTargetSnapshotOrigin target(0, 512, "base");
+    EXPECT_EQ(target.GetParameterString(), "base");
+    EXPECT_EQ(target.name(), "snapshot-origin");
+}
+
+class SnapshotTestHarness final {
+  public:
+    bool Setup();
+    bool Merge();
+
+    std::string origin_dev() const { return origin_dev_->path(); }
+    std::string snapshot_dev() const { return snapshot_dev_->path(); }
+
+    int base_fd() const { return base_fd_; }
+
+    static const uint64_t kBaseDeviceSize = 1024 * 1024;
+    static const uint64_t kCowDeviceSize = 1024 * 64;
+    static const uint64_t kSectorSize = 512;
+
+  private:
+    void SetupImpl();
+    void MergeImpl();
+
+    unique_fd base_fd_;
+    unique_fd cow_fd_;
+    unique_ptr<LoopDevice> base_loop_;
+    unique_ptr<LoopDevice> cow_loop_;
+    unique_ptr<TempDevice> origin_dev_;
+    unique_ptr<TempDevice> snapshot_dev_;
+    bool setup_ok_ = false;
+    bool merge_ok_ = false;
+};
+
+bool SnapshotTestHarness::Setup() {
+    SetupImpl();
+    return setup_ok_;
+}
+
+void SnapshotTestHarness::SetupImpl() {
+    base_fd_ = CreateTempFile("base_device", kBaseDeviceSize);
+    ASSERT_GE(base_fd_, 0);
+    cow_fd_ = CreateTempFile("cow_device", kCowDeviceSize);
+    ASSERT_GE(cow_fd_, 0);
+
+    base_loop_ = std::make_unique<LoopDevice>(base_fd_);
+    ASSERT_TRUE(base_loop_->valid());
+    cow_loop_ = std::make_unique<LoopDevice>(cow_fd_);
+    ASSERT_TRUE(cow_loop_->valid());
+
+    DmTable origin_table;
+    ASSERT_TRUE(origin_table.AddTarget(make_unique<DmTargetSnapshotOrigin>(
+            0, kBaseDeviceSize / kSectorSize, base_loop_->device())));
+    ASSERT_TRUE(origin_table.valid());
+
+    origin_dev_ = std::make_unique<TempDevice>("libdm-test-dm-snapshot-origin", origin_table);
+    ASSERT_TRUE(origin_dev_->valid());
+    ASSERT_FALSE(origin_dev_->path().empty());
+    ASSERT_TRUE(origin_dev_->WaitForUdev());
+
+    // chunk size = 4K blocks.
+    DmTable snap_table;
+    ASSERT_TRUE(snap_table.AddTarget(make_unique<DmTargetSnapshot>(
+            0, kBaseDeviceSize / kSectorSize, base_loop_->device(), cow_loop_->device(),
+            SnapshotStorageMode::Persistent, 8)));
+    ASSERT_TRUE(snap_table.valid());
+
+    snapshot_dev_ = std::make_unique<TempDevice>("libdm-test-dm-snapshot", snap_table);
+    ASSERT_TRUE(snapshot_dev_->valid());
+    ASSERT_FALSE(snapshot_dev_->path().empty());
+    ASSERT_TRUE(snapshot_dev_->WaitForUdev());
+
+    setup_ok_ = true;
+}
+
+bool SnapshotTestHarness::Merge() {
+    MergeImpl();
+    return merge_ok_;
+}
+
+void SnapshotTestHarness::MergeImpl() {
+    DmTable merge_table;
+    ASSERT_TRUE(merge_table.AddTarget(
+            make_unique<DmTargetSnapshot>(0, kBaseDeviceSize / kSectorSize, base_loop_->device(),
+                                          cow_loop_->device(), SnapshotStorageMode::Merge, 8)));
+    ASSERT_TRUE(merge_table.valid());
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    ASSERT_TRUE(dm.LoadTableAndActivate("libdm-test-dm-snapshot", merge_table));
+
+    while (true) {
+        vector<DeviceMapper::TargetInfo> status;
+        ASSERT_TRUE(dm.GetTableStatus("libdm-test-dm-snapshot", &status));
+        ASSERT_EQ(status.size(), 1);
+        ASSERT_EQ(strncmp(status[0].spec.target_type, "snapshot-merge", strlen("snapshot-merge")),
+                  0);
+
+        DmTargetSnapshot::Status merge_status;
+        ASSERT_TRUE(DmTargetSnapshot::ParseStatusText(status[0].data, &merge_status));
+        ASSERT_TRUE(merge_status.error.empty());
+        if (merge_status.sectors_allocated == merge_status.metadata_sectors) {
+            break;
+        }
+
+        std::this_thread::sleep_for(250ms);
+    }
+
+    merge_ok_ = true;
+}
+
+bool CheckSnapshotAvailability() {
+    DmTargetTypeInfo info;
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    if (!dm.GetTargetByName("snapshot", &info)) {
+        cout << "snapshot module not enabled; skipping test" << std::endl;
+        return false;
+    }
+    if (!dm.GetTargetByName("snapshot-merge", &info)) {
+        cout << "snapshot-merge module not enabled; skipping test" << std::endl;
+        return false;
+    }
+    if (!dm.GetTargetByName("snapshot-origin", &info)) {
+        cout << "snapshot-origin module not enabled; skipping test" << std::endl;
+        return false;
+    }
+    return true;
+}
+
+TEST(libdm, DmSnapshot) {
+    if (!CheckSnapshotAvailability()) {
+        return;
+    }
+
+    SnapshotTestHarness harness;
+    ASSERT_TRUE(harness.Setup());
+
+    // Open the dm devices.
+    unique_fd origin_fd(open(harness.origin_dev().c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_GE(origin_fd, 0);
+    unique_fd snapshot_fd(open(harness.snapshot_dev().c_str(), O_RDWR | O_CLOEXEC | O_SYNC));
+    ASSERT_GE(snapshot_fd, 0);
+
+    // Write to the first block of the snapshot device.
+    std::string data("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
+    ASSERT_TRUE(android::base::WriteFully(snapshot_fd, data.data(), data.size()));
+    ASSERT_EQ(lseek(snapshot_fd, 0, SEEK_SET), 0);
+
+    // We should get the same data back from the snapshot device.
+    std::string read(data.size(), '\0');
+    ASSERT_TRUE(android::base::ReadFully(snapshot_fd, read.data(), read.size()));
+    ASSERT_EQ(read, data);
+
+    // We should see the original data from the origin device.
+    std::string zeroes(data.size(), '\0');
+    ASSERT_TRUE(android::base::ReadFully(origin_fd, read.data(), read.size()));
+    ASSERT_EQ(lseek(snapshot_fd, 0, SEEK_SET), 0);
+    ASSERT_EQ(read, zeroes);
+
+    // We should also see the original data from the base device.
+    ASSERT_TRUE(android::base::ReadFully(harness.base_fd(), read.data(), read.size()));
+    ASSERT_EQ(lseek(harness.base_fd(), 0, SEEK_SET), 0);
+    ASSERT_EQ(read, zeroes);
+
+    // Now, perform the merge and wait.
+    ASSERT_TRUE(harness.Merge());
+
+    // Reading from the base device should give us the modified data.
+    ASSERT_TRUE(android::base::ReadFully(harness.base_fd(), read.data(), read.size()));
+    ASSERT_EQ(lseek(harness.base_fd(), 0, SEEK_SET), 0);
+    ASSERT_EQ(read, data);
+}
+
+TEST(libdm, DmSnapshotOverflow) {
+    if (!CheckSnapshotAvailability()) {
+        return;
+    }
+
+    SnapshotTestHarness harness;
+    ASSERT_TRUE(harness.Setup());
+
+    // Open the dm devices.
+    unique_fd snapshot_fd(open(harness.snapshot_dev().c_str(), O_RDWR | O_CLOEXEC));
+    ASSERT_GE(snapshot_fd, 0);
+
+    // Fill the copy-on-write device until it overflows.
+    uint64_t bytes_remaining = SnapshotTestHarness::kCowDeviceSize;
+    uint8_t byte = 1;
+    while (bytes_remaining) {
+        std::string data(4096, char(byte));
+        if (!android::base::WriteFully(snapshot_fd, data.data(), data.size())) {
+            ASSERT_EQ(errno, EIO);
+            break;
+        }
+        bytes_remaining -= data.size();
+    }
+
+    // If writes succeed (because they are buffered), then we should expect an
+    // fsync to fail with EIO.
+    if (!bytes_remaining) {
+        ASSERT_EQ(fsync(snapshot_fd), -1);
+        ASSERT_EQ(errno, EIO);
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+
+    vector<DeviceMapper::TargetInfo> target_status;
+    ASSERT_TRUE(dm.GetTableStatus("libdm-test-dm-snapshot", &target_status));
+    ASSERT_EQ(target_status.size(), 1);
+    ASSERT_EQ(strncmp(target_status[0].spec.target_type, "snapshot", strlen("snapshot")), 0);
+
+    DmTargetSnapshot::Status status;
+    ASSERT_TRUE(DmTargetSnapshot::ParseStatusText(target_status[0].data, &status));
+    if (DmTargetSnapshot::ReportsOverflow("snapshot")) {
+        ASSERT_EQ(status.error, "Overflow");
+    } else {
+        ASSERT_EQ(status.error, "Invalid");
+    }
+}
+
+TEST(libdm, CryptArgs) {
+    DmTargetCrypt target1(0, 512, "sha1", "abcdefgh", 50, "/dev/loop0", 100);
+    ASSERT_EQ(target1.name(), "crypt");
+    ASSERT_TRUE(target1.Valid());
+    ASSERT_EQ(target1.GetParameterString(), "sha1 abcdefgh 50 /dev/loop0 100");
+
+    DmTargetCrypt target2(0, 512, "sha1", "abcdefgh", 50, "/dev/loop0", 100);
+    target2.SetSectorSize(64);
+    target2.AllowDiscards();
+    target2.SetIvLargeSectors();
+    target2.AllowEncryptOverride();
+    ASSERT_EQ(target2.GetParameterString(),
+              "sha1 abcdefgh 50 /dev/loop0 100 4 allow_discards allow_encrypt_override "
+              "iv_large_sectors sector_size:64");
+}
+
+TEST(libdm, DefaultKeyArgs) {
+    DmTargetDefaultKey target(0, 4096, "AES-256-XTS", "abcdef0123456789", "/dev/loop0", 0);
+    ASSERT_EQ(target.name(), "default-key");
+    ASSERT_TRUE(target.Valid());
+    ASSERT_EQ(target.GetParameterString(), "AES-256-XTS abcdef0123456789 /dev/loop0 0");
+}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
new file mode 100644
index 0000000..08376c0
--- /dev/null
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -0,0 +1,184 @@
+/*
+ *  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 <linux/types.h>
+#include <stdint.h>
+#include <sys/sysmacros.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#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 GetState(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);
+
+    // Finds a target by name and returns its information if found. |info| may
+    // be null to check for the existence of a target.
+    bool GetTargetByName(const std::string& name, DmTargetTypeInfo* info);
+
+    // 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);
+
+    // Returns the dev_t for the named device-mapper node.
+    bool GetDeviceNumber(const std::string& name, dev_t* dev);
+
+    // Returns a major:minor string for the named device-mapper node, that can
+    // be used as inputs to DmTargets that take a block device.
+    bool GetDeviceString(const std::string& name, std::string* dev);
+
+    // 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);
+
+    // Identical to GetTableStatus, except also retrives the active table for the device
+    // mapper device from the kernel.
+    bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table);
+
+    static std::string GetTargetType(const struct dm_target_spec& spec);
+
+  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;
+
+    bool GetTable(const std::string& name, uint32_t flags, std::vector<TargetInfo>* table);
+
+    void InitIo(struct dm_ioctl* io, const std::string& name = std::string()) const;
+
+    DeviceMapper();
+
+    // 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..ee66653
--- /dev/null
+++ b/fs_mgr/libdm/include/libdm/dm_table.h
@@ -0,0 +1,92 @@
+/*
+ *  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);
+
+    // Adds a target, constructing it in-place for convenience. For example,
+    //
+    //   table.Emplace<DmTargetZero>(0, num_sectors);
+    template <typename T, typename... Args>
+    bool Emplace(Args&&... args) {
+        return AddTarget(std::make_unique<T>(std::forward<Args>(args)...));
+    }
+
+    // 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 total 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..722922d
--- /dev/null
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -0,0 +1,301 @@
+/*
+ *  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>
+
+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_);
+    }
+
+    uint32_t major_version() const { return major_; }
+    uint32_t minor_version() const { return minor_; }
+    uint32_t patch_level() const { return patch_; }
+
+    bool IsAtLeast(uint32_t major, uint32_t minor, uint32_t patch) const {
+        if (major_ > major) return true;
+        if (major_ < major) return false;
+        if (minor_ > minor) return true;
+        if (minor_ < minor) return false;
+        return patch_ >= 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_;
+};
+
+// dm-bow is the backup on write target that can provide checkpoint capability
+// for file systems that do not support checkpoints natively
+class DmTargetBow final : public DmTarget {
+  public:
+    DmTargetBow(uint64_t start, uint64_t length, const std::string& target_string)
+        : DmTarget(start, length), target_string_(target_string) {}
+
+    std::string name() const override { return "bow"; }
+    std::string GetParameterString() const override { return target_string_; }
+
+  private:
+    std::string target_string_;
+};
+
+enum class SnapshotStorageMode {
+    // The snapshot will be persisted to the COW device.
+    Persistent,
+    // The snapshot will be lost on reboot.
+    Transient,
+    // The snapshot will be merged from the COW device into the base device,
+    // in the background.
+    Merge
+};
+
+// Writes to a snapshot device will be written to the given COW device. Reads
+// will read from the COW device or base device. The chunk size is specified
+// in sectors.
+class DmTargetSnapshot final : public DmTarget {
+  public:
+    DmTargetSnapshot(uint64_t start, uint64_t length, const std::string& base_device,
+                     const std::string& cow_device, SnapshotStorageMode mode, uint64_t chunk_size)
+        : DmTarget(start, length),
+          base_device_(base_device),
+          cow_device_(cow_device),
+          mode_(mode),
+          chunk_size_(chunk_size) {}
+
+    std::string name() const override;
+    std::string GetParameterString() const override;
+    bool Valid() const override { return true; }
+
+    struct Status {
+        uint64_t sectors_allocated;
+        uint64_t total_sectors;
+        uint64_t metadata_sectors;
+        std::string error;
+    };
+
+    static bool ParseStatusText(const std::string& text, Status* status);
+    static bool ReportsOverflow(const std::string& target_type);
+
+  private:
+    std::string base_device_;
+    std::string cow_device_;
+    SnapshotStorageMode mode_;
+    uint64_t chunk_size_;
+};
+
+// snapshot-origin will read/write directly to the backing device, updating any
+// snapshot devices with a matching origin.
+class DmTargetSnapshotOrigin final : public DmTarget {
+  public:
+    DmTargetSnapshotOrigin(uint64_t start, uint64_t length, const std::string& device)
+        : DmTarget(start, length), device_(device) {}
+
+    std::string name() const override { return "snapshot-origin"; }
+    std::string GetParameterString() const override { return device_; }
+    bool Valid() const override { return true; }
+
+  private:
+    std::string device_;
+};
+
+class DmTargetCrypt final : public DmTarget {
+  public:
+    DmTargetCrypt(uint64_t start, uint64_t length, const std::string& cipher,
+                  const std::string& key, uint64_t iv_sector_offset, const std::string& device,
+                  uint64_t device_sector)
+        : DmTarget(start, length),
+          cipher_(cipher),
+          key_(key),
+          iv_sector_offset_(iv_sector_offset),
+          device_(device),
+          device_sector_(device_sector) {}
+
+    void AllowDiscards() { allow_discards_ = true; }
+    void AllowEncryptOverride() { allow_encrypt_override_ = true; }
+    void SetIvLargeSectors() { iv_large_sectors_ = true; }
+    void SetSectorSize(uint32_t sector_size) { sector_size_ = sector_size; }
+
+    std::string name() const override { return "crypt"; }
+    bool Valid() const override { return true; }
+    std::string GetParameterString() const override;
+
+  private:
+    std::string cipher_;
+    std::string key_;
+    uint64_t iv_sector_offset_;
+    std::string device_;
+    uint64_t device_sector_;
+    bool allow_discards_ = false;
+    bool allow_encrypt_override_ = false;
+    bool iv_large_sectors_ = false;
+    uint32_t sector_size_ = 0;
+};
+
+class DmTargetDefaultKey final : public DmTarget {
+  public:
+    DmTargetDefaultKey(uint64_t start, uint64_t length, const std::string& cipher,
+                       const std::string& key, const std::string& blockdev, uint64_t start_sector)
+        : DmTarget(start, length),
+          cipher_(cipher),
+          key_(key),
+          blockdev_(blockdev),
+          start_sector_(start_sector) {}
+
+    std::string name() const override { return "default-key"; }
+    bool Valid() const override { return true; }
+    std::string GetParameterString() const override;
+
+  private:
+    std::string cipher_;
+    std::string key_;
+    std::string blockdev_;
+    uint64_t start_sector_;
+};
+
+}  // 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..6b4c2d8
--- /dev/null
+++ b/fs_mgr/libdm/include/libdm/loop_control.h
@@ -0,0 +1,85 @@
+/*
+ *  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;
+
+    // Enable Direct I/O on a loop device. This requires kernel 4.9+.
+    static bool EnableDirectIo(int fd);
+
+    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..16bf4b0
--- /dev/null
+++ b/fs_mgr/libdm/loop_control.cpp
@@ -0,0 +1,142 @@
+/*
+ * 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;
+}
+
+bool LoopControl::EnableDirectIo(int fd) {
+#if !defined(LOOP_SET_BLOCK_SIZE)
+    static constexpr int LOOP_SET_BLOCK_SIZE = 0x4C09;
+#endif
+#if !defined(LOOP_SET_DIRECT_IO)
+    static constexpr int LOOP_SET_DIRECT_IO = 0x4C08;
+#endif
+
+    // Note: the block size has to be >= the logical block size of the underlying
+    // block device, *not* the filesystem block size.
+    if (ioctl(fd, LOOP_SET_BLOCK_SIZE, 4096)) {
+        PLOG(ERROR) << "Could not set loop device block size";
+        return false;
+    }
+    if (ioctl(fd, LOOP_SET_DIRECT_IO, 1)) {
+        PLOG(ERROR) << "Could not set loop direct IO";
+        return false;
+    }
+    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/libfiemap_writer/.clang-format b/fs_mgr/libfiemap_writer/.clang-format
new file mode 120000
index 0000000..8b770a1
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/.clang-format
@@ -0,0 +1 @@
+../../.clang-format-4
\ No newline at end of file
diff --git a/fs_mgr/libfiemap_writer/Android.bp b/fs_mgr/libfiemap_writer/Android.bp
new file mode 100644
index 0000000..ed209aa
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/Android.bp
@@ -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.
+//
+
+cc_library_static {
+    name: "libfiemap_writer",
+    defaults: ["fs_mgr_defaults"],
+    recovery_available: true,
+    export_include_dirs: ["include"],
+    cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+    ],
+
+    srcs: [
+        "fiemap_writer.cpp",
+        "split_fiemap_writer.cpp",
+        "utility.cpp",
+    ],
+
+    static_libs: [
+        "libdm",
+        "libext4_utils",
+    ],
+
+    header_libs: [
+        "libbase_headers",
+        "liblog_headers",
+    ],
+}
+
+cc_test {
+    name: "fiemap_writer_test",
+    cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+    ],
+    static_libs: [
+        "libbase",
+        "libdm",
+        "libfiemap_writer",
+        "liblog",
+    ],
+
+    data: [
+        "testdata/unaligned_file",
+        "testdata/file_4k",
+        "testdata/file_32k",
+    ],
+
+    srcs: [
+        "fiemap_writer_test.cpp",
+    ],
+}
diff --git a/fs_mgr/libfiemap_writer/Android.mk b/fs_mgr/libfiemap_writer/Android.mk
new file mode 100644
index 0000000..3c07b8e
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/Android.mk
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 := VtsFiemapWriterTest
+-include test/vts/tools/build/Android.host_config.mk
diff --git a/fs_mgr/libfiemap_writer/AndroidTest.xml b/fs_mgr/libfiemap_writer/AndroidTest.xml
new file mode 100644
index 0000000..08cff0e
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for VTS VtsFiemapWriterTest">
+    <option name="config-descriptor:metadata" key="plan" value="vts-kernel" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+        <option name="abort-on-push-failure" value="false"/>
+        <option name="push-group" value="HostDrivenTest.push"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+      <option name="test-module-name" value="VtsFiemapWriterTest"/>
+        <option name="binary-test-source" value="_32bit::DATA/nativetest/fiemap_writer_test/fiemap_writer_test" />
+        <option name="binary-test-source" value="_64bit::DATA/nativetest64/fiemap_writer_test/fiemap_writer_test" />
+        <option name="binary-test-type" value="gtest"/>
+        <option name="test-timeout" value="1m"/>
+    </test>
+</configuration>
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
new file mode 100644
index 0000000..0a3ba6c
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
@@ -0,0 +1,745 @@
+/*
+ * 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 <libfiemap_writer/fiemap_writer.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include <limits>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <libdm/dm.h>
+
+namespace android {
+namespace fiemap_writer {
+
+using namespace android::dm;
+
+// We are expecting no more than 512 extents in a fiemap of the file we create.
+// If we find more, then it is treated as error for now.
+static constexpr const uint32_t kMaxExtents = 512;
+
+// TODO: Fallback to using fibmap if FIEMAP_EXTENT_MERGED is set.
+static constexpr const uint32_t kUnsupportedExtentFlags =
+        FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_DELALLOC |
+        FIEMAP_EXTENT_NOT_ALIGNED | FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_DATA_TAIL |
+        FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_SHARED | FIEMAP_EXTENT_MERGED;
+
+// Large file support must be enabled.
+static_assert(sizeof(off_t) == sizeof(uint64_t));
+
+static inline void cleanup(const std::string& file_path, bool created) {
+    if (created) {
+        unlink(file_path.c_str());
+    }
+}
+
+static bool BlockDeviceToName(uint32_t major, uint32_t minor, std::string* bdev_name) {
+    // The symlinks in /sys/dev/block point to the block device node under /sys/device/..
+    // The directory name in the target corresponds to the name of the block device. We use
+    // that to extract the block device name.
+    // e.g for block device name 'ram0', there exists a symlink named '1:0' in /sys/dev/block as
+    // follows.
+    //    1:0 -> ../../devices/virtual/block/ram0
+    std::string sysfs_path = ::android::base::StringPrintf("/sys/dev/block/%u:%u", major, minor);
+    std::string sysfs_bdev;
+
+    if (!::android::base::Readlink(sysfs_path, &sysfs_bdev)) {
+        PLOG(ERROR) << "Failed to read link at: " << sysfs_path;
+        return false;
+    }
+
+    *bdev_name = ::android::base::Basename(sysfs_bdev);
+    // Paranoid sanity check to make sure we just didn't get the
+    // input in return as-is.
+    if (sysfs_bdev == *bdev_name) {
+        LOG(ERROR) << "Malformed symlink for block device: " << sysfs_bdev;
+        return false;
+    }
+
+    return true;
+}
+
+static bool ValidateDmTarget(const DeviceMapper::TargetInfo& target) {
+    const auto& entry = target.spec;
+    if (entry.sector_start != 0) {
+        LOG(INFO) << "Stopping at target with non-zero starting sector";
+        return false;
+    }
+
+    auto target_type = DeviceMapper::GetTargetType(entry);
+    if (target_type == "bow" || target_type == "default-key" || target_type == "crypt") {
+        return true;
+    }
+    if (target_type == "linear") {
+        auto pieces = android::base::Split(target.data, " ");
+        if (pieces[1] != "0") {
+            LOG(INFO) << "Stopping at complex linear target with non-zero starting sector: "
+                      << pieces[1];
+            return false;
+        }
+        return true;
+    }
+
+    LOG(INFO) << "Stopping at complex target type " << target_type;
+    return false;
+}
+
+static bool DeviceMapperStackPop(const std::string& bdev, std::string* bdev_raw) {
+    *bdev_raw = bdev;
+
+    if (!::android::base::StartsWith(bdev, "dm-")) {
+        // We are at the bottom of the device mapper stack.
+        return true;
+    }
+
+    // Get the device name.
+    auto dm_name_file = "/sys/block/" + bdev + "/dm/name";
+    std::string dm_name;
+    if (!android::base::ReadFileToString(dm_name_file, &dm_name)) {
+        PLOG(ERROR) << "Could not read file: " << dm_name_file;
+        return false;
+    }
+    dm_name = android::base::Trim(dm_name);
+
+    auto& dm = DeviceMapper::Instance();
+    std::vector<DeviceMapper::TargetInfo> table;
+    if (!dm.GetTableInfo(dm_name, &table)) {
+        LOG(ERROR) << "Could not read device-mapper table for " << dm_name << " at " << bdev;
+        return false;
+    }
+
+    // The purpose of libfiemap_writer is to provide an extent-based view into
+    // a file. This is difficult if devices are not layered in a 1:1 manner;
+    // we would have to translate and break up extents based on the actual
+    // block mapping. Since this is too complex, we simply stop processing
+    // the device-mapper stack if we encounter a complex case.
+    //
+    // It is up to the caller to decide whether stopping at a virtual block
+    // device is allowable. In most cases it is not, because we want either
+    // "userdata" or an external volume. It is useful for tests however.
+    // Callers can check by comparing the device number to that of userdata,
+    // or by checking whether is a device-mapper node.
+    if (table.size() > 1) {
+        LOG(INFO) << "Stopping at complex table for " << dm_name << " at " << bdev;
+        return true;
+    }
+    if (!ValidateDmTarget(table[0])) {
+        return true;
+    }
+
+    auto dm_leaf_dir = "/sys/block/" + bdev + "/slaves";
+    auto d = std::unique_ptr<DIR, decltype(&closedir)>(opendir(dm_leaf_dir.c_str()), closedir);
+    if (d == nullptr) {
+        PLOG(ERROR) << "Failed to open: " << dm_leaf_dir;
+        return false;
+    }
+
+    struct dirent* de;
+    uint32_t num_leaves = 0;
+    std::string bdev_next = "";
+    while ((de = readdir(d.get())) != nullptr) {
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
+            continue;
+        }
+
+        // We set the first name we find here
+        if (bdev_next.empty()) {
+            bdev_next = de->d_name;
+        }
+        num_leaves++;
+    }
+
+    // if we have more than one leaves, we return immediately. We can't continue to create the
+    // file since we don't know how to write it out using fiemap, so it will be readable via the
+    // underlying block devices later. The reader will also have to construct the same device mapper
+    // target in order read the file out.
+    if (num_leaves > 1) {
+        LOG(ERROR) << "Found " << num_leaves << " leaf block devices under device mapper device "
+                   << bdev;
+        return false;
+    }
+
+    // recursively call with the block device we found in order to pop the device mapper stack.
+    return DeviceMapperStackPop(bdev_next, bdev_raw);
+}
+
+bool FiemapWriter::GetBlockDeviceForFile(const std::string& file_path, std::string* bdev_path,
+                                         bool* uses_dm) {
+    struct stat sb;
+    if (stat(file_path.c_str(), &sb)) {
+        PLOG(ERROR) << "Failed to get stat for: " << file_path;
+        return false;
+    }
+
+    std::string bdev;
+    if (!BlockDeviceToName(major(sb.st_dev), minor(sb.st_dev), &bdev)) {
+        LOG(ERROR) << "Failed to get block device name for " << major(sb.st_dev) << ":"
+                   << minor(sb.st_dev);
+        return false;
+    }
+
+    std::string bdev_raw;
+    if (!DeviceMapperStackPop(bdev, &bdev_raw)) {
+        LOG(ERROR) << "Failed to get the bottom of the device mapper stack for device: " << bdev;
+        return false;
+    }
+
+    if (uses_dm) {
+        *uses_dm = (bdev_raw != bdev);
+    }
+
+    LOG(DEBUG) << "Popped device (" << bdev_raw << ") from device mapper stack starting with ("
+               << bdev << ")";
+
+    *bdev_path = ::android::base::StringPrintf("/dev/block/%s", bdev_raw.c_str());
+
+    // Make sure we are talking to a block device before calling it a success.
+    if (stat(bdev_path->c_str(), &sb)) {
+        PLOG(ERROR) << "Failed to get stat for block device: " << *bdev_path;
+        return false;
+    }
+
+    if ((sb.st_mode & S_IFMT) != S_IFBLK) {
+        PLOG(ERROR) << "File: " << *bdev_path << " is not a block device";
+        return false;
+    }
+
+    return true;
+}
+
+static bool GetBlockDeviceSize(int bdev_fd, const std::string& bdev_path, uint64_t* bdev_size) {
+    uint64_t size_in_bytes = 0;
+    if (ioctl(bdev_fd, BLKGETSIZE64, &size_in_bytes)) {
+        PLOG(ERROR) << "Failed to get total size for: " << bdev_path;
+        return false;
+    }
+
+    *bdev_size = size_in_bytes;
+
+    return true;
+}
+
+static uint64_t GetFileSize(const std::string& file_path) {
+    struct stat sb;
+    if (stat(file_path.c_str(), &sb)) {
+        PLOG(ERROR) << "Failed to get size for file: " << file_path;
+        return 0;
+    }
+
+    return sb.st_size;
+}
+
+static bool PerformFileChecks(const std::string& file_path, uint64_t file_size, uint64_t* blocksz,
+                              uint32_t* fs_type) {
+    struct statfs64 sfs;
+    if (statfs64(file_path.c_str(), &sfs)) {
+        PLOG(ERROR) << "Failed to read file system status at: " << file_path;
+        return false;
+    }
+
+    if (!sfs.f_bsize) {
+        LOG(ERROR) << "Unsupported block size: " << sfs.f_bsize;
+        return false;
+    }
+
+    // Check if the filesystem is of supported types.
+    // Only ext4, f2fs, and vfat are tested and supported.
+    switch (sfs.f_type) {
+        case EXT4_SUPER_MAGIC:
+        case F2FS_SUPER_MAGIC:
+        case MSDOS_SUPER_MAGIC:
+            break;
+        default:
+            LOG(ERROR) << "Unsupported file system type: 0x" << std::hex << sfs.f_type;
+            return false;
+    }
+
+    uint64_t available_bytes = sfs.f_bsize * sfs.f_bavail;
+    if (available_bytes <= file_size) {
+        LOG(ERROR) << "Not enough free space in file system to create file of size : " << file_size;
+        return false;
+    }
+
+    *blocksz = sfs.f_bsize;
+    *fs_type = sfs.f_type;
+    return true;
+}
+
+static bool FallocateFallback(int file_fd, uint64_t block_size, uint64_t file_size,
+                              const std::string& file_path,
+                              const std::function<bool(uint64_t, uint64_t)>& on_progress) {
+    // Even though this is much faster than writing zeroes, it is still slow
+    // enough that we need to fire the progress callback periodically. To
+    // easily achieve this, we seek in chunks. We use 1000 chunks since
+    // normally we only fire the callback on 1/1000th increments.
+    uint64_t bytes_per_chunk = std::max(file_size / 1000, block_size);
+
+    // Seek just to the end of each chunk and write a single byte, causing
+    // the filesystem to allocate blocks.
+    off_t cursor = 0;
+    off_t end = static_cast<off_t>(file_size);
+    while (cursor < end) {
+        cursor = std::min(static_cast<off_t>(cursor + bytes_per_chunk), end);
+        auto rv = TEMP_FAILURE_RETRY(lseek(file_fd, cursor - 1, SEEK_SET));
+        if (rv < 0) {
+            PLOG(ERROR) << "Failed to lseek " << file_path;
+            return false;
+        }
+        if (rv != cursor - 1) {
+            LOG(ERROR) << "Seek returned wrong offset " << rv << " for file " << file_path;
+            return false;
+        }
+        char buffer[] = {0};
+        if (!android::base::WriteFully(file_fd, buffer, 1)) {
+            PLOG(ERROR) << "Write failed: " << file_path;
+            return false;
+        }
+        if (on_progress && !on_progress(cursor, file_size)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
+                         uint64_t file_size, unsigned int fs_type,
+                         std::function<bool(uint64_t, uint64_t)> on_progress) {
+    // Reserve space for the file on the file system and write it out to make sure the extents
+    // don't come back unwritten. Return from this function with the kernel file offset set to 0.
+    // If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks
+    // aren't moved around.
+    switch (fs_type) {
+        case EXT4_SUPER_MAGIC:
+        case F2FS_SUPER_MAGIC:
+            if (fallocate(file_fd, FALLOC_FL_ZERO_RANGE, 0, file_size)) {
+                PLOG(ERROR) << "Failed to allocate space for file: " << file_path
+                            << " size: " << file_size;
+                return false;
+            }
+            break;
+        case MSDOS_SUPER_MAGIC:
+            // fallocate() is not supported, and not needed, since VFAT does not support holes.
+            // Instead we can perform a much faster allocation.
+            return FallocateFallback(file_fd, blocksz, file_size, file_path, on_progress);
+        default:
+            LOG(ERROR) << "Missing fallocate() support for file system " << fs_type;
+            return false;
+    }
+
+    // write zeroes in 'blocksz' byte increments until we reach file_size to make sure the data
+    // blocks are actually written to by the file system and thus getting rid of the holes in the
+    // file.
+    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksz), free);
+    if (buffer == nullptr) {
+        LOG(ERROR) << "failed to allocate memory for writing file";
+        return false;
+    }
+
+    off64_t offset = lseek64(file_fd, 0, SEEK_SET);
+    if (offset < 0) {
+        PLOG(ERROR) << "Failed to seek at the beginning of : " << file_path;
+        return false;
+    }
+
+    int permille = -1;
+    while (offset < file_size) {
+        if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) {
+            PLOG(ERROR) << "Failed to write" << blocksz << " bytes at offset" << offset
+                        << " in file " << file_path;
+            return false;
+        }
+
+        offset += blocksz;
+
+        // Don't invoke the callback every iteration - wait until a significant
+        // chunk (here, 1/1000th) of the data has been processed.
+        int new_permille = (static_cast<uint64_t>(offset) * 1000) / file_size;
+        if (new_permille != permille && static_cast<uint64_t>(offset) != file_size) {
+            if (on_progress && !on_progress(offset, file_size)) {
+                return false;
+            }
+            permille = new_permille;
+        }
+    }
+
+    if (lseek64(file_fd, 0, SEEK_SET) < 0) {
+        PLOG(ERROR) << "Failed to reset offset at the beginning of : " << file_path;
+        return false;
+    }
+
+    // flush all writes here ..
+    if (fsync(file_fd)) {
+        PLOG(ERROR) << "Failed to synchronize written file:" << file_path;
+        return false;
+    }
+
+    // Send one last progress notification.
+    if (on_progress && !on_progress(file_size, file_size)) {
+        return false;
+    }
+    return true;
+}
+
+static bool PinFile(int file_fd, const std::string& file_path, uint32_t fs_type) {
+    if (fs_type != F2FS_SUPER_MAGIC) {
+        // No pinning necessary for ext4/msdos. The blocks, once allocated, are
+        // expected to be fixed.
+        return true;
+    }
+
+// F2FS-specific ioctl
+// It requires the below kernel commit merged in v4.16-rc1.
+//   1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
+// In android-4.4,
+//   56ee1e817908 ("f2fs: updates on v4.16-rc1")
+// In android-4.9,
+//   2f17e34672a8 ("f2fs: updates on v4.16-rc1")
+// In android-4.14,
+//   ce767d9a55bc ("f2fs: updates on v4.16-rc1")
+#ifndef F2FS_IOC_SET_PIN_FILE
+#ifndef F2FS_IOCTL_MAGIC
+#define F2FS_IOCTL_MAGIC 0xf5
+#endif
+#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
+#endif
+
+    uint32_t pin_status = 1;
+    int error = ioctl(file_fd, F2FS_IOC_SET_PIN_FILE, &pin_status);
+    if (error < 0) {
+        if ((errno == ENOTTY) || (errno == ENOTSUP)) {
+            PLOG(ERROR) << "Failed to pin file, not supported by kernel: " << file_path;
+        } else {
+            PLOG(ERROR) << "Failed to pin file: " << file_path;
+        }
+        return false;
+    }
+
+    return true;
+}
+
+static bool IsFilePinned(int file_fd, const std::string& file_path, uint32_t fs_type) {
+    if (fs_type != F2FS_SUPER_MAGIC) {
+        // No pinning necessary for ext4 or vfat. The blocks, once allocated,
+        // are expected to be fixed.
+        return true;
+    }
+
+// F2FS-specific ioctl
+// It requires the below kernel commit merged in v4.16-rc1.
+//   1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
+// In android-4.4,
+//   56ee1e817908 ("f2fs: updates on v4.16-rc1")
+// In android-4.9,
+//   2f17e34672a8 ("f2fs: updates on v4.16-rc1")
+// In android-4.14,
+//   ce767d9a55bc ("f2fs: updates on v4.16-rc1")
+#ifndef F2FS_IOC_GET_PIN_FILE
+#ifndef F2FS_IOCTL_MAGIC
+#define F2FS_IOCTL_MAGIC 0xf5
+#endif
+#define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
+#endif
+
+    // f2fs: export FS_NOCOW_FL flag to user
+    uint32_t flags;
+    int error = ioctl(file_fd, FS_IOC_GETFLAGS, &flags);
+    if (error < 0) {
+        if ((errno == ENOTTY) || (errno == ENOTSUP)) {
+            PLOG(ERROR) << "Failed to get flags, not supported by kernel: " << file_path;
+        } else {
+            PLOG(ERROR) << "Failed to get flags: " << file_path;
+        }
+        return false;
+    }
+    if (!(flags & FS_NOCOW_FL)) {
+        LOG(ERROR) << "It is not pinned: " << file_path;
+        return false;
+    }
+
+    // F2FS_IOC_GET_PIN_FILE returns the number of blocks moved.
+    uint32_t moved_blocks_nr;
+    error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &moved_blocks_nr);
+    if (error < 0) {
+        if ((errno == ENOTTY) || (errno == ENOTSUP)) {
+            PLOG(ERROR) << "Failed to get file pin status, not supported by kernel: " << file_path;
+        } else {
+            PLOG(ERROR) << "Failed to get file pin status: " << file_path;
+        }
+        return false;
+    }
+
+    if (moved_blocks_nr) {
+        LOG(ERROR) << moved_blocks_nr << " blocks moved in file " << file_path;
+    }
+    return moved_blocks_nr == 0;
+}
+
+bool FiemapWriter::HasPinnedExtents(const std::string& file_path) {
+    android::base::unique_fd fd(open(file_path.c_str(), O_NOFOLLOW | O_CLOEXEC | O_RDONLY));
+    if (fd < 0) {
+        PLOG(ERROR) << "open: " << file_path;
+        return false;
+    }
+
+    struct statfs64 sfs;
+    if (fstatfs64(fd, &sfs)) {
+        PLOG(ERROR) << "fstatfs64: " << file_path;
+        return false;
+    }
+    return IsFilePinned(fd, file_path, sfs.f_type);
+}
+
+static bool ReadFiemap(int file_fd, const std::string& file_path,
+                       std::vector<struct fiemap_extent>* extents) {
+    uint64_t fiemap_size =
+            sizeof(struct fiemap_extent) + kMaxExtents * sizeof(struct fiemap_extent);
+    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, fiemap_size), free);
+    if (buffer == nullptr) {
+        LOG(ERROR) << "Failed to allocate memory for fiemap";
+        return false;
+    }
+
+    struct fiemap* fiemap = reinterpret_cast<struct fiemap*>(buffer.get());
+    fiemap->fm_start = 0;
+    fiemap->fm_length = UINT64_MAX;
+    // make sure file is synced to disk before we read the fiemap
+    fiemap->fm_flags = FIEMAP_FLAG_SYNC;
+    fiemap->fm_extent_count = kMaxExtents;
+
+    if (ioctl(file_fd, FS_IOC_FIEMAP, fiemap)) {
+        PLOG(ERROR) << "Failed to get FIEMAP from the kernel for file: " << file_path;
+        return false;
+    }
+
+    if (fiemap->fm_mapped_extents == 0) {
+        LOG(ERROR) << "File " << file_path << " has zero extents";
+        return false;
+    }
+
+    // Iterate through each extent read and make sure its valid before adding it to the vector
+    bool last_extent_seen = false;
+    struct fiemap_extent* extent = &fiemap->fm_extents[0];
+    for (uint32_t i = 0; i < fiemap->fm_mapped_extents; i++, extent++) {
+        // LogExtent(i + 1, *extent);
+        if (extent->fe_flags & kUnsupportedExtentFlags) {
+            LOG(ERROR) << "Extent " << i + 1 << " of file " << file_path
+                       << " has unsupported flags";
+            extents->clear();
+            return false;
+        }
+
+        if (extent->fe_flags & FIEMAP_EXTENT_LAST) {
+            last_extent_seen = true;
+            if (i != (fiemap->fm_mapped_extents - 1)) {
+                LOG(WARNING) << "Extents are being received out-of-order";
+            }
+        }
+        extents->emplace_back(std::move(*extent));
+    }
+
+    if (!last_extent_seen) {
+        // The file is possibly too fragmented.
+        if (fiemap->fm_mapped_extents == kMaxExtents) {
+            LOG(ERROR) << "File is too fragmented, needs more than " << kMaxExtents << " extents.";
+        }
+        extents->clear();
+    }
+
+    return last_extent_seen;
+}
+
+static bool ReadFibmap(int file_fd, const std::string& file_path,
+                       std::vector<struct fiemap_extent>* extents) {
+    struct stat s;
+    if (fstat(file_fd, &s)) {
+        PLOG(ERROR) << "Failed to stat " << file_path;
+        return false;
+    }
+
+    unsigned int blksize;
+    if (ioctl(file_fd, FIGETBSZ, &blksize) < 0) {
+        PLOG(ERROR) << "Failed to get FIGETBSZ for " << file_path;
+        return false;
+    }
+    if (!blksize) {
+        LOG(ERROR) << "Invalid filesystem block size: " << blksize;
+        return false;
+    }
+
+    uint64_t num_blocks = (s.st_size + blksize - 1) / blksize;
+    if (num_blocks > std::numeric_limits<uint32_t>::max()) {
+        LOG(ERROR) << "Too many blocks for FIBMAP (" << num_blocks << ")";
+        return false;
+    }
+
+    for (uint32_t last_block, block_number = 0; block_number < num_blocks; block_number++) {
+        uint32_t block = block_number;
+        if (ioctl(file_fd, FIBMAP, &block)) {
+            PLOG(ERROR) << "Failed to get FIBMAP for file " << file_path;
+            return false;
+        }
+        if (!block) {
+            LOG(ERROR) << "Logical block " << block_number << " is a hole, which is not supported";
+            return false;
+        }
+
+        if (!extents->empty() && block == last_block + 1) {
+            extents->back().fe_length += blksize;
+        } else {
+            extents->push_back(fiemap_extent{.fe_logical = block_number,
+                                             .fe_physical = static_cast<uint64_t>(block) * blksize,
+                                             .fe_length = static_cast<uint64_t>(blksize),
+                                             .fe_flags = 0});
+        }
+        last_block = block;
+    }
+    return true;
+}
+
+FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create,
+                                   std::function<bool(uint64_t, uint64_t)> progress) {
+    // if 'create' is false, open an existing file and do not truncate.
+    int open_flags = O_RDWR | O_CLOEXEC;
+    if (create) {
+        if (access(file_path.c_str(), F_OK) == 0) {
+            LOG(WARNING) << "File " << file_path << " already exists, truncating";
+        }
+        open_flags |= O_CREAT | O_TRUNC;
+    }
+    ::android::base::unique_fd file_fd(
+            TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags, S_IRUSR | S_IWUSR)));
+    if (file_fd < 0) {
+        PLOG(ERROR) << "Failed to create file at: " << file_path;
+        return nullptr;
+    }
+
+    std::string abs_path;
+    if (!::android::base::Realpath(file_path, &abs_path)) {
+        PLOG(ERROR) << "Invalid file path: " << file_path;
+        cleanup(file_path, create);
+        return nullptr;
+    }
+
+    std::string bdev_path;
+    if (!GetBlockDeviceForFile(abs_path, &bdev_path)) {
+        LOG(ERROR) << "Failed to get block dev path for file: " << file_path;
+        cleanup(abs_path, create);
+        return nullptr;
+    }
+
+    ::android::base::unique_fd bdev_fd(
+            TEMP_FAILURE_RETRY(open(bdev_path.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (bdev_fd < 0) {
+        PLOG(ERROR) << "Failed to open block device: " << bdev_path;
+        cleanup(file_path, create);
+        return nullptr;
+    }
+
+    uint64_t bdevsz;
+    if (!GetBlockDeviceSize(bdev_fd, bdev_path, &bdevsz)) {
+        LOG(ERROR) << "Failed to get block device size for : " << bdev_path;
+        cleanup(file_path, create);
+        return nullptr;
+    }
+
+    if (!create) {
+        file_size = GetFileSize(abs_path);
+        if (file_size == 0) {
+            LOG(ERROR) << "Invalid file size of zero bytes for file: " << abs_path;
+            return nullptr;
+        }
+    }
+
+    uint64_t blocksz;
+    uint32_t fs_type;
+    if (!PerformFileChecks(abs_path, file_size, &blocksz, &fs_type)) {
+        LOG(ERROR) << "Failed to validate file or file system for file:" << abs_path;
+        cleanup(abs_path, create);
+        return nullptr;
+    }
+
+    // Align up to the nearest block size.
+    if (file_size % blocksz) {
+        file_size += blocksz - (file_size % blocksz);
+    }
+
+    if (create) {
+        if (!AllocateFile(file_fd, abs_path, blocksz, file_size, fs_type, std::move(progress))) {
+            LOG(ERROR) << "Failed to allocate file: " << abs_path << " of size: " << file_size
+                       << " bytes";
+            cleanup(abs_path, create);
+            return nullptr;
+        }
+    }
+
+    // f2fs may move the file blocks around.
+    if (!PinFile(file_fd, abs_path, fs_type)) {
+        cleanup(abs_path, create);
+        LOG(ERROR) << "Failed to pin the file in storage";
+        return nullptr;
+    }
+
+    // now allocate the FiemapWriter and start setting it up
+    FiemapUniquePtr fmap(new FiemapWriter());
+    switch (fs_type) {
+        case EXT4_SUPER_MAGIC:
+        case F2FS_SUPER_MAGIC:
+            if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
+                LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
+                cleanup(abs_path, create);
+                return nullptr;
+            }
+            break;
+        case MSDOS_SUPER_MAGIC:
+            if (!ReadFibmap(file_fd, abs_path, &fmap->extents_)) {
+                LOG(ERROR) << "Failed to read fibmap of file: " << abs_path;
+                cleanup(abs_path, create);
+                return nullptr;
+            }
+            break;
+    }
+
+    fmap->file_path_ = abs_path;
+    fmap->bdev_path_ = bdev_path;
+    fmap->file_size_ = file_size;
+    fmap->bdev_size_ = bdevsz;
+    fmap->fs_type_ = fs_type;
+    fmap->block_size_ = blocksz;
+
+    LOG(VERBOSE) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
+                 << bdev_path;
+    return fmap;
+}
+
+}  // namespace fiemap_writer
+}  // namespace android
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
new file mode 100644
index 0000000..dda7dfd
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
@@ -0,0 +1,541 @@
+/*
+ * 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 <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <libdm/loop_control.h>
+#include <libfiemap_writer/fiemap_writer.h>
+#include <libfiemap_writer/split_fiemap_writer.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fiemap_writer {
+
+using namespace std;
+using namespace std::string_literals;
+using namespace android::fiemap_writer;
+using unique_fd = android::base::unique_fd;
+using LoopDevice = android::dm::LoopDevice;
+
+std::string gTestDir;
+uint64_t testfile_size = 536870912;  // default of 512MiB
+size_t gBlockSize = 0;
+
+class FiemapWriterTest : public ::testing::Test {
+  protected:
+    void SetUp() override {
+        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
+        testfile = gTestDir + "/"s + tinfo->name();
+    }
+
+    void TearDown() override { unlink(testfile.c_str()); }
+
+    // name of the file we use for testing
+    std::string testfile;
+};
+
+class SplitFiemapTest : public ::testing::Test {
+  protected:
+    void SetUp() override {
+        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
+        testfile = gTestDir + "/"s + tinfo->name();
+    }
+
+    void TearDown() override {
+        std::string message;
+        if (!SplitFiemap::RemoveSplitFiles(testfile, &message)) {
+            cerr << "Could not remove all split files: " << message;
+        }
+    }
+
+    // name of the file we use for testing
+    std::string testfile;
+};
+
+TEST_F(FiemapWriterTest, CreateImpossiblyLargeFile) {
+    // Try creating a file of size ~100TB but aligned to
+    // 512 byte to make sure block alignment tests don't
+    // fail.
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1099511627997184);
+    EXPECT_EQ(fptr, nullptr);
+    EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
+    EXPECT_EQ(errno, ENOENT);
+}
+
+TEST_F(FiemapWriterTest, CreateUnalignedFile) {
+    // Try creating a file of size 4097 bytes which is guaranteed
+    // to be unaligned to all known block sizes.
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize + 1);
+    ASSERT_NE(fptr, nullptr);
+    ASSERT_EQ(fptr->size(), gBlockSize * 2);
+}
+
+TEST_F(FiemapWriterTest, CheckFilePath) {
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
+    ASSERT_NE(fptr, nullptr);
+    EXPECT_EQ(fptr->size(), gBlockSize);
+    EXPECT_EQ(fptr->file_path(), testfile);
+    EXPECT_EQ(access(testfile.c_str(), F_OK), 0);
+}
+
+TEST_F(FiemapWriterTest, CheckFileSize) {
+    // Create a large-ish file and test that the expected size matches.
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1024 * 1024 * 16);
+    ASSERT_NE(fptr, nullptr);
+
+    struct stat s;
+    ASSERT_EQ(stat(testfile.c_str(), &s), 0);
+    EXPECT_EQ(static_cast<uint64_t>(s.st_size), fptr->size());
+}
+
+TEST_F(FiemapWriterTest, CheckProgress) {
+    std::vector<uint64_t> expected;
+    size_t invocations = 0;
+    auto callback = [&](uint64_t done, uint64_t total) -> bool {
+        if (invocations >= expected.size()) {
+            return false;
+        }
+        EXPECT_EQ(done, expected[invocations]);
+        EXPECT_EQ(total, gBlockSize);
+        invocations++;
+        return true;
+    };
+
+    expected.push_back(gBlockSize);
+
+    auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));
+    EXPECT_NE(ptr, nullptr);
+    EXPECT_EQ(invocations, expected.size());
+}
+
+TEST_F(FiemapWriterTest, CheckPinning) {
+    auto ptr = FiemapWriter::Open(testfile, 4096);
+    ASSERT_NE(ptr, nullptr);
+    EXPECT_TRUE(FiemapWriter::HasPinnedExtents(testfile));
+}
+
+TEST_F(FiemapWriterTest, CheckBlockDevicePath) {
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
+    EXPECT_EQ(fptr->size(), gBlockSize);
+    EXPECT_EQ(fptr->bdev_path().find("/dev/block/"), size_t(0));
+    EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
+}
+
+TEST_F(FiemapWriterTest, CheckFileCreated) {
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 32768);
+    ASSERT_NE(fptr, nullptr);
+    unique_fd fd(open(testfile.c_str(), O_RDONLY));
+    EXPECT_GT(fd, -1);
+}
+
+TEST_F(FiemapWriterTest, CheckFileSizeActual) {
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
+    ASSERT_NE(fptr, nullptr);
+
+    struct stat sb;
+    ASSERT_EQ(stat(testfile.c_str(), &sb), 0);
+    EXPECT_GE(sb.st_size, testfile_size);
+}
+
+TEST_F(FiemapWriterTest, CheckFileExtents) {
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
+    ASSERT_NE(fptr, nullptr);
+    EXPECT_GT(fptr->extents().size(), 0);
+}
+
+TEST_F(FiemapWriterTest, ExistingFile) {
+    // Create the file.
+    { ASSERT_NE(FiemapWriter::Open(testfile, gBlockSize), nullptr); }
+    // Test that we can still open it.
+    {
+        auto ptr = FiemapWriter::Open(testfile, 0, false);
+        ASSERT_NE(ptr, nullptr);
+        EXPECT_GT(ptr->extents().size(), 0);
+    }
+}
+
+TEST_F(FiemapWriterTest, FileDeletedOnError) {
+    auto callback = [](uint64_t, uint64_t) -> bool { return false; };
+    auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));
+    EXPECT_EQ(ptr, nullptr);
+    EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
+    EXPECT_EQ(errno, ENOENT);
+}
+
+TEST_F(FiemapWriterTest, MaxBlockSize) {
+    ASSERT_GT(DetermineMaximumFileSize(testfile), 0);
+}
+
+TEST_F(FiemapWriterTest, FibmapBlockAddressing) {
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
+    ASSERT_NE(fptr, nullptr);
+
+    switch (fptr->fs_type()) {
+        case F2FS_SUPER_MAGIC:
+        case EXT4_SUPER_MAGIC:
+            // Skip the test for FIEMAP supported filesystems. This is really
+            // because f2fs/ext4 have caches that seem to defeat reading back
+            // directly from the block device, and writing directly is too
+            // dangerous.
+            std::cout << "Skipping test, filesystem does not use FIBMAP\n";
+            return;
+    }
+
+    bool uses_dm;
+    std::string bdev_path;
+    ASSERT_TRUE(FiemapWriter::GetBlockDeviceForFile(testfile, &bdev_path, &uses_dm));
+
+    if (uses_dm) {
+        // We could use a device-mapper wrapper here to bypass encryption, but
+        // really this test is for FIBMAP correctness on VFAT (where encryption
+        // is never used), so we don't bother.
+        std::cout << "Skipping test, block device is metadata encrypted\n";
+        return;
+    }
+
+    std::string data(fptr->size(), '\0');
+    for (size_t i = 0; i < data.size(); i++) {
+        data[i] = 'A' + static_cast<char>(data.size() % 26);
+    }
+
+    {
+        unique_fd fd(open(testfile.c_str(), O_WRONLY | O_CLOEXEC));
+        ASSERT_GE(fd, 0);
+        ASSERT_TRUE(android::base::WriteFully(fd, data.data(), data.size()));
+        ASSERT_EQ(fsync(fd), 0);
+    }
+
+    ASSERT_FALSE(fptr->extents().empty());
+    const auto& first_extent = fptr->extents()[0];
+
+    unique_fd bdev(open(fptr->bdev_path().c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_GE(bdev, 0);
+
+    off_t where = first_extent.fe_physical;
+    ASSERT_EQ(lseek(bdev, where, SEEK_SET), where);
+
+    // Note: this will fail on encrypted folders.
+    std::string actual(data.size(), '\0');
+    ASSERT_GE(first_extent.fe_length, data.size());
+    ASSERT_TRUE(android::base::ReadFully(bdev, actual.data(), actual.size()));
+    EXPECT_EQ(memcmp(actual.data(), data.data(), data.size()), 0);
+}
+
+TEST_F(SplitFiemapTest, Create) {
+    auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
+    ASSERT_NE(ptr, nullptr);
+
+    auto extents = ptr->extents();
+
+    // Destroy the fiemap, closing file handles. This should not delete them.
+    ptr = nullptr;
+
+    std::vector<std::string> files;
+    ASSERT_TRUE(SplitFiemap::GetSplitFileList(testfile, &files));
+    for (const auto& path : files) {
+        EXPECT_EQ(access(path.c_str(), F_OK), 0);
+    }
+
+    ASSERT_GE(extents.size(), files.size());
+}
+
+TEST_F(SplitFiemapTest, Open) {
+    {
+        auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
+        ASSERT_NE(ptr, nullptr);
+    }
+
+    auto ptr = SplitFiemap::Open(testfile);
+    ASSERT_NE(ptr, nullptr);
+
+    auto extents = ptr->extents();
+    ASSERT_GE(extents.size(), 24);
+}
+
+TEST_F(SplitFiemapTest, DeleteOnFail) {
+    auto ptr = SplitFiemap::Create(testfile, 1024 * 1024 * 100, 1);
+    ASSERT_EQ(ptr, nullptr);
+
+    std::string first_file = testfile + ".0001";
+    ASSERT_NE(access(first_file.c_str(), F_OK), 0);
+    ASSERT_EQ(errno, ENOENT);
+    ASSERT_NE(access(testfile.c_str(), F_OK), 0);
+    ASSERT_EQ(errno, ENOENT);
+}
+
+static string ReadSplitFiles(const std::string& base_path, size_t num_files) {
+    std::string result;
+    for (int i = 0; i < num_files; i++) {
+        std::string path = base_path + android::base::StringPrintf(".%04d", i);
+        std::string data;
+        if (!android::base::ReadFileToString(path, &data)) {
+            return {};
+        }
+        result += data;
+    }
+    return result;
+}
+
+TEST_F(SplitFiemapTest, WriteWholeFile) {
+    static constexpr size_t kChunkSize = 32768;
+    static constexpr size_t kSize = kChunkSize * 3;
+    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
+    ASSERT_NE(ptr, nullptr);
+
+    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
+    for (size_t i = 0; i < kSize / sizeof(int); i++) {
+        buffer[i] = i;
+    }
+    ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
+
+    std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
+    auto actual = ReadSplitFiles(testfile, 3);
+    ASSERT_EQ(expected.size(), actual.size());
+    EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
+}
+
+TEST_F(SplitFiemapTest, WriteFileInChunks1) {
+    static constexpr size_t kChunkSize = 32768;
+    static constexpr size_t kSize = kChunkSize * 3;
+    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
+    ASSERT_NE(ptr, nullptr);
+
+    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
+    for (size_t i = 0; i < kSize / sizeof(int); i++) {
+        buffer[i] = i;
+    }
+
+    // Write in chunks of 1000 (so some writes straddle the boundary of two
+    // files).
+    size_t bytes_written = 0;
+    while (bytes_written < kSize) {
+        size_t to_write = std::min(kSize - bytes_written, (size_t)1000);
+        char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
+        ASSERT_TRUE(ptr->Write(data, to_write));
+        bytes_written += to_write;
+    }
+
+    std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
+    auto actual = ReadSplitFiles(testfile, 3);
+    ASSERT_EQ(expected.size(), actual.size());
+    EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
+}
+
+TEST_F(SplitFiemapTest, WriteFileInChunks2) {
+    static constexpr size_t kChunkSize = 32768;
+    static constexpr size_t kSize = kChunkSize * 3;
+    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
+    ASSERT_NE(ptr, nullptr);
+
+    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
+    for (size_t i = 0; i < kSize / sizeof(int); i++) {
+        buffer[i] = i;
+    }
+
+    // Write in chunks of 32KiB so every write is exactly at the end of the
+    // current file.
+    size_t bytes_written = 0;
+    while (bytes_written < kSize) {
+        size_t to_write = std::min(kSize - bytes_written, kChunkSize);
+        char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
+        ASSERT_TRUE(ptr->Write(data, to_write));
+        bytes_written += to_write;
+    }
+
+    std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
+    auto actual = ReadSplitFiles(testfile, 3);
+    ASSERT_EQ(expected.size(), actual.size());
+    EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
+}
+
+TEST_F(SplitFiemapTest, WritePastEnd) {
+    static constexpr size_t kChunkSize = 32768;
+    static constexpr size_t kSize = kChunkSize * 3;
+    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
+    ASSERT_NE(ptr, nullptr);
+
+    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
+    for (size_t i = 0; i < kSize / sizeof(int); i++) {
+        buffer[i] = i;
+    }
+    ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
+    ASSERT_FALSE(ptr->Write(buffer.get(), kSize));
+}
+
+class VerifyBlockWritesExt4 : public ::testing::Test {
+    // 2GB Filesystem and 4k block size by default
+    static constexpr uint64_t block_size = 4096;
+    static constexpr uint64_t fs_size = 2147483648;
+
+  protected:
+    void SetUp() override {
+        fs_path = std::string(getenv("TMPDIR")) + "/ext4_2G.img";
+        uint64_t count = fs_size / block_size;
+        std::string dd_cmd =
+                ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
+                                              " count=%" PRIu64 " > /dev/null 2>&1",
+                                              fs_path.c_str(), block_size, count);
+        std::string mkfs_cmd =
+                ::android::base::StringPrintf("/system/bin/mkfs.ext4 -q %s", fs_path.c_str());
+        // create mount point
+        mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
+        ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
+        // create file for the file system
+        int ret = system(dd_cmd.c_str());
+        ASSERT_EQ(ret, 0);
+        // Get and attach a loop device to the filesystem we created
+        LoopDevice loop_dev(fs_path);
+        ASSERT_TRUE(loop_dev.valid());
+        // create file system
+        ret = system(mkfs_cmd.c_str());
+        ASSERT_EQ(ret, 0);
+
+        // mount the file system
+        ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "ext4", 0, nullptr), 0);
+    }
+
+    void TearDown() override {
+        umount(mntpoint.c_str());
+        rmdir(mntpoint.c_str());
+        unlink(fs_path.c_str());
+    }
+
+    std::string mntpoint;
+    std::string fs_path;
+};
+
+class VerifyBlockWritesF2fs : public ::testing::Test {
+    // 2GB Filesystem and 4k block size by default
+    static constexpr uint64_t block_size = 4096;
+    static constexpr uint64_t fs_size = 2147483648;
+
+  protected:
+    void SetUp() override {
+        fs_path = std::string(getenv("TMPDIR")) + "/f2fs_2G.img";
+        uint64_t count = fs_size / block_size;
+        std::string dd_cmd =
+                ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
+                                              " count=%" PRIu64 " > /dev/null 2>&1",
+                                              fs_path.c_str(), block_size, count);
+        std::string mkfs_cmd =
+                ::android::base::StringPrintf("/system/bin/make_f2fs -q %s", fs_path.c_str());
+        // create mount point
+        mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
+        ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
+        // create file for the file system
+        int ret = system(dd_cmd.c_str());
+        ASSERT_EQ(ret, 0);
+        // Get and attach a loop device to the filesystem we created
+        LoopDevice loop_dev(fs_path);
+        ASSERT_TRUE(loop_dev.valid());
+        // create file system
+        ret = system(mkfs_cmd.c_str());
+        ASSERT_EQ(ret, 0);
+
+        // mount the file system
+        ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "f2fs", 0, nullptr), 0);
+    }
+
+    void TearDown() override {
+        umount(mntpoint.c_str());
+        rmdir(mntpoint.c_str());
+        unlink(fs_path.c_str());
+    }
+
+    std::string mntpoint;
+    std::string fs_path;
+};
+
+bool DetermineBlockSize() {
+    struct statfs s;
+    if (statfs(gTestDir.c_str(), &s)) {
+        std::cerr << "Could not call statfs: " << strerror(errno) << "\n";
+        return false;
+    }
+    if (!s.f_bsize) {
+        std::cerr << "Invalid block size: " << s.f_bsize << "\n";
+        return false;
+    }
+
+    gBlockSize = s.f_bsize;
+    return true;
+}
+
+}  // namespace fiemap_writer
+}  // namespace android
+
+using namespace android::fiemap_writer;
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    if (argc > 1 && argv[1] == "-h"s) {
+        cerr << "Usage: [test_dir] [file_size]\n";
+        cerr << "\n";
+        cerr << "Note: test_dir must be a writable, unencrypted directory.\n";
+        exit(EXIT_FAILURE);
+    }
+    ::android::base::InitLogging(argv, ::android::base::StderrLogger);
+
+    std::string root_dir = "/data/local/unencrypted";
+    if (access(root_dir.c_str(), F_OK)) {
+        root_dir = "/data";
+    }
+
+    std::string tempdir = root_dir + "/XXXXXX"s;
+    if (!mkdtemp(tempdir.data())) {
+        cerr << "unable to create tempdir on " << root_dir << "\n";
+        exit(EXIT_FAILURE);
+    }
+    if (!android::base::Realpath(tempdir, &gTestDir)) {
+        cerr << "unable to find realpath for " << tempdir;
+        exit(EXIT_FAILURE);
+    }
+
+    if (argc > 2) {
+        testfile_size = strtoull(argv[2], NULL, 0);
+        if (testfile_size == ULLONG_MAX) {
+            testfile_size = 512 * 1024 * 1024;
+        }
+    }
+
+    if (!DetermineBlockSize()) {
+        exit(EXIT_FAILURE);
+    }
+
+    auto result = RUN_ALL_TESTS();
+
+    std::string cmd = "rm -rf " + gTestDir;
+    system(cmd.c_str());
+
+    return result;
+}
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
new file mode 100644
index 0000000..ee79262
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
@@ -0,0 +1,118 @@
+/*
+ * 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 <linux/fiemap.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fiemap_writer {
+
+class FiemapWriter;
+using FiemapUniquePtr = std::unique_ptr<FiemapWriter>;
+
+class FiemapWriter final {
+  public:
+    // Factory method for FiemapWriter.
+    // The method returns FiemapUniquePtr that contains all the data necessary to be able to write
+    // to the given file directly using raw block i/o. The optional progress callback will be
+    // invoked, if create is true, while the file is being initialized. It receives the bytes
+    // written and the number of total bytes. If the callback returns false, the operation will
+    // fail.
+    //
+    // Note: when create is true, the file size will be aligned up to the nearest file system
+    // block.
+    static FiemapUniquePtr Open(const std::string& file_path, uint64_t file_size,
+                                bool create = true,
+                                std::function<bool(uint64_t, uint64_t)> progress = {});
+
+    // Check that a file still has the same extents since it was last opened with FiemapWriter,
+    // assuming the file was not resized outside of FiemapWriter. Returns false either on error
+    // or if the file was not pinned.
+    //
+    // This will always return true on Ext4. On F2FS, it will return true if either of the
+    // following cases are true:
+    //   - The file was never pinned.
+    //   - The file is pinned and has not been moved by the GC.
+    // Thus, this method should only be called for pinned files (such as those returned by
+    // FiemapWriter::Open).
+    static bool HasPinnedExtents(const std::string& file_path);
+
+    // Returns the underlying block device of a file. This will look past device-mapper layers
+    // as long as each layer would not change block mappings (i.e., dm-crypt, dm-bow, and dm-
+    // default-key tables are okay; dm-linear is not). If a mapping such as dm-linear is found,
+    // it will be returned in place of any physical block device.
+    //
+    // It is the caller's responsibility to check whether the returned block device is acceptable.
+    // Gsid, for example, will only accept /dev/block/by-name/userdata as the bottom device.
+    // Callers can check the device name (dm- or loop prefix), inspect sysfs, or compare the major
+    // number against a boot device.
+    //
+    // If device-mapper nodes were encountered, then |uses_dm| will be set to true.
+    static bool GetBlockDeviceForFile(const std::string& file_path, std::string* bdev_path,
+                                      bool* uses_dm = nullptr);
+
+    ~FiemapWriter() = default;
+
+    const std::string& file_path() const { return file_path_; };
+    uint64_t size() const { return file_size_; };
+    const std::string& bdev_path() const { return bdev_path_; };
+    uint64_t block_size() const { return block_size_; };
+    const std::vector<struct fiemap_extent>& extents() { return extents_; };
+    uint32_t fs_type() const { return fs_type_; }
+
+    // Non-copyable & Non-movable
+    FiemapWriter(const FiemapWriter&) = delete;
+    FiemapWriter& operator=(const FiemapWriter&) = delete;
+    FiemapWriter& operator=(FiemapWriter&&) = delete;
+    FiemapWriter(FiemapWriter&&) = delete;
+
+  private:
+    // Name of the file managed by this class.
+    std::string file_path_;
+    // Block device on which we have created the file.
+    std::string bdev_path_;
+
+    // Size in bytes of the file this class is writing
+    uint64_t file_size_;
+
+    // total size in bytes of the block device
+    uint64_t bdev_size_;
+
+    // Filesystem type where the file is being created.
+    // See: <uapi/linux/magic.h> for filesystem magic numbers
+    uint32_t fs_type_;
+
+    // block size as reported by the kernel of the underlying block device;
+    uint64_t block_size_;
+
+    // This file's fiemap
+    std::vector<struct fiemap_extent> extents_;
+
+    FiemapWriter() = default;
+};
+
+}  // namespace fiemap_writer
+}  // namespace android
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h
new file mode 100644
index 0000000..7b977e1
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "fiemap_writer.h"
+
+namespace android {
+namespace fiemap_writer {
+
+// Wrapper around FiemapWriter that is able to split images across files if
+// necessary.
+class SplitFiemap final {
+  public:
+    using ProgressCallback = std::function<bool(uint64_t, uint64_t)>;
+
+    // Create a new split fiemap file. If |max_piece_size| is 0, the number of
+    // pieces will be determined automatically by detecting the filesystem.
+    // Otherwise, the file will be split evenly (with the remainder in the
+    // final file).
+    static std::unique_ptr<SplitFiemap> Create(const std::string& file_path, uint64_t file_size,
+                                               uint64_t max_piece_size,
+                                               ProgressCallback progress = {});
+
+    // Open an existing split fiemap file.
+    static std::unique_ptr<SplitFiemap> Open(const std::string& file_path);
+
+    ~SplitFiemap();
+
+    // Return a list of all files created for a split file.
+    static bool GetSplitFileList(const std::string& file_path, std::vector<std::string>* list);
+
+    // Destroy all components of a split file. If the root file does not exist,
+    // this returns true and does not report an error.
+    static bool RemoveSplitFiles(const std::string& file_path, std::string* message = nullptr);
+
+    // Return whether all components of a split file still have pinned extents.
+    bool HasPinnedExtents() const;
+
+    // Helper method for writing data that spans files. Note there is no seek
+    // method (yet); this starts at 0 and increments the position by |bytes|.
+    bool Write(const void* data, uint64_t bytes);
+
+    // Flush all writes to all split files.
+    bool Flush();
+
+    const std::vector<struct fiemap_extent>& extents();
+    uint32_t block_size() const;
+    uint64_t size() const { return total_size_; }
+    const std::string& bdev_path() const;
+
+    // Non-copyable & Non-movable
+    SplitFiemap(const SplitFiemap&) = delete;
+    SplitFiemap& operator=(const SplitFiemap&) = delete;
+    SplitFiemap& operator=(SplitFiemap&&) = delete;
+    SplitFiemap(SplitFiemap&&) = delete;
+
+  private:
+    SplitFiemap() = default;
+    void AddFile(FiemapUniquePtr&& file);
+
+    bool creating_ = false;
+    std::string list_file_;
+    std::vector<FiemapUniquePtr> files_;
+    std::vector<struct fiemap_extent> extents_;
+    uint64_t total_size_ = 0;
+
+    // Most recently open file and position for Write().
+    size_t cursor_index_ = 0;
+    uint64_t cursor_file_pos_ = 0;
+    android::base::unique_fd cursor_fd_;
+};
+
+}  // namespace fiemap_writer
+}  // namespace android
diff --git a/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp b/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp
new file mode 100644
index 0000000..16a82d2
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <libfiemap_writer/split_fiemap_writer.h>
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fiemap_writer {
+
+using android::base::unique_fd;
+
+// We use a four-digit suffix at the end of filenames.
+static const size_t kMaxFilePieces = 500;
+
+std::unique_ptr<SplitFiemap> SplitFiemap::Create(const std::string& file_path, uint64_t file_size,
+                                                 uint64_t max_piece_size,
+                                                 ProgressCallback progress) {
+    if (!file_size) {
+        LOG(ERROR) << "Cannot create a fiemap for a 0-length file: " << file_path;
+        return nullptr;
+    }
+
+    if (!max_piece_size) {
+        max_piece_size = DetermineMaximumFileSize(file_path);
+        if (!max_piece_size) {
+            LOG(ERROR) << "Could not determine maximum file size for " << file_path;
+            return nullptr;
+        }
+    }
+
+    // Call |progress| only when the total percentage would significantly change.
+    int permille = -1;
+    uint64_t total_bytes_written = 0;
+    auto on_progress = [&](uint64_t written, uint64_t) -> bool {
+        uint64_t actual_written = total_bytes_written + written;
+        int new_permille = (actual_written * 1000) / file_size;
+        if (new_permille != permille && actual_written < file_size) {
+            if (progress && !progress(actual_written, file_size)) {
+                return false;
+            }
+            permille = new_permille;
+        }
+        return true;
+    };
+
+    std::unique_ptr<SplitFiemap> out(new SplitFiemap());
+    out->creating_ = true;
+    out->list_file_ = file_path;
+
+    // Create the split files.
+    uint64_t remaining_bytes = file_size;
+    while (remaining_bytes) {
+        if (out->files_.size() >= kMaxFilePieces) {
+            LOG(ERROR) << "Requested size " << file_size << " created too many split files";
+            return nullptr;
+        }
+        std::string chunk_path =
+                android::base::StringPrintf("%s.%04d", file_path.c_str(), (int)out->files_.size());
+        uint64_t chunk_size = std::min(max_piece_size, remaining_bytes);
+        auto writer = FiemapWriter::Open(chunk_path, chunk_size, true, on_progress);
+        if (!writer) {
+            return nullptr;
+        }
+
+        // To make sure the alignment doesn't create too much inconsistency, we
+        // account the *actual* size, not the requested size.
+        total_bytes_written += writer->size();
+        // writer->size() is block size aligned and could be bigger than remaining_bytes
+        // If remaining_bytes is bigger, set remaining_bytes to 0 to avoid underflow error.
+        remaining_bytes = remaining_bytes > writer->size() ? (remaining_bytes - writer->size()) : 0;
+        out->AddFile(std::move(writer));
+    }
+
+    // Create the split file list.
+    unique_fd fd(open(out->list_file_.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0660));
+    if (fd < 0) {
+        PLOG(ERROR) << "Failed to open " << file_path;
+        return nullptr;
+    }
+
+    for (const auto& writer : out->files_) {
+        std::string line = android::base::Basename(writer->file_path()) + "\n";
+        if (!android::base::WriteFully(fd, line.data(), line.size())) {
+            PLOG(ERROR) << "Write failed " << file_path;
+            return nullptr;
+        }
+    }
+
+    // Unset this bit, so we don't unlink on destruction.
+    out->creating_ = false;
+    return out;
+}
+
+std::unique_ptr<SplitFiemap> SplitFiemap::Open(const std::string& file_path) {
+    std::vector<std::string> files;
+    if (!GetSplitFileList(file_path, &files)) {
+        return nullptr;
+    }
+
+    std::unique_ptr<SplitFiemap> out(new SplitFiemap());
+    out->list_file_ = file_path;
+
+    for (const auto& file : files) {
+        auto writer = FiemapWriter::Open(file, 0, false);
+        if (!writer) {
+            // Error was logged in Open().
+            return nullptr;
+        }
+        out->AddFile(std::move(writer));
+    }
+    return out;
+}
+
+bool SplitFiemap::GetSplitFileList(const std::string& file_path, std::vector<std::string>* list) {
+    // This is not the most efficient thing, but it is simple and recovering
+    // the fiemap/fibmap is much more expensive.
+    std::string contents;
+    if (!android::base::ReadFileToString(file_path, &contents, true)) {
+        PLOG(ERROR) << "Error reading file: " << file_path;
+        return false;
+    }
+
+    std::vector<std::string> names = android::base::Split(contents, "\n");
+    std::string dir = android::base::Dirname(file_path);
+    for (const auto& name : names) {
+        if (!name.empty()) {
+            list->emplace_back(dir + "/" + name);
+        }
+    }
+    return true;
+}
+
+bool SplitFiemap::RemoveSplitFiles(const std::string& file_path, std::string* message) {
+    // Early exit if this does not exist, and do not report an error.
+    if (access(file_path.c_str(), F_OK) && errno == ENOENT) {
+        return true;
+    }
+
+    bool ok = true;
+    std::vector<std::string> files;
+    if (GetSplitFileList(file_path, &files)) {
+        for (const auto& file : files) {
+            ok &= android::base::RemoveFileIfExists(file, message);
+        }
+    }
+    ok &= android::base::RemoveFileIfExists(file_path, message);
+    return ok;
+}
+
+bool SplitFiemap::HasPinnedExtents() const {
+    for (const auto& file : files_) {
+        if (!FiemapWriter::HasPinnedExtents(file->file_path())) {
+            return false;
+        }
+    }
+    return true;
+}
+
+const std::vector<struct fiemap_extent>& SplitFiemap::extents() {
+    if (extents_.empty()) {
+        for (const auto& file : files_) {
+            const auto& extents = file->extents();
+            extents_.insert(extents_.end(), extents.begin(), extents.end());
+        }
+    }
+    return extents_;
+}
+
+bool SplitFiemap::Write(const void* data, uint64_t bytes) {
+    // Open the current file.
+    FiemapWriter* file = files_[cursor_index_].get();
+
+    const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(data);
+    uint64_t bytes_remaining = bytes;
+    while (bytes_remaining) {
+        // How many bytes can we write into the current file?
+        uint64_t file_bytes_left = file->size() - cursor_file_pos_;
+        if (!file_bytes_left) {
+            if (cursor_index_ == files_.size() - 1) {
+                LOG(ERROR) << "write past end of file requested";
+                return false;
+            }
+
+            // No space left in the current file, but we have more files to
+            // use, so prep the next one.
+            cursor_fd_ = {};
+            cursor_file_pos_ = 0;
+            file = files_[++cursor_index_].get();
+            file_bytes_left = file->size();
+        }
+
+        // Open the current file if it's not open.
+        if (cursor_fd_ < 0) {
+            cursor_fd_.reset(open(file->file_path().c_str(), O_CLOEXEC | O_WRONLY));
+            if (cursor_fd_ < 0) {
+                PLOG(ERROR) << "open failed: " << file->file_path();
+                return false;
+            }
+            CHECK(cursor_file_pos_ == 0);
+        }
+
+        if (!FiemapWriter::HasPinnedExtents(file->file_path())) {
+            LOG(ERROR) << "file is no longer pinned: " << file->file_path();
+            return false;
+        }
+
+        uint64_t bytes_to_write = std::min(file_bytes_left, bytes_remaining);
+        if (!android::base::WriteFully(cursor_fd_, data_ptr, bytes_to_write)) {
+            PLOG(ERROR) << "write failed: " << file->file_path();
+            return false;
+        }
+        data_ptr += bytes_to_write;
+        bytes_remaining -= bytes_to_write;
+        cursor_file_pos_ += bytes_to_write;
+    }
+
+    // If we've reached the end of the current file, close it for sanity.
+    if (cursor_file_pos_ == file->size()) {
+        cursor_fd_ = {};
+    }
+    return true;
+}
+
+bool SplitFiemap::Flush() {
+    for (const auto& file : files_) {
+        unique_fd fd(open(file->file_path().c_str(), O_RDONLY | O_CLOEXEC));
+        if (fd < 0) {
+            PLOG(ERROR) << "open failed: " << file->file_path();
+            return false;
+        }
+        if (fsync(fd)) {
+            PLOG(ERROR) << "fsync failed: " << file->file_path();
+            return false;
+        }
+    }
+    return true;
+}
+
+SplitFiemap::~SplitFiemap() {
+    if (!creating_) {
+        return;
+    }
+
+    // We failed to finish creating, so unlink everything.
+    unlink(list_file_.c_str());
+    for (auto&& file : files_) {
+        std::string path = file->file_path();
+        file = nullptr;
+
+        unlink(path.c_str());
+    }
+}
+
+void SplitFiemap::AddFile(FiemapUniquePtr&& file) {
+    total_size_ += file->size();
+    files_.emplace_back(std::move(file));
+}
+
+uint32_t SplitFiemap::block_size() const {
+    return files_[0]->block_size();
+}
+
+const std::string& SplitFiemap::bdev_path() const {
+    return files_[0]->bdev_path();
+}
+
+}  // namespace fiemap_writer
+}  // namespace android
diff --git a/fs_mgr/libfiemap_writer/testdata/file_32k b/fs_mgr/libfiemap_writer/testdata/file_32k
new file mode 100644
index 0000000..12f3be4
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/testdata/file_32k
Binary files differ
diff --git a/fs_mgr/libfiemap_writer/testdata/file_4k b/fs_mgr/libfiemap_writer/testdata/file_4k
new file mode 100644
index 0000000..08e7df1
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/testdata/file_4k
Binary files differ
diff --git a/fs_mgr/libfiemap_writer/testdata/unaligned_file b/fs_mgr/libfiemap_writer/testdata/unaligned_file
new file mode 100644
index 0000000..c107c26
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/testdata/unaligned_file
Binary files differ
diff --git a/fs_mgr/libfiemap_writer/utility.cpp b/fs_mgr/libfiemap_writer/utility.cpp
new file mode 100644
index 0000000..192ec16
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/utility.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "utility.h"
+
+#include <stdint.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <libfiemap_writer/fiemap_writer.h>
+
+namespace android {
+namespace fiemap_writer {
+
+uint64_t DetermineMaximumFileSize(const std::string& file_path) {
+    // Create the smallest file possible (one block).
+    auto writer = FiemapWriter::Open(file_path, 1);
+    if (!writer) {
+        return 0;
+    }
+
+    uint64_t result = 0;
+    switch (writer->fs_type()) {
+        case EXT4_SUPER_MAGIC:
+            // The minimum is 16GiB, so just report that. If we wanted we could parse the
+            // superblock and figure out if 64-bit support is enabled.
+            result = 17179869184ULL;
+            break;
+        case F2FS_SUPER_MAGIC:
+            // Formula is from https://www.kernel.org/doc/Documentation/filesystems/f2fs.txt
+            // 4KB * (923 + 2 * 1018 + 2 * 1018 * 1018 + 1018 * 1018 * 1018) := 3.94TB.
+            result = 4329690886144ULL;
+            break;
+        case MSDOS_SUPER_MAGIC:
+            // 4GB-1, which we want aligned to the block size.
+            result = 4294967295;
+            result -= (result % writer->block_size());
+            break;
+        default:
+            LOG(ERROR) << "Unknown file system type: " << writer->fs_type();
+            break;
+    }
+
+    // Close and delete the temporary file.
+    writer = nullptr;
+    unlink(file_path.c_str());
+
+    return result;
+}
+
+}  // namespace fiemap_writer
+}  // namespace android
diff --git a/fs_mgr/libfiemap_writer/utility.h b/fs_mgr/libfiemap_writer/utility.h
new file mode 100644
index 0000000..2d418da
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/utility.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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>
+
+namespace android {
+namespace fiemap_writer {
+
+// Given a file that will be created, determine the maximum size its containing
+// filesystem allows. Note this is a theoretical maximum size; free space is
+// ignored entirely.
+uint64_t DetermineMaximumFileSize(const std::string& file_path);
+
+}  // namespace fiemap_writer
+}  // namespace android
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
new file mode 100644
index 0000000..a3c76ab
--- /dev/null
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -0,0 +1,143 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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: "libfs_avb",
+    defaults: ["fs_mgr_defaults"],
+    recovery_available: true,
+    host_supported: true,
+    export_include_dirs: ["include"],
+    srcs: [
+        "avb_ops.cpp",
+        "avb_util.cpp",
+        "fs_avb.cpp",
+        "fs_avb_util.cpp",
+        "types.cpp",
+        "util.cpp",
+    ],
+    static_libs: [
+        "libavb",
+        "libdm",
+        "libfstab",
+    ],
+    export_static_lib_headers: [
+        "libfstab",
+    ],
+    shared_libs: [
+        "libcrypto",
+    ],
+    header_libs: [
+        "libbase_headers",
+    ],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+}
+
+cc_defaults {
+    name: "libfs_avb_host_test_defaults",
+    required: [
+        "avbtool",
+    ],
+    data: [
+        "tests/data/*",
+    ],
+    static_libs: [
+        "libavb",
+        "libavb_host_sysdeps",
+        "libdm",
+        "libfs_avb",
+        "libfstab",
+        "libgtest_host",
+    ],
+    shared_libs: [
+        "libbase",
+        "libchrome",
+    ],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+    cflags: [
+        "-DHOST_TEST",
+    ],
+}
+
+cc_library_host_static {
+    name: "libfs_avb_test_util",
+    defaults: ["libfs_avb_host_test_defaults"],
+    srcs: [
+        "tests/fs_avb_test_util.cpp",
+    ],
+}
+
+cc_test_host {
+    name: "libfs_avb_test",
+    defaults: ["libfs_avb_host_test_defaults"],
+    test_suites: ["general-tests"],
+    static_libs: [
+        "libfs_avb_test_util",
+    ],
+    shared_libs: [
+        "libcrypto",
+    ],
+    srcs: [
+        "tests/basic_test.cpp",
+        "tests/fs_avb_test.cpp",
+        "tests/fs_avb_util_test.cpp",
+    ],
+}
+
+cc_test_host {
+    name: "libfs_avb_internal_test",
+    defaults: ["libfs_avb_host_test_defaults"],
+    test_suites: ["general-tests"],
+    static_libs: [
+        "libfs_avb_test_util",
+    ],
+    srcs: [
+        "avb_util.cpp",
+        "util.cpp",
+        "tests/avb_util_test.cpp",
+        "tests/util_test.cpp",
+    ],
+}
+
+cc_test {
+    name: "libfs_avb_device_test",
+    test_suites: ["device-tests"],
+    static_libs: [
+        "libavb",
+        "libdm",
+        "libfs_avb",
+        "libfstab",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcrypto",
+    ],
+    srcs: [
+        "tests/fs_avb_device_test.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+}
diff --git a/fs_mgr/libfs_avb/TEST_MAPPING b/fs_mgr/libfs_avb/TEST_MAPPING
new file mode 100644
index 0000000..b0f36d4
--- /dev/null
+++ b/fs_mgr/libfs_avb/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "postsubmit": [
+    {
+      "name": "libfs_avb_test",
+      "host": true
+    },
+    {
+      "name": "libfs_avb_internal_test",
+      "host": true
+    }
+  ]
+}
diff --git a/fs_mgr/libfs_avb/avb_ops.cpp b/fs_mgr/libfs_avb/avb_ops.cpp
new file mode 100644
index 0000000..c192bf5
--- /dev/null
+++ b/fs_mgr/libfs_avb/avb_ops.cpp
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2016 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 "avb_ops.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <string>
+
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <libavb/libavb.h>
+#include <libdm/dm.h>
+#include <utils/Compat.h>
+
+#include "util.h"
+
+using namespace std::literals;
+
+namespace android {
+namespace fs_mgr {
+
+static AvbIOResult read_from_partition(AvbOps* ops, const char* partition, int64_t offset,
+                                       size_t num_bytes, void* buffer, size_t* out_num_read) {
+    return FsManagerAvbOps::GetInstanceFromAvbOps(ops)->ReadFromPartition(
+            partition, offset, num_bytes, buffer, out_num_read);
+}
+
+static AvbIOResult dummy_read_rollback_index(AvbOps* ops ATTRIBUTE_UNUSED,
+                                             size_t rollback_index_location ATTRIBUTE_UNUSED,
+                                             uint64_t* out_rollback_index) {
+    // rollback_index has been checked in bootloader phase.
+    // In user-space, returns the smallest value 0 to pass the check.
+    *out_rollback_index = 0;
+    return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult dummy_validate_vbmeta_public_key(
+        AvbOps* ops ATTRIBUTE_UNUSED, const uint8_t* public_key_data ATTRIBUTE_UNUSED,
+        size_t public_key_length ATTRIBUTE_UNUSED,
+        const uint8_t* public_key_metadata ATTRIBUTE_UNUSED,
+        size_t public_key_metadata_length ATTRIBUTE_UNUSED, bool* out_is_trusted) {
+    // vbmeta public key has been checked in bootloader phase.
+    // In user-space, returns true to pass the check.
+    //
+    // Addtionally, user-space should check
+    // androidboot.vbmeta.{hash_alg, size, digest} against the digest
+    // of all vbmeta images after invoking avb_slot_verify().
+    *out_is_trusted = true;
+    return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult dummy_read_is_device_unlocked(AvbOps* ops ATTRIBUTE_UNUSED,
+                                                 bool* out_is_unlocked) {
+    // The function is for bootloader to update the value into
+    // androidboot.vbmeta.device_state in kernel cmdline.
+    // In user-space, returns true as we don't need to update it anymore.
+    *out_is_unlocked = true;
+    return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult dummy_get_unique_guid_for_partition(AvbOps* ops ATTRIBUTE_UNUSED,
+                                                       const char* partition ATTRIBUTE_UNUSED,
+                                                       char* guid_buf, size_t guid_buf_size) {
+    // The function is for bootloader to set the correct UUID
+    // for a given partition in kernel cmdline.
+    // In user-space, returns a faking one as we don't need to update
+    // it anymore.
+    snprintf(guid_buf, guid_buf_size, "1234-fake-guid-for:%s", partition);
+    return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult dummy_get_size_of_partition(AvbOps* ops ATTRIBUTE_UNUSED,
+                                               const char* partition ATTRIBUTE_UNUSED,
+                                               uint64_t* out_size_num_byte) {
+    // The function is for bootloader to load entire content of AVB HASH partitions.
+    // In user-space, returns 0 as we only need to set up AVB HASHTHREE partitions.
+    *out_size_num_byte = 0;
+    return AVB_IO_RESULT_OK;
+}
+
+// Converts a partition name (with ab_suffix) to the corresponding mount point.
+// e.g., "system_a" => "/system",
+// e.g., "vendor_a" => "/vendor",
+static std::string DeriveMountPoint(const std::string& partition_name) {
+    const std::string ab_suffix = fs_mgr_get_slot_suffix();
+    std::string mount_point(partition_name);
+    auto found = partition_name.rfind(ab_suffix);
+    if (found != std::string::npos) {
+        mount_point.erase(found);  // converts system_a => system
+    }
+
+    return "/" + mount_point;
+}
+
+FsManagerAvbOps::FsManagerAvbOps() {
+    // We only need to provide the implementation of read_from_partition()
+    // operation since that's all what is being used by the avb_slot_verify().
+    // Other I/O operations are only required in bootloader but not in
+    // user-space so we set them as dummy operations. Also zero the entire
+    // struct so operations added in the future will be set to NULL.
+    memset(&avb_ops_, 0, sizeof(AvbOps));
+    avb_ops_.read_from_partition = read_from_partition;
+    avb_ops_.read_rollback_index = dummy_read_rollback_index;
+    avb_ops_.validate_vbmeta_public_key = dummy_validate_vbmeta_public_key;
+    avb_ops_.read_is_device_unlocked = dummy_read_is_device_unlocked;
+    avb_ops_.get_unique_guid_for_partition = dummy_get_unique_guid_for_partition;
+    avb_ops_.get_size_of_partition = dummy_get_size_of_partition;
+
+    // Sets user_data for GetInstanceFromAvbOps() to convert it back to FsManagerAvbOps.
+    avb_ops_.user_data = this;
+}
+
+// Given a partition name (with ab_suffix), e.g., system_a, returns the corresponding
+// dm-linear path for it. e.g., /dev/block/dm-0. If not found, returns an empty string.
+// This assumes that the prefix of the partition name and the mount point are the same.
+// e.g., partition vendor_a is mounted under /vendor, product_a is mounted under /product, etc.
+// This might not be true for some special fstab files, e.g., fstab.postinstall.
+// But it's good enough for the default fstab. Also note that the logical path is a
+// fallback solution when the physical path (/dev/block/by-name/<partition>) cannot be found.
+std::string FsManagerAvbOps::GetLogicalPath(const std::string& partition_name) {
+    if (fstab_.empty() && !ReadDefaultFstab(&fstab_)) {
+        return "";
+    }
+
+    const auto mount_point = DeriveMountPoint(partition_name);
+    if (mount_point.empty()) return "";
+
+    auto fstab_entry = GetEntryForMountPoint(&fstab_, mount_point);
+    if (!fstab_entry) return "";
+
+    std::string device_path;
+    if (fstab_entry->fs_mgr_flags.logical) {
+        dm::DeviceMapper& dm = dm::DeviceMapper::Instance();
+        if (!dm.GetDmDevicePathByName(fstab_entry->blk_device, &device_path)) {
+            LERROR << "Failed to resolve logical device path for: " << fstab_entry->blk_device;
+            return "";
+        }
+        return device_path;
+    }
+
+    return "";
+}
+
+AvbIOResult FsManagerAvbOps::ReadFromPartition(const char* partition, int64_t offset,
+                                               size_t num_bytes, void* buffer,
+                                               size_t* out_num_read) {
+    std::string path = "/dev/block/by-name/"s + partition;
+
+    // Ensures the device path (a symlink created by init) is ready to access.
+    if (!WaitForFile(path, 1s)) {
+        LERROR << "Device path not found: " << path;
+        // Falls back to logical path if the physical path is not found.
+        // This mostly only works for emulator (no bootloader). Because in normal
+        // device, bootloader is unable to read logical partitions. So if libavb in
+        // the bootloader failed to read a physical partition, it will failed to boot
+        // the HLOS and we won't reach the code here.
+        path = GetLogicalPath(partition);
+        if (path.empty() || !WaitForFile(path, 1s)) return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+        LINFO << "Fallback to use logical device path: " << path;
+    }
+
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd < 0) {
+        PERROR << "Failed to open " << path;
+        return AVB_IO_RESULT_ERROR_IO;
+    }
+
+    // If offset is negative, interprets its absolute value as the
+    //  number of bytes from the end of the partition.
+    if (offset < 0) {
+        off64_t total_size = lseek64(fd, 0, SEEK_END);
+        if (total_size == -1) {
+            PERROR << "Failed to lseek64 to end of the partition";
+            return AVB_IO_RESULT_ERROR_IO;
+        }
+        offset = total_size + offset;
+        // Repositions the offset to the beginning.
+        if (lseek64(fd, 0, SEEK_SET) == -1) {
+            PERROR << "Failed to lseek64 to the beginning of the partition";
+            return AVB_IO_RESULT_ERROR_IO;
+        }
+    }
+
+    // On Linux, we never get partial reads from block devices (except
+    // for EOF).
+    ssize_t num_read = TEMP_FAILURE_RETRY(pread64(fd, buffer, num_bytes, offset));
+    if (num_read < 0 || (size_t)num_read != num_bytes) {
+        PERROR << "Failed to read " << num_bytes << " bytes from " << path << " offset " << offset;
+        return AVB_IO_RESULT_ERROR_IO;
+    }
+
+    if (out_num_read != nullptr) {
+        *out_num_read = num_read;
+    }
+
+    return AVB_IO_RESULT_OK;
+}
+
+AvbSlotVerifyResult FsManagerAvbOps::AvbSlotVerify(const std::string& ab_suffix,
+                                                   AvbSlotVerifyFlags flags,
+                                                   std::vector<VBMetaData>* out_vbmeta_images) {
+    // Invokes avb_slot_verify() to load and verify all vbmeta images.
+    // Sets requested_partitions to nullptr as it's to copy the contents
+    // of HASH partitions into handle>avb_slot_data_, which is not required as
+    // fs_mgr only deals with HASHTREE partitions.
+    const char* requested_partitions[] = {nullptr};
+
+    // Local resource to store vbmeta images from avb_slot_verify();
+    AvbSlotVerifyData* avb_slot_data;
+
+    // The |hashtree_error_mode| field doesn't matter as it only
+    // influences the generated kernel cmdline parameters.
+    auto verify_result =
+            avb_slot_verify(&avb_ops_, requested_partitions, ab_suffix.c_str(), flags,
+                            AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &avb_slot_data);
+
+    if (!avb_slot_data) return verify_result;
+    // Copies avb_slot_data->vbmeta_images[].
+    for (size_t i = 0; i < avb_slot_data->num_vbmeta_images; i++) {
+        out_vbmeta_images->emplace_back(VBMetaData(avb_slot_data->vbmeta_images[i].vbmeta_data,
+                                                   avb_slot_data->vbmeta_images[i].vbmeta_size,
+                                                   avb_slot_data->vbmeta_images[i].partition_name));
+    }
+
+    // Free the local resource.
+    avb_slot_verify_data_free(avb_slot_data);
+
+    return verify_result;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libfs_avb/avb_ops.h b/fs_mgr/libfs_avb/avb_ops.h
new file mode 100644
index 0000000..b39812d
--- /dev/null
+++ b/fs_mgr/libfs_avb/avb_ops.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <fs_avb/types.h>
+#include <fstab/fstab.h>
+#include <libavb/libavb.h>
+
+namespace android {
+namespace fs_mgr {
+
+// This class provides C++ bindings to interact with libavb, a small
+// self-contained piece of code that's intended to be used in bootloaders.
+// It mainly contains two functions:
+//   - ReadFromPartition(): to read AVB metadata from a given partition.
+//     It provides the implementation of AvbOps.read_from_partition() when
+//     reading metadata through libavb.
+//   - AvbSlotVerify(): the C++ binding of libavb->avb_slot_verify() to
+//     read and verify the metadata and store it into the out_data parameter.
+//     The caller MUST check the integrity of metadata against the
+//     androidboot.vbmeta.{hash_alg, size, digest} values from /proc/cmdline.
+//     e.g., see class AvbVerifier for more details.
+//
+class FsManagerAvbOps {
+  public:
+    FsManagerAvbOps();
+
+    static FsManagerAvbOps* GetInstanceFromAvbOps(AvbOps* ops) {
+        return reinterpret_cast<FsManagerAvbOps*>(ops->user_data);
+    }
+
+    AvbIOResult ReadFromPartition(const char* partition, int64_t offset, size_t num_bytes,
+                                  void* buffer, size_t* out_num_read);
+
+    AvbSlotVerifyResult AvbSlotVerify(const std::string& ab_suffix, AvbSlotVerifyFlags flags,
+                                      std::vector<VBMetaData>* out_vbmeta_images);
+
+  private:
+    std::string GetLogicalPath(const std::string& partition_name);
+    AvbOps avb_ops_;
+    Fstab fstab_;
+};
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
new file mode 100644
index 0000000..d9650f3
--- /dev/null
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -0,0 +1,613 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "avb_util.h"
+
+#include <unistd.h>
+
+#include <array>
+#include <sstream>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include "util.h"
+
+using android::base::Basename;
+using android::base::ReadFileToString;
+using android::base::StartsWith;
+using android::base::unique_fd;
+
+namespace android {
+namespace fs_mgr {
+
+std::string GetAvbPropertyDescriptor(const std::string& key,
+                                     const std::vector<VBMetaData>& vbmeta_images) {
+    size_t value_size;
+    for (const auto& vbmeta : vbmeta_images) {
+        const char* value = avb_property_lookup(vbmeta.data(), vbmeta.size(), key.data(),
+                                                key.size(), &value_size);
+        if (value != nullptr) {
+            return {value, value_size};
+        }
+    }
+    return "";
+}
+
+// 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
+bool ConstructVerityTable(const FsAvbHashtreeDescriptor& hashtree_desc,
+                          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)) {
+        verity_mode = "enforcing";  // Defaults to enforcing when it's absent.
+    }
+
+    // Converts veritymode to the format used in kernel.
+    std::string dm_verity_mode;
+    if (verity_mode == "enforcing") {
+        dm_verity_mode = "restart_on_corruption";
+    } else if (verity_mode == "logging") {
+        dm_verity_mode = "ignore_corruption";
+    } else if (verity_mode != "eio") {  // Default dm_verity_mode is eio.
+        LERROR << "Unknown androidboot.veritymode: " << verity_mode;
+        return false;
+    }
+
+    std::ostringstream hash_algorithm;
+    hash_algorithm << hashtree_desc.hash_algorithm;
+
+    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(),
+            hashtree_desc.root_digest, hashtree_desc.salt);
+    if (hashtree_desc.fec_size > 0) {
+        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()) {
+        target.SetVerityMode(dm_verity_mode);
+    }
+    // Always use ignore_zero_blocks.
+    target.IgnoreZeroBlocks();
+
+    LINFO << "Built verity table: '" << target.GetParameterString() << "'";
+
+    return table->AddTarget(std::make_unique<android::dm::DmTargetVerity>(target));
+}
+
+bool HashtreeDmVeritySetup(FstabEntry* fstab_entry, const FsAvbHashtreeDescriptor& hashtree_desc,
+                           bool wait_for_verity_dev) {
+    android::dm::DmTable table;
+    if (!ConstructVerityTable(hashtree_desc, fstab_entry->blk_device, &table) || !table.valid()) {
+        LERROR << "Failed to construct verity table.";
+        return false;
+    }
+    table.set_readonly(true);
+
+    const std::string mount_point(Basename(fstab_entry->mount_point));
+    const std::string device_name(GetVerityDeviceName(*fstab_entry));
+    android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
+    if (!dm.CreateDevice(device_name, table)) {
+        LERROR << "Couldn't create verity device!";
+        return false;
+    }
+
+    std::string dev_path;
+    if (!dm.GetDmDevicePathByName(device_name, &dev_path)) {
+        LERROR << "Couldn't get verity device path!";
+        return false;
+    }
+
+    // Marks the underlying block device as read-only.
+    SetBlockDeviceReadOnly(fstab_entry->blk_device);
+
+    // Updates fstab_rec->blk_device to verity device name.
+    fstab_entry->blk_device = dev_path;
+
+    // Makes sure we've set everything up properly.
+    if (wait_for_verity_dev && !WaitForFile(dev_path, 1s)) {
+        return false;
+    }
+
+    return true;
+}
+
+std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
+        const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images) {
+    bool found = false;
+    const uint8_t* desc_partition_name;
+    auto hashtree_desc = std::make_unique<FsAvbHashtreeDescriptor>();
+
+    for (const auto& vbmeta : vbmeta_images) {
+        size_t num_descriptors;
+        std::unique_ptr<const AvbDescriptor* [], decltype(&avb_free)> descriptors(
+                avb_descriptor_get_all(vbmeta.data(), vbmeta.size(), &num_descriptors), avb_free);
+
+        if (!descriptors || num_descriptors < 1) {
+            continue;
+        }
+
+        for (size_t n = 0; n < num_descriptors && !found; n++) {
+            AvbDescriptor desc;
+            if (!avb_descriptor_validate_and_byteswap(descriptors[n], &desc)) {
+                LWARNING << "Descriptor[" << n << "] is invalid";
+                continue;
+            }
+            if (desc.tag == AVB_DESCRIPTOR_TAG_HASHTREE) {
+                desc_partition_name =
+                        (const uint8_t*)descriptors[n] + sizeof(AvbHashtreeDescriptor);
+                if (!avb_hashtree_descriptor_validate_and_byteswap(
+                        (AvbHashtreeDescriptor*)descriptors[n], hashtree_desc.get())) {
+                    continue;
+                }
+                if (hashtree_desc->partition_name_len != partition_name.length()) {
+                    continue;
+                }
+                // Notes that desc_partition_name is not NUL-terminated.
+                std::string hashtree_partition_name((const char*)desc_partition_name,
+                                                    hashtree_desc->partition_name_len);
+                if (hashtree_partition_name == partition_name) {
+                    found = true;
+                }
+            }
+        }
+
+        if (found) break;
+    }
+
+    if (!found) {
+        LERROR << "Hashtree descriptor not found: " << partition_name;
+        return nullptr;
+    }
+
+    hashtree_desc->partition_name = partition_name;
+
+    const uint8_t* desc_salt = desc_partition_name + hashtree_desc->partition_name_len;
+    hashtree_desc->salt = BytesToHex(desc_salt, hashtree_desc->salt_len);
+
+    const uint8_t* desc_digest = desc_salt + hashtree_desc->salt_len;
+    hashtree_desc->root_digest = BytesToHex(desc_digest, hashtree_desc->root_digest_len);
+
+    return hashtree_desc;
+}
+
+bool LoadAvbHashtreeToEnableVerity(FstabEntry* fstab_entry, bool wait_for_verity_dev,
+                                   const std::vector<VBMetaData>& vbmeta_images,
+                                   const std::string& ab_suffix,
+                                   const std::string& ab_other_suffix) {
+    // 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 = DeriveAvbPartitionName(*fstab_entry, ab_suffix, ab_other_suffix);
+
+    if (partition_name.empty()) {
+        LERROR << "partition name is empty, cannot lookup AVB descriptors";
+        return false;
+    }
+
+    std::unique_ptr<FsAvbHashtreeDescriptor> hashtree_descriptor =
+            GetHashtreeDescriptor(partition_name, vbmeta_images);
+    if (!hashtree_descriptor) {
+        return false;
+    }
+
+    // Converts HASHTREE descriptor to verity table to load into kernel.
+    // When success, the new device path will be returned, e.g., /dev/block/dm-2.
+    return HashtreeDmVeritySetup(fstab_entry, *hashtree_descriptor, wait_for_verity_dev);
+}
+
+// Converts a AVB partition_name (without A/B suffix) to a device partition name.
+// e.g.,       "system" => "system_a",
+//       "system_other" => "system_b".
+//
+// If the device is non-A/B, converts it to a partition name without suffix.
+// e.g.,       "system" => "system",
+//       "system_other" => "system".
+std::string AvbPartitionToDevicePatition(const std::string& avb_partition_name,
+                                         const std::string& ab_suffix,
+                                         const std::string& ab_other_suffix) {
+    bool is_other_slot = false;
+    std::string sanitized_partition_name(avb_partition_name);
+
+    auto other_suffix = sanitized_partition_name.rfind("_other");
+    if (other_suffix != std::string::npos) {
+        sanitized_partition_name.erase(other_suffix);  // converts system_other => system
+        is_other_slot = true;
+    }
+
+    auto append_suffix = is_other_slot ? ab_other_suffix : ab_suffix;
+    return sanitized_partition_name + append_suffix;
+}
+
+// Converts fstab_entry.blk_device (with ab_suffix) to a AVB partition name.
+// e.g., "/dev/block/by-name/system_a", slot_select       => "system",
+//       "/dev/block/by-name/system_b", slot_select_other => "system_other".
+//
+// Or for a logical partition (with ab_suffix):
+// e.g., "system_a", slot_select       => "system",
+//       "system_b", slot_select_other => "system_other".
+std::string DeriveAvbPartitionName(const FstabEntry& fstab_entry, const std::string& ab_suffix,
+                                   const std::string& ab_other_suffix) {
+    std::string partition_name;
+    if (fstab_entry.fs_mgr_flags.logical) {
+        partition_name = fstab_entry.logical_partition_name;
+    } else {
+        partition_name = Basename(fstab_entry.blk_device);
+    }
+
+    if (fstab_entry.fs_mgr_flags.slot_select) {
+        auto found = partition_name.rfind(ab_suffix);
+        if (found != std::string::npos) {
+            partition_name.erase(found);  // converts system_a => system
+        }
+    } else if (fstab_entry.fs_mgr_flags.slot_select_other) {
+        auto found = partition_name.rfind(ab_other_suffix);
+        if (found != std::string::npos) {
+            partition_name.erase(found);  // converts system_b => system
+        }
+        partition_name += "_other";  // converts system => system_other
+    }
+
+    return partition_name;
+}
+
+off64_t GetTotalSize(int fd) {
+    off64_t saved_current = lseek64(fd, 0, SEEK_CUR);
+    if (saved_current == -1) {
+        PERROR << "Failed to get current position";
+        return -1;
+    }
+
+    // lseek64() returns the resulting offset location from the beginning of the file.
+    off64_t total_size = lseek64(fd, 0, SEEK_END);
+    if (total_size == -1) {
+        PERROR << "Failed to lseek64 to end of the partition";
+        return -1;
+    }
+
+    // Restores the original offset.
+    if (lseek64(fd, saved_current, SEEK_SET) == -1) {
+        PERROR << "Failed to lseek64 to the original offset: " << saved_current;
+    }
+
+    return total_size;
+}
+
+std::unique_ptr<AvbFooter> GetAvbFooter(int fd) {
+    std::array<uint8_t, AVB_FOOTER_SIZE> footer_buf;
+    auto footer = std::make_unique<AvbFooter>();
+
+    off64_t footer_offset = GetTotalSize(fd) - AVB_FOOTER_SIZE;
+
+    ssize_t num_read =
+            TEMP_FAILURE_RETRY(pread64(fd, footer_buf.data(), AVB_FOOTER_SIZE, footer_offset));
+    if (num_read < 0 || num_read != AVB_FOOTER_SIZE) {
+        PERROR << "Failed to read AVB footer at offset: " << footer_offset;
+        return nullptr;
+    }
+
+    if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_buf.data(), footer.get())) {
+        PERROR << "AVB footer verification failed at offset " << footer_offset;
+        return nullptr;
+    }
+
+    return footer;
+}
+
+bool ValidatePublicKeyBlob(const uint8_t* key, size_t length,
+                           const std::string& expected_key_blob) {
+    if (expected_key_blob.empty()) {  // no expectation of the key, return true.
+        return true;
+    }
+    if (expected_key_blob.size() != length) {
+        return false;
+    }
+    if (0 == memcmp(key, expected_key_blob.data(), length)) {
+        return true;
+    }
+    return false;
+}
+
+bool ValidatePublicKeyBlob(const std::string& key_blob_to_validate,
+                           const std::vector<std::string>& allowed_key_paths) {
+    std::string allowed_key_blob;
+    if (key_blob_to_validate.empty()) {
+        LWARNING << "Failed to validate an empty key";
+        return false;
+    }
+    for (const auto& path : allowed_key_paths) {
+        if (ReadFileToString(path, &allowed_key_blob)) {
+            if (key_blob_to_validate == allowed_key_blob) return true;
+        }
+    }
+    return false;
+}
+
+VBMetaVerifyResult VerifyVBMetaSignature(const VBMetaData& vbmeta,
+                                         const std::string& expected_public_key_blob,
+                                         std::string* out_public_key_data) {
+    const uint8_t* pk_data;
+    size_t pk_len;
+    ::AvbVBMetaVerifyResult vbmeta_ret;
+
+    vbmeta_ret = avb_vbmeta_image_verify(vbmeta.data(), vbmeta.size(), &pk_data, &pk_len);
+
+    if (out_public_key_data != nullptr) {
+        out_public_key_data->clear();
+        if (pk_len > 0) {
+            out_public_key_data->append(reinterpret_cast<const char*>(pk_data), pk_len);
+        }
+    }
+
+    switch (vbmeta_ret) {
+        case AVB_VBMETA_VERIFY_RESULT_OK:
+            if (pk_data == nullptr || pk_len <= 0) {
+                LERROR << vbmeta.partition()
+                       << ": Error verifying vbmeta image: failed to get public key";
+                return VBMetaVerifyResult::kError;
+            }
+            if (!ValidatePublicKeyBlob(pk_data, pk_len, expected_public_key_blob)) {
+                LERROR << vbmeta.partition() << ": Error verifying vbmeta image: public key used to"
+                       << " sign data does not match key in chain descriptor";
+                return VBMetaVerifyResult::kErrorVerification;
+            }
+            return VBMetaVerifyResult::kSuccess;
+        case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED:
+        case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH:
+        case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH:
+            LERROR << vbmeta.partition() << ": Error verifying vbmeta image: "
+                   << avb_vbmeta_verify_result_to_string(vbmeta_ret);
+            return VBMetaVerifyResult::kErrorVerification;
+        case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER:
+            // No way to continue this case.
+            LERROR << vbmeta.partition() << ": Error verifying vbmeta image: invalid vbmeta header";
+            break;
+        case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION:
+            // No way to continue this case.
+            LERROR << vbmeta.partition()
+                   << ": Error verifying vbmeta image: unsupported AVB version";
+            break;
+        default:
+            LERROR << "Unknown vbmeta image verify return value: " << vbmeta_ret;
+            break;
+    }
+
+    return VBMetaVerifyResult::kError;
+}
+
+std::unique_ptr<VBMetaData> VerifyVBMetaData(int fd, const std::string& partition_name,
+                                             const std::string& expected_public_key_blob,
+                                             std::string* out_public_key_data,
+                                             VBMetaVerifyResult* out_verify_result) {
+    uint64_t vbmeta_offset = 0;
+    uint64_t vbmeta_size = VBMetaData::kMaxVBMetaSize;
+    bool is_vbmeta_partition = StartsWith(partition_name, "vbmeta");
+
+    if (out_verify_result) {
+        *out_verify_result = VBMetaVerifyResult::kError;
+    }
+
+    if (!is_vbmeta_partition) {
+        std::unique_ptr<AvbFooter> footer = GetAvbFooter(fd);
+        if (!footer) {
+            return nullptr;
+        }
+        vbmeta_offset = footer->vbmeta_offset;
+        vbmeta_size = footer->vbmeta_size;
+    }
+
+    if (vbmeta_size > VBMetaData::kMaxVBMetaSize) {
+        LERROR << "VbMeta size in footer exceeds kMaxVBMetaSize";
+        return nullptr;
+    }
+
+    auto vbmeta = std::make_unique<VBMetaData>(vbmeta_size, partition_name);
+    ssize_t num_read = TEMP_FAILURE_RETRY(pread64(fd, vbmeta->data(), vbmeta_size, vbmeta_offset));
+    // Allows partial read for vbmeta partition, because its vbmeta_size is kMaxVBMetaSize.
+    if (num_read < 0 || (!is_vbmeta_partition && static_cast<uint64_t>(num_read) != vbmeta_size)) {
+        PERROR << partition_name << ": Failed to read vbmeta at offset " << vbmeta_offset
+               << " with size " << vbmeta_size;
+        return nullptr;
+    }
+
+    auto verify_result =
+            VerifyVBMetaSignature(*vbmeta, expected_public_key_blob, out_public_key_data);
+
+    if (out_verify_result != nullptr) {
+        *out_verify_result = verify_result;
+    }
+
+    if (verify_result == VBMetaVerifyResult::kSuccess ||
+        verify_result == VBMetaVerifyResult::kErrorVerification) {
+        return vbmeta;
+    }
+
+    return nullptr;
+}
+
+bool RollbackDetected(const std::string& partition_name ATTRIBUTE_UNUSED,
+                      uint64_t rollback_index ATTRIBUTE_UNUSED) {
+    // TODO(bowgotsai): Support rollback protection.
+    return false;
+}
+
+std::vector<ChainInfo> GetChainPartitionInfo(const VBMetaData& vbmeta, bool* fatal_error) {
+    CHECK(fatal_error != nullptr);
+    std::vector<ChainInfo> chain_partitions;
+
+    size_t num_descriptors;
+    std::unique_ptr<const AvbDescriptor* [], decltype(&avb_free)> descriptors(
+            avb_descriptor_get_all(vbmeta.data(), vbmeta.size(), &num_descriptors), avb_free);
+
+    if (!descriptors || num_descriptors < 1) {
+        return {};
+    }
+
+    for (size_t i = 0; i < num_descriptors; i++) {
+        AvbDescriptor desc;
+        if (!avb_descriptor_validate_and_byteswap(descriptors[i], &desc)) {
+            LERROR << "Descriptor[" << i << "] is invalid in vbmeta: " << vbmeta.partition();
+            *fatal_error = true;
+            return {};
+        }
+        if (desc.tag == AVB_DESCRIPTOR_TAG_CHAIN_PARTITION) {
+            AvbChainPartitionDescriptor chain_desc;
+            if (!avb_chain_partition_descriptor_validate_and_byteswap(
+                        (AvbChainPartitionDescriptor*)descriptors[i], &chain_desc)) {
+                LERROR << "Chain descriptor[" << i
+                       << "] is invalid in vbmeta: " << vbmeta.partition();
+                *fatal_error = true;
+                return {};
+            }
+            const char* chain_partition_name =
+                    ((const char*)descriptors[i]) + sizeof(AvbChainPartitionDescriptor);
+            const char* chain_public_key_blob =
+                    chain_partition_name + chain_desc.partition_name_len;
+            chain_partitions.emplace_back(
+                    std::string(chain_partition_name, chain_desc.partition_name_len),
+                    std::string(chain_public_key_blob, chain_desc.public_key_len));
+        }
+    }
+
+    return chain_partitions;
+}
+
+// Loads the vbmeta from a given path.
+std::unique_ptr<VBMetaData> LoadAndVerifyVbmetaByPath(
+        const std::string& image_path, const std::string& partition_name,
+        const std::string& expected_public_key_blob, bool allow_verification_error,
+        bool rollback_protection, bool is_chained_vbmeta, std::string* out_public_key_data,
+        bool* out_verification_disabled, VBMetaVerifyResult* out_verify_result) {
+    if (out_verify_result) {
+        *out_verify_result = VBMetaVerifyResult::kError;
+    }
+
+    // Ensures the device path (might be a symlink created by init) is ready to access.
+    if (!WaitForFile(image_path, 1s)) {
+        PERROR << "No such path: " << image_path;
+        return nullptr;
+    }
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(image_path.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd < 0) {
+        PERROR << "Failed to open: " << image_path;
+        return nullptr;
+    }
+
+    VBMetaVerifyResult verify_result;
+    std::unique_ptr<VBMetaData> vbmeta = VerifyVBMetaData(
+            fd, partition_name, expected_public_key_blob, out_public_key_data, &verify_result);
+    if (!vbmeta) {
+        LERROR << partition_name << ": Failed to load vbmeta, result: " << verify_result;
+        return nullptr;
+    }
+    vbmeta->set_vbmeta_path(image_path);
+
+    if (!allow_verification_error && verify_result == VBMetaVerifyResult::kErrorVerification) {
+        LERROR << partition_name << ": allow verification error is not allowed";
+        return nullptr;
+    }
+
+    std::unique_ptr<AvbVBMetaImageHeader> vbmeta_header =
+            vbmeta->GetVBMetaHeader(true /* update_vbmeta_size */);
+    if (!vbmeta_header) {
+        LERROR << partition_name << ": Failed to get vbmeta header";
+        return nullptr;
+    }
+
+    if (rollback_protection && RollbackDetected(partition_name, vbmeta_header->rollback_index)) {
+        return nullptr;
+    }
+
+    // vbmeta flags can only be set by the top-level vbmeta image.
+    if (is_chained_vbmeta && vbmeta_header->flags != 0) {
+        LERROR << partition_name << ": chained vbmeta image has non-zero flags";
+        return nullptr;
+    }
+
+    // Checks if verification has been disabled by setting a bit in the image.
+    if (out_verification_disabled) {
+        if (vbmeta_header->flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) {
+            LWARNING << "VERIFICATION_DISABLED bit is set for partition: " << partition_name;
+            *out_verification_disabled = true;
+        } else {
+            *out_verification_disabled = false;
+        }
+    }
+
+    if (out_verify_result) {
+        *out_verify_result = verify_result;
+    }
+
+    return vbmeta;
+}
+
+VBMetaVerifyResult LoadAndVerifyVbmetaByPartition(
+    const std::string& partition_name, const std::string& ab_suffix,
+    const std::string& ab_other_suffix, const std::string& expected_public_key_blob,
+    bool allow_verification_error, bool load_chained_vbmeta, bool rollback_protection,
+    std::function<std::string(const std::string&)> device_path_constructor, bool is_chained_vbmeta,
+    std::vector<VBMetaData>* out_vbmeta_images) {
+    auto image_path = device_path_constructor(
+        AvbPartitionToDevicePatition(partition_name, ab_suffix, ab_other_suffix));
+
+    bool verification_disabled = false;
+    VBMetaVerifyResult verify_result;
+    auto vbmeta = LoadAndVerifyVbmetaByPath(image_path, partition_name, expected_public_key_blob,
+                                            allow_verification_error, rollback_protection,
+                                            is_chained_vbmeta, nullptr /* out_public_key_data */,
+                                            &verification_disabled, &verify_result);
+
+    if (!vbmeta) {
+        return VBMetaVerifyResult::kError;
+    }
+    if (out_vbmeta_images) {
+        out_vbmeta_images->emplace_back(std::move(*vbmeta));
+    }
+
+    // Only loads chained vbmeta if AVB verification is NOT disabled.
+    if (!verification_disabled && load_chained_vbmeta) {
+        bool fatal_error = false;
+        auto chain_partitions = GetChainPartitionInfo(*out_vbmeta_images->rbegin(), &fatal_error);
+        if (fatal_error) {
+            return VBMetaVerifyResult::kError;
+        }
+        for (auto& chain : chain_partitions) {
+            auto sub_ret = LoadAndVerifyVbmetaByPartition(
+                chain.partition_name, ab_suffix, ab_other_suffix, chain.public_key_blob,
+                allow_verification_error, load_chained_vbmeta, rollback_protection,
+                device_path_constructor, true, /* is_chained_vbmeta */
+                out_vbmeta_images);
+            if (sub_ret != VBMetaVerifyResult::kSuccess) {
+                verify_result = sub_ret;  // might be 'ERROR' or 'ERROR VERIFICATION'.
+                if (verify_result == VBMetaVerifyResult::kError) {
+                    return verify_result;  // stop here if we got an 'ERROR'.
+                }
+            }
+        }
+    }
+
+    return verify_result;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libfs_avb/avb_util.h b/fs_mgr/libfs_avb/avb_util.h
new file mode 100644
index 0000000..09c786a
--- /dev/null
+++ b/fs_mgr/libfs_avb/avb_util.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <ostream>
+#include <string>
+#include <vector>
+
+#include <fstab/fstab.h>
+#include <libavb/libavb.h>
+#include <libdm/dm.h>
+
+#include "fs_avb/types.h"
+
+namespace android {
+namespace fs_mgr {
+
+struct ChainInfo {
+    std::string partition_name;
+    std::string public_key_blob;
+
+    ChainInfo(const std::string& chain_partition_name, const std::string& chain_public_key_blob)
+        : partition_name(chain_partition_name), public_key_blob(chain_public_key_blob) {}
+};
+
+std::string GetAvbPropertyDescriptor(const std::string& key,
+                                     const std::vector<VBMetaData>& vbmeta_images);
+
+// AvbHashtreeDescriptor to dm-verity table setup.
+std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
+        const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);
+
+bool ConstructVerityTable(const FsAvbHashtreeDescriptor& hashtree_desc,
+                          const std::string& blk_device, android::dm::DmTable* table);
+
+bool HashtreeDmVeritySetup(FstabEntry* fstab_entry, const FsAvbHashtreeDescriptor& hashtree_desc,
+                           bool wait_for_verity_dev);
+
+// Searches a Avb hashtree descriptor in vbmeta_images for fstab_entry, to enable dm-verity.
+bool LoadAvbHashtreeToEnableVerity(FstabEntry* fstab_entry, bool wait_for_verity_dev,
+                                   const std::vector<VBMetaData>& vbmeta_images,
+                                   const std::string& ab_suffix, const std::string& ab_other_suffix);
+
+// Converts AVB partition name to a device partition name.
+std::string AvbPartitionToDevicePatition(const std::string& avb_partition_name,
+                                         const std::string& ab_suffix,
+                                         const std::string& ab_other_suffix);
+
+// Converts by-name symlink to AVB partition name.
+std::string DeriveAvbPartitionName(const FstabEntry& fstab_entry, const std::string& ab_suffix,
+                                   const std::string& ab_other_suffix);
+
+// AvbFooter and AvbMetaImage maninpulations.
+off64_t GetTotalSize(int fd);
+
+std::unique_ptr<AvbFooter> GetAvbFooter(int fd);
+
+std::unique_ptr<VBMetaData> VerifyVBMetaData(int fd, const std::string& partition_name,
+                                             const std::string& expected_public_key_blob,
+                                             std::string* out_public_key_data,
+                                             VBMetaVerifyResult* out_verify_result);
+
+VBMetaVerifyResult VerifyVBMetaSignature(const VBMetaData& vbmeta,
+                                         const std::string& expected_public_key_blob,
+                                         std::string* out_public_key_data);
+
+bool ValidatePublicKeyBlob(const uint8_t* key, size_t length, const std::string& expected_key_blob);
+
+bool ValidatePublicKeyBlob(const std::string& key_blob_to_validate,
+                           const std::vector<std::string>& expected_key_paths);
+
+// Detects if whether a partition contains a rollback image.
+bool RollbackDetected(const std::string& partition_name, uint64_t rollback_index);
+
+// Extracts chain partition info.
+std::vector<ChainInfo> GetChainPartitionInfo(const VBMetaData& vbmeta, bool* fatal_error);
+
+// Loads the single vbmeta from a given path.
+std::unique_ptr<VBMetaData> LoadAndVerifyVbmetaByPath(
+        const std::string& image_path, const std::string& partition_name,
+        const std::string& expected_public_key_blob, bool allow_verification_error,
+        bool rollback_protection, bool is_chained_vbmeta, std::string* out_public_key_data,
+        bool* out_verification_disabled, VBMetaVerifyResult* out_verify_result);
+
+// Loads the top-level vbmeta and all its chained vbmeta images.
+// The actual device path is constructed at runtime by:
+// partition_name, ab_suffix, ab_other_suffix, and device_path_constructor.
+VBMetaVerifyResult LoadAndVerifyVbmetaByPartition(
+    const std::string& partition_name, const std::string& ab_suffix,
+    const std::string& ab_other_suffix, const std::string& expected_public_key_blob,
+    bool allow_verification_error, bool load_chained_vbmeta, bool rollback_protection,
+    std::function<std::string(const std::string&)> device_path_constructor, bool is_chained_vbmeta,
+    std::vector<VBMetaData>* out_vbmeta_images);
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
new file mode 100644
index 0000000..c4d7511
--- /dev/null
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -0,0 +1,494 @@
+/*
+ * 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 "fs_avb/fs_avb.h"
+
+#include <fcntl.h>
+#include <libgen.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <libavb/libavb.h>
+#include <libdm/dm.h>
+
+#include "avb_ops.h"
+#include "avb_util.h"
+#include "sha.h"
+#include "util.h"
+
+using android::base::Basename;
+using android::base::ParseUint;
+using android::base::ReadFileToString;
+using android::base::Split;
+using android::base::StringPrintf;
+
+namespace android {
+namespace fs_mgr {
+
+template <typename Hasher>
+std::pair<size_t, bool> VerifyVbmetaDigest(const std::vector<VBMetaData>& vbmeta_images,
+                                           const uint8_t* expected_digest) {
+    size_t total_size = 0;
+    Hasher hasher;
+    for (const auto& vbmeta : vbmeta_images) {
+        hasher.update(vbmeta.data(), vbmeta.size());
+        total_size += vbmeta.size();
+    }
+
+    bool matched = (memcmp(hasher.finalize(), expected_digest, Hasher::DIGEST_SIZE) == 0);
+
+    return std::make_pair(total_size, matched);
+}
+
+template <typename Hasher>
+std::pair<std::string, size_t> CalculateVbmetaDigest(const std::vector<VBMetaData>& vbmeta_images) {
+    std::string digest;
+    size_t total_size = 0;
+
+    Hasher hasher;
+    for (const auto& vbmeta : vbmeta_images) {
+        hasher.update(vbmeta.data(), vbmeta.size());
+        total_size += vbmeta.size();
+    }
+
+    // Converts digest bytes to a hex string.
+    digest = BytesToHex(hasher.finalize(), Hasher::DIGEST_SIZE);
+    return std::make_pair(digest, total_size);
+}
+
+// class AvbVerifier
+// -----------------
+// Reads the following values from kernel cmdline and provides the
+// VerifyVbmetaImages() to verify AvbSlotVerifyData.
+//   - androidboot.vbmeta.hash_alg
+//   - androidboot.vbmeta.size
+//   - androidboot.vbmeta.digest
+class AvbVerifier {
+  public:
+    // The factory method to return a unique_ptr<AvbVerifier>
+    static std::unique_ptr<AvbVerifier> Create();
+    bool VerifyVbmetaImages(const std::vector<VBMetaData>& vbmeta_images);
+
+  protected:
+    AvbVerifier() = default;
+
+  private:
+    HashAlgorithm hash_alg_;
+    uint8_t digest_[SHA512_DIGEST_LENGTH];
+    size_t vbmeta_size_;
+};
+
+std::unique_ptr<AvbVerifier> AvbVerifier::Create() {
+    std::unique_ptr<AvbVerifier> avb_verifier(new AvbVerifier());
+    if (!avb_verifier) {
+        LERROR << "Failed to create unique_ptr<AvbVerifier>";
+        return nullptr;
+    }
+
+    std::string value;
+    if (!fs_mgr_get_boot_config("vbmeta.size", &value) ||
+        !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_ = HashAlgorithm::kSHA256;
+    } else if (hash_alg == "sha512") {
+        expected_digest_size = SHA512_DIGEST_LENGTH * 2;
+        avb_verifier->hash_alg_ = HashAlgorithm::kSHA512;
+    } else {
+        LERROR << "Unknown hash algorithm: " << hash_alg.c_str();
+        return nullptr;
+    }
+
+    // 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 << ")";
+        return nullptr;
+    }
+
+    if (!HexToBytes(avb_verifier->digest_, sizeof(avb_verifier->digest_), digest)) {
+        LERROR << "Hash digest contains non-hexidecimal character: " << digest.c_str();
+        return nullptr;
+    }
+
+    return avb_verifier;
+}
+
+bool AvbVerifier::VerifyVbmetaImages(const std::vector<VBMetaData>& vbmeta_images) {
+    if (vbmeta_images.empty()) {
+        LERROR << "No vbmeta images";
+        return false;
+    }
+
+    size_t total_size = 0;
+    bool digest_matched = false;
+
+    if (hash_alg_ == HashAlgorithm::kSHA256) {
+        std::tie(total_size, digest_matched) =
+                VerifyVbmetaDigest<SHA256Hasher>(vbmeta_images, digest_);
+    } else if (hash_alg_ == HashAlgorithm::kSHA512) {
+        std::tie(total_size, digest_matched) =
+                VerifyVbmetaDigest<SHA512Hasher>(vbmeta_images, digest_);
+    }
+
+    if (total_size != vbmeta_size_) {
+        LERROR << "total vbmeta size mismatch: " << total_size << " (expected: " << vbmeta_size_
+               << ")";
+        return false;
+    }
+
+    if (!digest_matched) {
+        LERROR << "vbmeta digest mismatch";
+        return false;
+    }
+
+    return true;
+}
+
+// class AvbHandle
+// ---------------
+AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(
+        const std::string& partition_name, const std::string& ab_suffix,
+        const std::string& ab_other_suffix, const std::string& expected_public_key_path,
+        const HashAlgorithm& hash_algorithm, bool allow_verification_error,
+        bool load_chained_vbmeta, bool rollback_protection,
+        std::function<std::string(const std::string&)> custom_device_path) {
+    AvbUniquePtr avb_handle(new AvbHandle());
+    if (!avb_handle) {
+        LERROR << "Failed to allocate AvbHandle";
+        return nullptr;
+    }
+
+    std::string expected_key_blob;
+    if (!expected_public_key_path.empty()) {
+        if (access(expected_public_key_path.c_str(), F_OK) != 0) {
+            LERROR << "Expected public key path doesn't exist: " << expected_public_key_path;
+            return nullptr;
+        } else if (!ReadFileToString(expected_public_key_path, &expected_key_blob)) {
+            LERROR << "Failed to load: " << expected_public_key_path;
+            return nullptr;
+        }
+    }
+
+    auto android_by_name_symlink = [](const std::string& partition_name_with_ab) {
+        return "/dev/block/by-name/" + partition_name_with_ab;
+    };
+
+    auto device_path = custom_device_path ? custom_device_path : android_by_name_symlink;
+
+    auto verify_result = LoadAndVerifyVbmetaByPartition(
+        partition_name, ab_suffix, ab_other_suffix, expected_key_blob, allow_verification_error,
+        load_chained_vbmeta, rollback_protection, device_path, false,
+        /* is_chained_vbmeta */ &avb_handle->vbmeta_images_);
+    switch (verify_result) {
+        case VBMetaVerifyResult::kSuccess:
+            avb_handle->status_ = AvbHandleStatus::kSuccess;
+            break;
+        case VBMetaVerifyResult::kErrorVerification:
+            avb_handle->status_ = AvbHandleStatus::kVerificationError;
+            break;
+        default:
+            LERROR << "LoadAndVerifyVbmetaByPartition failed, result: " << verify_result;
+            return nullptr;
+    }
+
+    // Sanity check here because we have to use vbmeta_images_[0] below.
+    if (avb_handle->vbmeta_images_.size() < 1) {
+        LERROR << "LoadAndVerifyVbmetaByPartition failed, no vbmeta loaded";
+        return nullptr;
+    }
+
+    // Sets the MAJOR.MINOR for init to set it into "ro.boot.avb_version".
+    avb_handle->avb_version_ = StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
+
+    // Checks any disabled flag is set.
+    std::unique_ptr<AvbVBMetaImageHeader> vbmeta_header =
+            avb_handle->vbmeta_images_[0].GetVBMetaHeader();
+    bool verification_disabled = ((AvbVBMetaImageFlags)vbmeta_header->flags &
+                                  AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
+    bool hashtree_disabled =
+            ((AvbVBMetaImageFlags)vbmeta_header->flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+    if (verification_disabled) {
+        avb_handle->status_ = AvbHandleStatus::kVerificationDisabled;
+    } else if (hashtree_disabled) {
+        avb_handle->status_ = AvbHandleStatus::kHashtreeDisabled;
+    }
+
+    // Calculates the summary info for all vbmeta_images_;
+    std::string digest;
+    size_t total_size;
+    if (hash_algorithm == HashAlgorithm::kSHA256) {
+        std::tie(digest, total_size) =
+                CalculateVbmetaDigest<SHA256Hasher>(avb_handle->vbmeta_images_);
+    } else if (hash_algorithm == HashAlgorithm::kSHA512) {
+        std::tie(digest, total_size) =
+                CalculateVbmetaDigest<SHA512Hasher>(avb_handle->vbmeta_images_);
+    } else {
+        LERROR << "Invalid hash algorithm";
+        return nullptr;
+    }
+    avb_handle->vbmeta_info_ = VBMetaInfo(digest, hash_algorithm, total_size);
+
+    LINFO << "Returning avb_handle with status: " << avb_handle->status_;
+    return avb_handle;
+}
+
+AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry) {
+    if (fstab_entry.avb_keys.empty()) {
+        LERROR << "avb_keys=/path/to/key(s) is missing for " << fstab_entry.mount_point;
+        return nullptr;
+    }
+
+    // Binds allow_verification_error and rollback_protection to device unlock state.
+    bool allow_verification_error = IsDeviceUnlocked();
+    bool rollback_protection = !allow_verification_error;
+
+    std::string public_key_data;
+    bool verification_disabled = false;
+    VBMetaVerifyResult verify_result = VBMetaVerifyResult::kError;
+    std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(
+            fstab_entry.blk_device, "" /* partition_name, no need for a standalone path */,
+            "" /* expected_public_key_blob, */, allow_verification_error, rollback_protection,
+            false /* not is_chained_vbmeta */, &public_key_data, &verification_disabled,
+            &verify_result);
+
+    if (!vbmeta) {
+        LERROR << "Failed to load vbmeta: " << fstab_entry.blk_device;
+        return nullptr;
+    }
+
+    AvbUniquePtr avb_handle(new AvbHandle());
+    if (!avb_handle) {
+        LERROR << "Failed to allocate AvbHandle";
+        return nullptr;
+    }
+    avb_handle->vbmeta_images_.emplace_back(std::move(*vbmeta));
+
+    switch (verify_result) {
+        case VBMetaVerifyResult::kSuccess:
+            avb_handle->status_ = AvbHandleStatus::kSuccess;
+            break;
+        case VBMetaVerifyResult::kErrorVerification:
+            avb_handle->status_ = AvbHandleStatus::kVerificationError;
+            break;
+        default:
+            LERROR << "LoadAndVerifyVbmetaByPath failed, result: " << verify_result;
+            return nullptr;
+    }
+
+    if (!ValidatePublicKeyBlob(public_key_data, Split(fstab_entry.avb_keys, ":"))) {
+        avb_handle->status_ = AvbHandleStatus::kVerificationError;
+        LWARNING << "Found unknown public key used to sign " << fstab_entry.mount_point;
+        if (!allow_verification_error) {
+            LERROR << "Unknown public key is not allowed";
+            return nullptr;
+        }
+    }
+
+    if (verification_disabled) {
+        LINFO << "AVB verification disabled on: " << fstab_entry.mount_point;
+        avb_handle->status_ = AvbHandleStatus::kVerificationDisabled;
+    }
+
+    LINFO << "Returning avb_handle for '" << fstab_entry.mount_point
+          << "' with status: " << avb_handle->status_;
+    return avb_handle;
+}
+
+AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta() {
+    // Loads inline vbmeta images, starting from /vbmeta.
+    return LoadAndVerifyVbmeta("vbmeta", fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix(),
+                               {} /* expected_public_key, already checked by bootloader */,
+                               HashAlgorithm::kSHA256,
+                               IsDeviceUnlocked(), /* allow_verification_error */
+                               true,               /* load_chained_vbmeta */
+                               false, /* rollback_protection, already checked by bootloader */
+                               nullptr /* custom_device_path */);
+}
+
+// TODO(b/128807537): removes this function.
+AvbUniquePtr AvbHandle::Open() {
+    bool is_device_unlocked = IsDeviceUnlocked();
+
+    AvbUniquePtr avb_handle(new AvbHandle());
+    if (!avb_handle) {
+        LERROR << "Failed to allocate AvbHandle";
+        return nullptr;
+    }
+
+    FsManagerAvbOps avb_ops;
+    AvbSlotVerifyFlags flags = is_device_unlocked ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
+                                                  : AVB_SLOT_VERIFY_FLAGS_NONE;
+    AvbSlotVerifyResult verify_result =
+            avb_ops.AvbSlotVerify(fs_mgr_get_slot_suffix(), flags, &avb_handle->vbmeta_images_);
+
+    // Only allow the following verify results:
+    //   - AVB_SLOT_VERIFY_RESULT_OK.
+    //   - AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION (UNLOCKED only).
+    //     Might occur in either the top-level vbmeta or a chained vbmeta.
+    //   - AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED (UNLOCKED only).
+    //     Could only occur in a chained vbmeta. Because we have *dummy* operations in
+    //     FsManagerAvbOps such that avb_ops->validate_vbmeta_public_key() used to validate
+    //     the public key of the top-level vbmeta always pass in userspace here.
+    //
+    // The following verify result won't happen, because the *dummy* operation
+    // avb_ops->read_rollback_index() always returns the minimum value zero. So rollbacked
+    // vbmeta images, which should be caught in the bootloader stage, won't be detected here.
+    //   - AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX
+    switch (verify_result) {
+        case AVB_SLOT_VERIFY_RESULT_OK:
+            avb_handle->status_ = AvbHandleStatus::kSuccess;
+            break;
+        case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
+        case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
+            if (!is_device_unlocked) {
+                LERROR << "ERROR_VERIFICATION / PUBLIC_KEY_REJECTED isn't allowed "
+                       << "if the device is LOCKED";
+                return nullptr;
+            }
+            avb_handle->status_ = AvbHandleStatus::kVerificationError;
+            break;
+        default:
+            LERROR << "avb_slot_verify failed, result: " << verify_result;
+            return nullptr;
+    }
+
+    // Sets the MAJOR.MINOR for init to set it into "ro.boot.avb_version".
+    avb_handle->avb_version_ = StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
+
+    // Checks whether FLAGS_VERIFICATION_DISABLED is set:
+    //   - Only the top-level vbmeta struct is read.
+    //   - vbmeta struct in other partitions are NOT processed, including AVB HASH descriptor(s)
+    //     and AVB HASHTREE descriptor(s).
+    AvbVBMetaImageHeader vbmeta_header;
+    avb_vbmeta_image_header_to_host_byte_order(
+            (AvbVBMetaImageHeader*)avb_handle->vbmeta_images_[0].data(), &vbmeta_header);
+    bool verification_disabled = ((AvbVBMetaImageFlags)vbmeta_header.flags &
+                                  AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
+
+    if (verification_disabled) {
+        avb_handle->status_ = AvbHandleStatus::kVerificationDisabled;
+    } else {
+        // Verifies vbmeta structs against the digest passed from bootloader in kernel cmdline.
+        std::unique_ptr<AvbVerifier> avb_verifier = AvbVerifier::Create();
+        if (!avb_verifier) {
+            LERROR << "Failed to create AvbVerifier";
+            return nullptr;
+        }
+        if (!avb_verifier->VerifyVbmetaImages(avb_handle->vbmeta_images_)) {
+            LERROR << "VerifyVbmetaImages failed";
+            return nullptr;
+        }
+
+        // Checks whether FLAGS_HASHTREE_DISABLED is set.
+        bool hashtree_disabled = ((AvbVBMetaImageFlags)vbmeta_header.flags &
+                                  AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+        if (hashtree_disabled) {
+            avb_handle->status_ = AvbHandleStatus::kHashtreeDisabled;
+        }
+    }
+
+    LINFO << "Returning avb_handle with status: " << avb_handle->status_;
+    return avb_handle;
+}
+
+AvbHashtreeResult AvbHandle::SetUpStandaloneAvbHashtree(FstabEntry* fstab_entry,
+                                                        bool wait_for_verity_dev) {
+    auto avb_handle = LoadAndVerifyVbmeta(*fstab_entry);
+    if (!avb_handle) {
+        return AvbHashtreeResult::kFail;
+    }
+
+    return avb_handle->SetUpAvbHashtree(fstab_entry, wait_for_verity_dev);
+}
+
+AvbHashtreeResult AvbHandle::SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev) {
+    if (!fstab_entry || status_ == AvbHandleStatus::kUninitialized || vbmeta_images_.size() < 1) {
+        return AvbHashtreeResult::kFail;
+    }
+
+    if (status_ == AvbHandleStatus::kHashtreeDisabled ||
+        status_ == AvbHandleStatus::kVerificationDisabled) {
+        LINFO << "AVB HASHTREE disabled on: " << fstab_entry->mount_point;
+        return AvbHashtreeResult::kDisabled;
+    }
+
+    if (!LoadAvbHashtreeToEnableVerity(fstab_entry, wait_for_verity_dev, vbmeta_images_,
+                                       fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix())) {
+        return AvbHashtreeResult::kFail;
+    }
+
+    return AvbHashtreeResult::kSuccess;
+}
+
+bool AvbHandle::TearDownAvbHashtree(FstabEntry* fstab_entry, bool wait) {
+    if (!fstab_entry) {
+        return false;
+    }
+
+    const std::string device_name(GetVerityDeviceName(*fstab_entry));
+
+    // TODO: remove duplicated code with UnmapDevice()
+    android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
+    std::string path;
+    if (wait) {
+        dm.GetDmDevicePathByName(device_name, &path);
+    }
+    if (!dm.DeleteDevice(device_name)) {
+        return false;
+    }
+    if (!path.empty() && !WaitForFile(path, 1000ms, FileWaitMode::DoesNotExist)) {
+        return false;
+    }
+
+    return true;
+}
+
+std::string AvbHandle::GetSecurityPatchLevel(const FstabEntry& fstab_entry) const {
+    if (vbmeta_images_.size() < 1) {
+        return "";
+    }
+    std::string avb_partition_name = DeriveAvbPartitionName(fstab_entry, fs_mgr_get_slot_suffix(),
+                                                            fs_mgr_get_other_slot_suffix());
+    auto avb_prop_name = "com.android.build." + avb_partition_name + ".security_patch";
+    return GetAvbPropertyDescriptor(avb_prop_name, vbmeta_images_);
+}
+
+bool AvbHandle::IsDeviceUnlocked() {
+    return android::fs_mgr::IsDeviceUnlocked();
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libfs_avb/fs_avb_util.cpp b/fs_mgr/libfs_avb/fs_avb_util.cpp
new file mode 100644
index 0000000..f82f83d
--- /dev/null
+++ b/fs_mgr/libfs_avb/fs_avb_util.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "fs_avb/fs_avb_util.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/strings.h>
+#include <fstab/fstab.h>
+#include <libavb/libavb.h>
+#include <libdm/dm.h>
+
+#include "avb_util.h"
+#include "util.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Given a FstabEntry, loads and verifies the vbmeta, to extract the Avb Hashtree descriptor.
+std::unique_ptr<VBMetaData> LoadAndVerifyVbmeta(const FstabEntry& fstab_entry,
+                                                const std::string& expected_public_key_blob,
+                                                std::string* out_public_key_data,
+                                                std::string* out_avb_partition_name,
+                                                VBMetaVerifyResult* out_verify_result) {
+    // 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 avb_partition_name = DeriveAvbPartitionName(fstab_entry, fs_mgr_get_slot_suffix(),
+                                                            fs_mgr_get_other_slot_suffix());
+    if (out_avb_partition_name) {
+        *out_avb_partition_name = avb_partition_name;
+    }
+
+    // Updates fstab_entry->blk_device from <partition> to /dev/block/dm-<N> if
+    // it's a logical partition.
+    std::string device_path = fstab_entry.blk_device;
+    if (fstab_entry.fs_mgr_flags.logical &&
+        !android::base::StartsWith(fstab_entry.blk_device, "/")) {
+        dm::DeviceMapper& dm = dm::DeviceMapper::Instance();
+        if (!dm.GetDmDevicePathByName(fstab_entry.blk_device, &device_path)) {
+            LERROR << "Failed to resolve logical device path for: " << fstab_entry.blk_device;
+            return nullptr;
+        }
+    }
+
+    return LoadAndVerifyVbmetaByPath(device_path, avb_partition_name, expected_public_key_blob,
+                                     true /* allow_verification_error */,
+                                     false /* rollback_protection */, false /* is_chained_vbmeta */,
+                                     out_public_key_data, nullptr /* out_verification_disabled */,
+                                     out_verify_result);
+}
+
+// Given a path, loads and verifies the vbmeta, to extract the Avb Hashtree descriptor.
+std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
+        const std::string& avb_partition_name, VBMetaData&& vbmeta) {
+    if (!vbmeta.size()) return nullptr;
+
+    std::vector<VBMetaData> vbmeta_images;
+    vbmeta_images.emplace_back(std::move(vbmeta));
+    return GetHashtreeDescriptor(avb_partition_name, vbmeta_images);
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
new file mode 100644
index 0000000..521f2d5
--- /dev/null
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
@@ -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.
+ */
+
+#pragma once
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <fs_avb/types.h>
+#include <fstab/fstab.h>
+#include <libavb/libavb.h>
+
+namespace android {
+namespace fs_mgr {
+
+struct VBMetaInfo {
+    std::string digest;
+    HashAlgorithm hash_algorithm;
+    size_t total_size;
+
+    VBMetaInfo() {}
+
+    VBMetaInfo(std::string digest_value, HashAlgorithm algorithm, size_t size)
+        : digest(std::move(digest_value)), hash_algorithm(algorithm), total_size(size) {}
+};
+
+class FsManagerAvbOps;
+
+class AvbHandle;
+using AvbUniquePtr = std::unique_ptr<AvbHandle>;
+
+// Provides a factory method to return a unique_ptr pointing to itself and the
+// SetUpAvbHashtree() function to extract dm-verity parameters from AVB HASHTREE
+// descriptors to load verity table into kernel through ioctl.
+class AvbHandle {
+  public:
+    // The factory methods to return a AvbUniquePtr that holds
+    // the verified AVB (external/avb) metadata of all verified partitions
+    // in vbmeta_images_.
+    //
+    // The metadata is checked against the following values from /proc/cmdline.
+    //   - androidboot.vbmeta.{hash_alg, size, digest}.
+    //
+    // A typical usage will be:
+    //   - AvbUniquePtr handle = AvbHandle::Open(); or
+    //   - AvbUniquePtr handle = AvbHandle::LoadAndVerifyVbmeta();
+    //
+    // Possible return values:
+    //   - nullptr: any error when reading and verifying the metadata,
+    //     e.g., I/O error, digest value mismatch, size mismatch, etc.
+    //
+    //   - a valid unique_ptr with status AvbHandleStatus::HashtreeDisabled:
+    //     to support the existing 'adb disable-verity' feature in Android.
+    //     It's very helpful for developers to make the filesystem writable to
+    //     allow replacing binaries on the device.
+    //
+    //   - a valid unique_ptr with status AvbHandleStatus::VerificationDisabled:
+    //     to support 'avbctl disable-verification': only the top-level
+    //     vbmeta is read, vbmeta structs in other partitions are not processed.
+    //     It's needed to bypass AVB when using the generic system.img to run
+    //     VTS for project Treble.
+    //
+    //   - a valid unique_ptr with status AvbHandleStatus::VerificationError:
+    //     there is verification error when libavb loads vbmeta from each
+    //     partition. This is only allowed when the device is unlocked.
+    //
+    //   - a valid unique_ptr with status AvbHandleStatus::Success: the metadata
+    //     is verified and can be trusted.
+    //
+    // TODO(bowgotsai): remove Open() and switch to LoadAndVerifyVbmeta().
+    static AvbUniquePtr Open();                 // loads inline vbmeta, via libavb.
+    static AvbUniquePtr LoadAndVerifyVbmeta();  // loads inline vbmeta.
+    static AvbUniquePtr LoadAndVerifyVbmeta(
+            const FstabEntry& fstab_entry);     // loads offline vbmeta.
+    static AvbUniquePtr LoadAndVerifyVbmeta(    // loads offline vbmeta.
+            const std::string& partition_name, const std::string& ab_suffix,
+            const std::string& ab_other_suffix, const std::string& expected_public_key,
+            const HashAlgorithm& hash_algorithm, bool allow_verification_error,
+            bool load_chained_vbmeta, bool rollback_protection,
+            std::function<std::string(const std::string&)> custom_device_path = nullptr);
+
+    // Sets up dm-verity on the given fstab entry.
+    // The 'wait_for_verity_dev' parameter makes this function wait for the
+    // verity device to get created before return.
+    //
+    // Return value:
+    //   - kSuccess: successfully loads dm-verity table into kernel.
+    //   - kFailed: failed to setup dm-verity, e.g., vbmeta verification error,
+    //     failed to get the HASHTREE descriptor, runtime error when set up
+    //     device-mapper, etc.
+    //   - kDisabled: hashtree is disabled.
+    AvbHashtreeResult SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev);
+
+    // Similar to above, but loads the offline vbmeta from the end of fstab_entry->blk_device.
+    static AvbHashtreeResult SetUpStandaloneAvbHashtree(FstabEntry* fstab_entry,
+                                                        bool wait_for_verity_dev = true);
+
+    // Tear down dm devices created by SetUp[Standalone]AvbHashtree
+    // The 'wait' parameter makes this function wait for the verity device to get destroyed
+    // before return.
+    static bool TearDownAvbHashtree(FstabEntry* fstab_entry, bool wait);
+
+    static bool IsDeviceUnlocked();
+
+    std::string GetSecurityPatchLevel(const FstabEntry& fstab_entry) const;
+
+    const std::string& avb_version() const { return avb_version_; }
+    const VBMetaInfo& vbmeta_info() const { return vbmeta_info_; }
+    AvbHandleStatus status() const { return status_; }
+
+    AvbHandle(const AvbHandle&) = delete;             // no copy
+    AvbHandle& operator=(const AvbHandle&) = delete;  // no assignment
+
+    AvbHandle(AvbHandle&&) noexcept = delete;             // no move
+    AvbHandle& operator=(AvbHandle&&) noexcept = delete;  // no move assignment
+
+  private:
+    AvbHandle() : status_(AvbHandleStatus::kUninitialized) {}
+
+    std::vector<VBMetaData> vbmeta_images_;
+    VBMetaInfo vbmeta_info_;  // A summary info for vbmeta_images_.
+    AvbHandleStatus status_;
+    std::string avb_version_;
+};
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
new file mode 100644
index 0000000..ec8badb
--- /dev/null
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <fs_avb/types.h>
+#include <fstab/fstab.h>
+#include <libavb/libavb.h>
+
+namespace android {
+namespace fs_mgr {
+
+// Given a FstabEntry, loads and verifies the vbmeta.
+std::unique_ptr<VBMetaData> LoadAndVerifyVbmeta(const FstabEntry& fstab_entry,
+                                                const std::string& expected_public_key_blob,
+                                                std::string* out_public_key_data,
+                                                std::string* out_avb_partition_name,
+                                                VBMetaVerifyResult* out_verify_result);
+
+// Gets the hashtree descriptor for avb_partition_name from the vbmeta.
+std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
+        const std::string& avb_partition_name, VBMetaData&& vbmeta);
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libfs_avb/include/fs_avb/types.h b/fs_mgr/libfs_avb/include/fs_avb/types.h
new file mode 100644
index 0000000..bd638e6
--- /dev/null
+++ b/fs_mgr/libfs_avb/include/fs_avb/types.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <cstring>
+#include <memory>
+#include <ostream>
+
+#include <libavb/libavb.h>
+
+namespace android {
+namespace fs_mgr {
+
+enum class VBMetaVerifyResult {
+    kSuccess = 0,
+    kError = 1,
+    kErrorVerification = 2,
+};
+
+std::ostream& operator<<(std::ostream& os, VBMetaVerifyResult);
+
+enum class AvbHashtreeResult {
+    kSuccess = 0,
+    kFail,
+    kDisabled,
+};
+
+enum class HashAlgorithm {
+    kInvalid = 0,
+    kSHA256 = 1,
+    kSHA512 = 2,
+};
+
+enum class AvbHandleStatus {
+    kSuccess = 0,
+    kUninitialized = 1,
+    kHashtreeDisabled = 2,
+    kVerificationDisabled = 3,
+    kVerificationError = 4,
+};
+
+std::ostream& operator<<(std::ostream& os, AvbHandleStatus status);
+
+struct FsAvbHashtreeDescriptor : AvbHashtreeDescriptor {
+    std::string partition_name;
+    std::string salt;
+    std::string root_digest;
+};
+
+class VBMetaData {
+  public:
+    // Constructors
+    VBMetaData() : vbmeta_ptr_(nullptr), vbmeta_size_(0){};
+
+    VBMetaData(const uint8_t* data, size_t size, const std::string& partition_name)
+        : vbmeta_ptr_(new (std::nothrow) uint8_t[size]),
+          vbmeta_size_(size),
+          partition_name_(partition_name) {
+        // The ownership of data is NOT transferred, i.e., the caller still
+        // needs to release the memory as we make a copy here.
+        std::memcpy(vbmeta_ptr_.get(), data, size * sizeof(uint8_t));
+    }
+
+    explicit VBMetaData(size_t size, const std::string& partition_name)
+        : vbmeta_ptr_(new (std::nothrow) uint8_t[size]),
+          vbmeta_size_(size),
+          partition_name_(partition_name) {}
+
+    // Extracts vbmeta header from the vbmeta buffer, set update_vbmeta_size to
+    // true to update vbmeta_size_ to the actual size with valid content.
+    std::unique_ptr<AvbVBMetaImageHeader> GetVBMetaHeader(bool update_vbmeta_size = false);
+
+    // Sets the vbmeta_path where we load the vbmeta data. Could be a partition or a file.
+    // e.g.,
+    // - /dev/block/by-name/system_a
+    // - /path/to/system_other.img.
+    void set_vbmeta_path(std::string vbmeta_path) { vbmeta_path_ = std::move(vbmeta_path); }
+
+    // Get methods for each data member.
+    const std::string& partition() const { return partition_name_; }
+    const std::string& vbmeta_path() const { return vbmeta_path_; }
+    uint8_t* data() const { return vbmeta_ptr_.get(); }
+    const size_t& size() const { return vbmeta_size_; }
+
+    // Maximum size of a vbmeta data - 64 KiB.
+    static const size_t kMaxVBMetaSize = 64 * 1024;
+
+  private:
+    std::unique_ptr<uint8_t[]> vbmeta_ptr_;
+    size_t vbmeta_size_;
+    std::string partition_name_;
+    std::string vbmeta_path_;
+};
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libfs_avb/run_tests.sh b/fs_mgr/libfs_avb/run_tests.sh
new file mode 100755
index 0000000..5d2ce3d
--- /dev/null
+++ b/fs_mgr/libfs_avb/run_tests.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# Run host tests
+atest libfs_avb_test                 # Tests public libfs_avb APIs.
+atest libfs_avb_internal_test        # Tests libfs_avb private APIs.
+
+# Run device tests
+atest libfs_avb_device_test          # Test public libfs_avb APIs on a device.
diff --git a/fs_mgr/libfs_avb/sha.h b/fs_mgr/libfs_avb/sha.h
new file mode 100644
index 0000000..2d3ca6d
--- /dev/null
+++ b/fs_mgr/libfs_avb/sha.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.
+ */
+
+#pragma once
+
+#include <openssl/sha.h>
+
+namespace android {
+namespace fs_mgr {
+
+class SHA256Hasher {
+  private:
+    SHA256_CTX sha256_ctx;
+    uint8_t hash[SHA256_DIGEST_LENGTH];
+
+  public:
+    enum { DIGEST_SIZE = SHA256_DIGEST_LENGTH };
+
+    SHA256Hasher() { SHA256_Init(&sha256_ctx); }
+
+    void update(const uint8_t* data, size_t data_size) {
+        SHA256_Update(&sha256_ctx, data, data_size);
+    }
+
+    const uint8_t* finalize() {
+        SHA256_Final(hash, &sha256_ctx);
+        return hash;
+    }
+};
+
+class SHA512Hasher {
+  private:
+    SHA512_CTX sha512_ctx;
+    uint8_t hash[SHA512_DIGEST_LENGTH];
+
+  public:
+    enum { DIGEST_SIZE = SHA512_DIGEST_LENGTH };
+
+    SHA512Hasher() { SHA512_Init(&sha512_ctx); }
+
+    void update(const uint8_t* data, size_t data_size) {
+        SHA512_Update(&sha512_ctx, data, data_size);
+    }
+
+    const uint8_t* finalize() {
+        SHA512_Final(hash, &sha512_ctx);
+        return hash;
+    }
+};
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
new file mode 100644
index 0000000..0d342d3
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -0,0 +1,1617 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <endian.h>
+
+#include <android-base/unique_fd.h>
+#include <base/files/file_util.h>
+#include <base/rand_util.h>
+#include <base/strings/string_util.h>
+#include <libavb/libavb.h>
+
+#include "avb_util.h"
+#include "fs_avb_test_util.h"
+
+// Target classes or functions to test:
+using android::fs_mgr::AvbPartitionToDevicePatition;
+using android::fs_mgr::DeriveAvbPartitionName;
+using android::fs_mgr::FstabEntry;
+using android::fs_mgr::GetAvbFooter;
+using android::fs_mgr::GetAvbPropertyDescriptor;
+using android::fs_mgr::GetChainPartitionInfo;
+using android::fs_mgr::GetTotalSize;
+using android::fs_mgr::LoadAndVerifyVbmetaByPartition;
+using android::fs_mgr::LoadAndVerifyVbmetaByPath;
+using android::fs_mgr::ValidatePublicKeyBlob;
+using android::fs_mgr::VBMetaData;
+using android::fs_mgr::VBMetaVerifyResult;
+using android::fs_mgr::VerifyVBMetaData;
+using android::fs_mgr::VerifyVBMetaSignature;
+
+namespace fs_avb_host_test {
+
+class AvbUtilTest : public BaseFsAvbTest {
+  public:
+    AvbUtilTest(){};
+
+  protected:
+    ~AvbUtilTest(){};
+    // Helper function for VerifyVBMetaSignature test. Modifies vbmeta.data()
+    // in a number of places at |offset| of size |length| and checks that
+    // VerifyVBMetaSignature() returns |expected_result|.
+    bool TestVBMetaModification(VBMetaVerifyResult expected_result, const VBMetaData& vbmeta,
+                                size_t offset, size_t length);
+    // Modifies a random bit for a file, in the range of [offset, offset + length - 1].
+    void ModifyFile(const base::FilePath& file_path, size_t offset, ssize_t length);
+
+    // Loads the content of avb_image_path and comparies it with the content of vbmeta.
+    bool CompareVBMeta(const base::FilePath& avb_image_path, const VBMetaData& expected_vbmeta);
+
+    // Sets the flas in vbmeta header, the image_path could be a vbmeta.img or a system.img.
+    void SetVBMetaFlags(const base::FilePath& image_path, uint32_t flags);
+};
+
+void AvbUtilTest::SetVBMetaFlags(const base::FilePath& image_path, uint32_t flags) {
+    if (!base::PathExists(image_path)) return;
+
+    std::string image_file_name = image_path.RemoveExtension().BaseName().value();
+    bool is_vbmeta_partition =
+        base::StartsWith(image_file_name, "vbmeta", base::CompareCase::INSENSITIVE_ASCII);
+
+    android::base::unique_fd fd(open(image_path.value().c_str(), O_RDWR | O_CLOEXEC));
+    EXPECT_TRUE(fd > 0);
+
+    uint64_t vbmeta_offset = 0;  // for vbmeta.img
+    if (!is_vbmeta_partition) {
+        std::unique_ptr<AvbFooter> footer = GetAvbFooter(fd);
+        EXPECT_NE(nullptr, footer);
+        vbmeta_offset = footer->vbmeta_offset;
+    }
+
+    auto flags_offset = vbmeta_offset + offsetof(AvbVBMetaImageHeader, flags);
+    uint32_t flags_data = htobe32(flags);
+    EXPECT_EQ(flags_offset, lseek64(fd, flags_offset, SEEK_SET));
+    EXPECT_EQ(sizeof flags_data, write(fd, &flags_data, sizeof flags_data));
+}
+
+TEST_F(AvbUtilTest, AvbPartitionToDevicePatition) {
+    EXPECT_EQ("system", AvbPartitionToDevicePatition("system", "", ""));
+    EXPECT_EQ("system", AvbPartitionToDevicePatition("system", "", "_b"));
+
+    EXPECT_EQ("system_a", AvbPartitionToDevicePatition("system", "_a", ""));
+    EXPECT_EQ("system_a", AvbPartitionToDevicePatition("system", "_a", "_b"));
+
+    EXPECT_EQ("system_b", AvbPartitionToDevicePatition("system_other", "", "_b"));
+    EXPECT_EQ("system_b", AvbPartitionToDevicePatition("system_other", "_a", "_b"));
+}
+
+TEST_F(AvbUtilTest, DeriveAvbPartitionName) {
+    // The fstab_entry to test.
+    FstabEntry fstab_entry = {
+        .blk_device = "/dev/block/dm-1",  // a dm-linear device (logical)
+        .mount_point = "/system",
+        .fs_type = "ext4",
+        .logical_partition_name = "system",
+    };
+
+    // Logical partitions.
+    // non-A/B
+    fstab_entry.fs_mgr_flags.logical = true;
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_dont_care", "_dont_care"));
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_b"));
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "", ""));
+    // Active slot.
+    fstab_entry.fs_mgr_flags.slot_select = true;
+    fstab_entry.logical_partition_name = "system_a";
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_dont_care"));
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_b"));
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", ""));
+    EXPECT_EQ("system_a", DeriveAvbPartitionName(fstab_entry, "_wont_erase_a", "_dont_care"));
+    // The other slot.
+    fstab_entry.fs_mgr_flags.slot_select = false;
+    fstab_entry.fs_mgr_flags.slot_select_other = true;
+    fstab_entry.logical_partition_name = "system_b";
+    EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "_dont_care", "_b"));
+    EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "_a", "_b"));
+    EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "", "_b"));
+    EXPECT_EQ("system_b_other", DeriveAvbPartitionName(fstab_entry, "_dont_care", "_wont_erase_b"));
+
+    // Non-logical partitions.
+    // non-A/B.
+    fstab_entry.fs_mgr_flags.logical = false;
+    fstab_entry.fs_mgr_flags.slot_select = false;
+    fstab_entry.fs_mgr_flags.slot_select_other = false;
+    fstab_entry.blk_device = "/dev/block/by-name/system";
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_dont_care", "_dont_care"));
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_b"));
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "", ""));
+    // Active slot _a.
+    fstab_entry.fs_mgr_flags.slot_select = true;
+    fstab_entry.blk_device = "/dev/block/by-name/system_a";
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_dont_care"));
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_b"));
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", ""));
+    EXPECT_EQ("system_a", DeriveAvbPartitionName(fstab_entry, "_wont_erase_a", "_dont_care"));
+    // Inactive slot _b.
+    fstab_entry.fs_mgr_flags.slot_select = false;
+    fstab_entry.fs_mgr_flags.slot_select_other = true;
+    fstab_entry.blk_device = "/dev/block/by-name/system_b";
+    EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "dont_care", "_b"));
+    EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "_a", "_b"));
+    EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "", "_b"));
+    EXPECT_EQ("system_b_other", DeriveAvbPartitionName(fstab_entry, "dont_care", "_wont_erase_b"));
+}
+
+TEST_F(AvbUtilTest, GetFdTotalSize) {
+    // Generates a raw test.img via BaseFsAvbTest.
+    const size_t image_size = 5 * 1024 * 1024;
+    base::FilePath image_path = GenerateImage("test.img", image_size);
+
+    // Checks file size is as expected via base::GetFileSize().
+    int64_t file_size;
+    ASSERT_TRUE(base::GetFileSize(image_path, &file_size));
+    EXPECT_EQ(image_size, file_size);
+
+    // Checks file size is expected via libfs_avb internal utils.
+    auto fd = OpenUniqueReadFd(image_path);
+    EXPECT_EQ(image_size, GetTotalSize(fd));
+}
+
+TEST_F(AvbUtilTest, GetFdTotalSizeWithOffset) {
+    // Generates a raw test.img via BaseFsAvbTest.
+    const size_t image_size = 10 * 1024 * 1024;
+    base::FilePath image_path = GenerateImage("test.img", image_size);
+
+    // Checks file size is expected even with a non-zero offset at the beginning.
+    auto fd = OpenUniqueReadFd(image_path);
+    off_t initial_offset = 2019;
+    EXPECT_EQ(initial_offset, lseek(fd, initial_offset, SEEK_SET));
+    EXPECT_EQ(image_size, GetTotalSize(fd));            // checks that total size is still returned.
+    EXPECT_EQ(initial_offset, lseek(fd, 0, SEEK_CUR));  // checks original offset is restored.
+}
+
+TEST_F(AvbUtilTest, GetAvbFooter) {
+    // Generates a raw system.img
+    const size_t image_size = 10 * 1024 * 1024;
+    const size_t partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", image_size);
+    EXPECT_NE(0U, system_path.value().size());
+
+    // Checks image size is as expected.
+    int64_t file_size;
+    ASSERT_TRUE(base::GetFileSize(system_path, &file_size));
+    EXPECT_EQ(image_size, file_size);
+
+    // Appends AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", partition_size, "SHA512_RSA8192", 20,
+                 data_dir_.Append("testkey_rsa8192.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Checks partition size is as expected, after adding footer.
+    ASSERT_TRUE(base::GetFileSize(system_path, &file_size));
+    EXPECT_EQ(partition_size, file_size);
+
+    // Checks avb footer and avb vbmeta.
+    EXPECT_EQ(
+            "Footer version:           1.0\n"
+            "Image size:               15728640 bytes\n"
+            "Original image size:      10485760 bytes\n"
+            "VBMeta offset:            10661888\n"
+            "VBMeta size:              3648 bytes\n"
+            "--\n"
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     1088 bytes\n"
+            "Auxiliary Block:          2304 bytes\n"
+            "Algorithm:                SHA512_RSA8192\n"
+            "Rollback Index:           20\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    Hashtree descriptor:\n"
+            "      Version of dm-verity:  1\n"
+            "      Image Size:            10485760 bytes\n"
+            "      Tree Offset:           10485760\n"
+            "      Tree Size:             86016 bytes\n"
+            "      Data Block Size:       4096 bytes\n"
+            "      Hash Block Size:       4096 bytes\n"
+            "      FEC num roots:         2\n"
+            "      FEC offset:            10571776\n"
+            "      FEC size:              90112 bytes\n"
+            "      Hash Algorithm:        sha1\n"
+            "      Partition Name:        system\n"
+            "      Salt:                  d00df00d\n"
+            "      Root Digest:           a3d5dd307341393d85de356c384ff543ec1ed81b\n"
+            "      Flags:                 0\n",
+            InfoImage(system_path));
+
+    // Checks each field from GetAvbFooter(fd).
+    auto fd = OpenUniqueReadFd(system_path);
+    auto footer = GetAvbFooter(fd);
+    EXPECT_NE(nullptr, footer);
+    EXPECT_EQ(10485760, footer->original_image_size);
+    EXPECT_EQ(10661888, footer->vbmeta_offset);
+    EXPECT_EQ(3648, footer->vbmeta_size);
+}
+
+TEST_F(AvbUtilTest, GetAvbFooterErrorVerification) {
+    // Generates a raw system.img
+    const size_t image_size = 5 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", image_size);
+
+    // Checks each field from GetAvbFooter(fd).
+    auto fd = OpenUniqueReadFd(system_path);
+    auto footer = GetAvbFooter(fd);
+    EXPECT_EQ(nullptr, footer);
+}
+
+TEST_F(AvbUtilTest, GetAvbFooterInsufficientSize) {
+    // Generates a raw system.img
+    const size_t image_size = AVB_FOOTER_SIZE - 10;
+    base::FilePath system_path = GenerateImage("system.img", image_size);
+
+    // Checks each field from GetAvbFooter(fd).
+    auto fd = OpenUniqueReadFd(system_path);
+    auto footer = GetAvbFooter(fd);
+    EXPECT_EQ(nullptr, footer);
+}
+
+TEST_F(AvbUtilTest, GetAvbPropertyDescriptor_Basic) {
+    // Makes a vbmeta.img with some properties.
+    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+                        {}, /* include_descriptor_image_paths */
+                        {}, /* chain_partitions */
+                        "--prop foo:android "
+                        "--prop bar:treble "
+                        "--internal_release_string \"unit test\" ");
+    auto vbmeta = LoadVBMetaData("vbmeta.img");
+
+    // Puts the vbmeta into a vector, for GetAvbPropertyDescriptor to use.
+    std::vector<VBMetaData> vbmeta_images;
+    vbmeta_images.emplace_back(std::move(vbmeta));
+
+    EXPECT_EQ("android", GetAvbPropertyDescriptor("foo", vbmeta_images));
+    EXPECT_EQ("treble", GetAvbPropertyDescriptor("bar", vbmeta_images));
+    EXPECT_EQ("", GetAvbPropertyDescriptor("non-existent", vbmeta_images));
+}
+
+TEST_F(AvbUtilTest, GetAvbPropertyDescriptor_SecurityPatchLevel) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+                 data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--prop com.android.build.system.security_patch:2019-04-05 "
+                 "--internal_release_string \"unit test\"");
+
+    // Generates chain partition descriptors.
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+    // Makes a vbmeta.img including the 'system' chained descriptor.
+    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+                        {boot_path},                         /* include_descriptor_image_paths */
+                        {{"system", 3, rsa4096_public_key}}, /* chain_partitions */
+                        "--internal_release_string \"unit test\"");
+
+    auto vbmeta = LoadVBMetaData("vbmeta.img");
+    auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, "system-vbmeta.img");
+
+    // Puts the vbmeta into a vector, for GetAvbPropertyDescriptor to use.
+    std::vector<VBMetaData> vbmeta_images;
+    vbmeta_images.emplace_back(std::move(vbmeta));
+    vbmeta_images.emplace_back(std::move(system_vbmeta));
+
+    EXPECT_EQ("2019-04-05",
+              GetAvbPropertyDescriptor("com.android.build.system.security_patch", vbmeta_images));
+}
+
+TEST_F(AvbUtilTest, GetVBMetaHeader) {
+    // Generates a raw boot.img
+    const size_t image_size = 5 * 1024 * 1024;
+    const size_t partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", image_size);
+    // Appends AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", partition_size, "SHA256_RSA4096", 10,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+    // Extracts boot vbmeta from boot.img into boot-vbmeta.img.
+    base::FilePath boot_vbmeta = ExtractVBMetaImage(boot_path, "boot-vbmeta.img");
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     576 bytes\n"
+            "Auxiliary Block:          1216 bytes\n"
+            "Algorithm:                SHA256_RSA4096\n"
+            "Rollback Index:           10\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    Hash descriptor:\n"
+            "      Image Size:            5242880 bytes\n"
+            "      Hash Algorithm:        sha256\n"
+            "      Partition Name:        boot\n"
+            "      Salt:                  d00df00d\n"
+            "      Digest:                "
+            "222dd01e98284a1fcd7781f85d1392e43a530511a64eff96db197db90ebc4df1\n"
+            "      Flags:                 0\n",
+            InfoImage("boot-vbmeta.img"));
+
+    // Creates a VBMetaData with the content from boot-vbmeta.img.
+    std::string content;
+    EXPECT_TRUE(base::ReadFileToString(boot_vbmeta, &content));
+    VBMetaData vbmeta((uint8_t*)content.data(), content.size(), "boot-vbmeta");
+    EXPECT_EQ(content.size(), vbmeta.size());
+
+    // Checks each field returned from GetVBMetaHeader().
+    auto vbmeta_header = vbmeta.GetVBMetaHeader(false /* update_vbmeta_size */);
+    EXPECT_NE(nullptr, vbmeta_header);
+    EXPECT_EQ(576, vbmeta_header->authentication_data_block_size);
+    EXPECT_EQ(1216, vbmeta_header->auxiliary_data_block_size);
+    EXPECT_EQ(AVB_ALGORITHM_TYPE_SHA256_RSA4096, vbmeta_header->algorithm_type);
+    EXPECT_EQ(0, vbmeta_header->hash_offset);
+    EXPECT_EQ(32, vbmeta_header->hash_size);
+    EXPECT_EQ(32, vbmeta_header->signature_offset);
+    EXPECT_EQ(512, vbmeta_header->signature_size);
+    EXPECT_EQ(176, vbmeta_header->public_key_offset);
+    EXPECT_EQ(1032, vbmeta_header->public_key_size);
+    EXPECT_EQ(0, vbmeta_header->descriptors_offset);
+    EXPECT_EQ(176, vbmeta_header->descriptors_size);
+    EXPECT_EQ(10, vbmeta_header->rollback_index);
+    EXPECT_EQ(0, vbmeta_header->flags);
+    EXPECT_EQ("unit test", std::string((const char*)vbmeta_header->release_string));
+
+    // Appends some garbage to the end of the vbmeta buffer, checks it still can work.
+    std::string padding(2020, 'A');  // Generate a padding with length 2020.
+    std::string content_padding = content + padding;
+    VBMetaData vbmeta_padding((const uint8_t*)content_padding.data(), content_padding.size(),
+                              "boot");
+    EXPECT_EQ(content_padding.size(), vbmeta_padding.size());
+
+    // Checks each field still can be parsed properly, even with garbage padding.
+    vbmeta_header = vbmeta_padding.GetVBMetaHeader(false /* update_vbmeta_size */);
+    EXPECT_NE(nullptr, vbmeta_header);
+    EXPECT_EQ(576, vbmeta_header->authentication_data_block_size);
+    EXPECT_EQ(1216, vbmeta_header->auxiliary_data_block_size);
+    EXPECT_EQ(AVB_ALGORITHM_TYPE_SHA256_RSA4096, vbmeta_header->algorithm_type);
+    EXPECT_EQ(0, vbmeta_header->hash_offset);
+    EXPECT_EQ(32, vbmeta_header->hash_size);
+    EXPECT_EQ(32, vbmeta_header->signature_offset);
+    EXPECT_EQ(512, vbmeta_header->signature_size);
+    EXPECT_EQ(176, vbmeta_header->public_key_offset);
+    EXPECT_EQ(1032, vbmeta_header->public_key_size);
+    EXPECT_EQ(0, vbmeta_header->descriptors_offset);
+    EXPECT_EQ(176, vbmeta_header->descriptors_size);
+    EXPECT_EQ(10, vbmeta_header->rollback_index);
+    EXPECT_EQ(0, vbmeta_header->flags);
+    EXPECT_EQ("unit test", std::string((const char*)vbmeta_header->release_string));
+
+    // Checks vbmeta size is updated to the actual size without padding.
+    vbmeta_header = vbmeta_padding.GetVBMetaHeader(true /* update_vbmeta_size */);
+    EXPECT_EQ(content_padding.size() - padding.size(), vbmeta_padding.size());
+}
+
+TEST_F(AvbUtilTest, ValidatePublicKeyBlob) {
+    // Generates a raw key.bin
+    const size_t key_size = 2048;
+    base::FilePath key_path = GenerateImage("key.bin", key_size);
+
+    uint8_t key_data[key_size];
+    EXPECT_EQ(key_size, base::ReadFile(key_path, (char*)key_data, key_size));
+
+    std::string expected_key_blob;
+    EXPECT_TRUE(base::ReadFileToString(key_path, &expected_key_blob));
+    EXPECT_TRUE(ValidatePublicKeyBlob(key_data, key_size, expected_key_blob));
+
+    key_data[10] ^= 0x80;  // toggles a bit and expects a failure
+    EXPECT_FALSE(ValidatePublicKeyBlob(key_data, key_size, expected_key_blob));
+    key_data[10] ^= 0x80;  // toggles the bit again, should pass
+    EXPECT_TRUE(ValidatePublicKeyBlob(key_data, key_size, expected_key_blob));
+}
+
+TEST_F(AvbUtilTest, VerifyEmptyPublicKeyBlob) {
+    // Generates a raw key.bin
+    const size_t key_size = 2048;
+    base::FilePath key_path = GenerateImage("key.bin", key_size);
+
+    uint8_t key_data[key_size];
+    EXPECT_EQ(key_size, base::ReadFile(key_path, (char*)key_data, key_size));
+
+    std::string expected_key_blob = "";  // empty means no expectation, thus return true.
+    EXPECT_TRUE(ValidatePublicKeyBlob(key_data, key_size, expected_key_blob));
+}
+
+TEST_F(AvbUtilTest, ValidatePublicKeyBlob_MultipleAllowedKeys) {
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+    base::FilePath rsa8192_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa8192.pem"));
+
+    std::vector<std::string> allowed_key_paths;
+    allowed_key_paths.push_back(rsa2048_public_key.value());
+    allowed_key_paths.push_back(rsa4096_public_key.value());
+
+    std::string expected_key_blob_2048;
+    EXPECT_TRUE(base::ReadFileToString(rsa2048_public_key, &expected_key_blob_2048));
+    std::string expected_key_blob_4096;
+    EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));
+    std::string expected_key_blob_8192;
+    EXPECT_TRUE(base::ReadFileToString(rsa8192_public_key, &expected_key_blob_8192));
+
+    EXPECT_TRUE(ValidatePublicKeyBlob(expected_key_blob_2048, allowed_key_paths));
+    EXPECT_TRUE(ValidatePublicKeyBlob(expected_key_blob_4096, allowed_key_paths));
+
+    EXPECT_FALSE(ValidatePublicKeyBlob(expected_key_blob_8192, allowed_key_paths));
+    EXPECT_FALSE(ValidatePublicKeyBlob("invalid_content", allowed_key_paths));
+    EXPECT_FALSE(ValidatePublicKeyBlob("", allowed_key_paths));
+
+    allowed_key_paths.push_back(rsa8192_public_key.value());
+    EXPECT_TRUE(ValidatePublicKeyBlob(expected_key_blob_8192, allowed_key_paths));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaSignature) {
+    const size_t image_size = 10 * 1024 * 1024;
+    const size_t partition_size = 15 * 1024 * 1024;
+    auto signing_key = data_dir_.Append("testkey_rsa4096.pem");
+    auto vbmeta = GenerateImageAndExtractVBMetaData("system", image_size, partition_size,
+                                                    "hashtree", signing_key, "SHA256_RSA4096",
+                                                    10 /* rollback_index */);
+
+    auto expected_public_key_blob = ExtractPublicKeyAvbBlob(signing_key);
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+              VerifyVBMetaSignature(vbmeta, expected_public_key_blob,
+                                    nullptr /* out_public_key_data */));
+
+    // Converts the expected key into an 'unexpected' key.
+    expected_public_key_blob[10] ^= 0x80;
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+              VerifyVBMetaSignature(vbmeta, expected_public_key_blob,
+                                    nullptr /* out_public_key_data */));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaSignatureOutputPublicKeyData) {
+    const size_t image_size = 10 * 1024 * 1024;
+    const size_t partition_size = 15 * 1024 * 1024;
+    auto signing_key = data_dir_.Append("testkey_rsa4096.pem");
+    auto vbmeta = GenerateImageAndExtractVBMetaData("system", image_size, partition_size,
+                                                    "hashtree", signing_key, "SHA256_RSA4096",
+                                                    10 /* rollback_index */);
+    std::string out_public_key_data;
+    auto expected_public_key_blob = ExtractPublicKeyAvbBlob(signing_key);
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+              VerifyVBMetaSignature(vbmeta, expected_public_key_blob, &out_public_key_data));
+    EXPECT_EQ(out_public_key_data, expected_public_key_blob);
+
+    // Converts the expected key into an 'unexpected' key.
+    expected_public_key_blob[10] ^= 0x80;
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+              VerifyVBMetaSignature(vbmeta, expected_public_key_blob, &out_public_key_data));
+    EXPECT_NE(out_public_key_data, expected_public_key_blob);
+}
+
+bool AvbUtilTest::TestVBMetaModification(VBMetaVerifyResult expected_result,
+                                         const VBMetaData& vbmeta, size_t offset, size_t length) {
+    uint8_t* d = reinterpret_cast<uint8_t*>(vbmeta.data());
+    const int kNumCheckIntervals = 8;
+
+    // Tests |kNumCheckIntervals| modifications in the start, middle, and
+    // end of the given sub-array at offset with size.
+    for (int n = 0; n <= kNumCheckIntervals; n++) {
+        size_t o = std::min(length * n / kNumCheckIntervals, length - 1) + offset;
+        d[o] ^= 0x80;
+        VBMetaVerifyResult result = VerifyVBMetaSignature(vbmeta, "" /* expected_public_key_blob */,
+                                                          nullptr /* out_public_key_data */);
+        d[o] ^= 0x80;
+        if (result != expected_result) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaSignatureWithModification) {
+    const size_t image_size = 10 * 1024 * 1024;
+    const size_t partition_size = 15 * 1024 * 1024;
+    auto signing_key = data_dir_.Append("testkey_rsa4096.pem");
+    auto vbmeta = GenerateImageAndExtractVBMetaData("system", image_size, partition_size,
+                                                    "hashtree", signing_key, "SHA256_RSA4096",
+                                                    10 /* rollback_index */);
+
+    auto header = vbmeta.GetVBMetaHeader(true /* update_vbmeta_size */);
+    size_t header_block_offset = 0;
+    size_t authentication_block_offset = header_block_offset + sizeof(AvbVBMetaImageHeader);
+    size_t auxiliary_block_offset =
+            authentication_block_offset + header->authentication_data_block_size;
+
+    // Should detect modifications in the auxiliary data block.
+    EXPECT_TRUE(TestVBMetaModification(VBMetaVerifyResult::kErrorVerification, vbmeta,
+                                       auxiliary_block_offset, header->auxiliary_data_block_size));
+
+    // Sholud detect modifications in the hash part of authentication data block.
+    EXPECT_TRUE(TestVBMetaModification(VBMetaVerifyResult::kErrorVerification, vbmeta,
+                                       authentication_block_offset + header->hash_offset,
+                                       header->hash_size));
+
+    // Sholud detect modifications in the signature part of authentication data block.
+    EXPECT_TRUE(TestVBMetaModification(VBMetaVerifyResult::kErrorVerification, vbmeta,
+                                       authentication_block_offset + header->signature_offset,
+                                       header->signature_size));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaSignatureNotSigned) {
+    const size_t image_size = 10 * 1024 * 1024;
+    const size_t partition_size = 15 * 1024 * 1024;
+    auto vbmeta = GenerateImageAndExtractVBMetaData(
+            "system", image_size, partition_size, "hashtree", {} /* avb_signing_key */,
+            "" /* avb_algorithm */, 10 /* rollback_index */);
+
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+              VerifyVBMetaSignature(vbmeta, "" /* expected_public_key_blob */,
+                                    nullptr /* out_public_key_data */));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaSignatureInvalidVBMeta) {
+    const size_t buffer_size = 5 * 1024 * 1024;
+    std::vector<uint8_t> vbmeta_buffer(buffer_size);
+    for (size_t n = 0; n < buffer_size; n++) {
+        vbmeta_buffer[n] = uint8_t(n);
+    }
+
+    VBMetaData invalid_vbmeta((const uint8_t*)vbmeta_buffer.data(), vbmeta_buffer.size(),
+                              "invalid_vbmeta");
+    EXPECT_EQ(VBMetaVerifyResult::kError,
+              VerifyVBMetaSignature(invalid_vbmeta, "" /* expected_public_key_blob */,
+                                    nullptr /* out_public_Key_data */));
+}
+
+bool AvbUtilTest::CompareVBMeta(const base::FilePath& avb_image_path,
+                                const VBMetaData& expected_vbmeta) {
+    if (!base::PathExists(avb_image_path)) return false;
+
+    std::string image_file_name = avb_image_path.RemoveExtension().BaseName().value();
+
+    base::FilePath extracted_vbmeta_path;
+    if (base::StartsWith(image_file_name, "vbmeta", base::CompareCase::INSENSITIVE_ASCII)) {
+        extracted_vbmeta_path = avb_image_path;  // no need to extract if it's a vbmeta image.
+    } else {
+        extracted_vbmeta_path = ExtractVBMetaImage(avb_image_path, image_file_name + "-vbmeta.img");
+    }
+
+    // Gets file size of the vbmeta image.
+    int64_t extracted_vbmeta_size;
+    EXPECT_TRUE(base::GetFileSize(extracted_vbmeta_path, &extracted_vbmeta_size));
+
+    // Reads the vbmeta into a vector.
+    std::vector<uint8_t> extracted_vbmeta_content(extracted_vbmeta_size);
+    EXPECT_TRUE(base::ReadFile(extracted_vbmeta_path,
+                               reinterpret_cast<char*>(extracted_vbmeta_content.data()),
+                               extracted_vbmeta_size));
+
+    // Compares extracted_vbmeta_content with the expected_vbmeta.
+    EXPECT_EQ(expected_vbmeta.size(), extracted_vbmeta_size);
+    return memcmp(reinterpret_cast<void*>(extracted_vbmeta_content.data()),
+                  reinterpret_cast<void*>(expected_vbmeta.data()), extracted_vbmeta_size) == 0;
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaDataWithoutFooter) {
+    // Generates chain partition descriptors.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+    // Makes a vbmeta image includeing 'boot' and 'system' chained descriptors.
+    auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+                                           data_dir_.Append("testkey_rsa8192.pem"),
+                                           {}, /* include_descriptor_image_paths */
+                                           {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                                            {"system", 2, rsa4096_public_key}},
+                                           "--internal_release_string \"unit test\"");
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     1088 bytes\n"
+            "Auxiliary Block:          3840 bytes\n"
+            "Algorithm:                SHA256_RSA8192\n"
+            "Rollback Index:           0\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    Chain Partition descriptor:\n"
+            "      Partition Name:          boot\n"
+            "      Rollback Index Location: 1\n"
+            "      Public key (sha1):       cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
+            "    Chain Partition descriptor:\n"
+            "      Partition Name:          system\n"
+            "      Rollback Index Location: 2\n"
+            "      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\n",
+            InfoImage("vbmeta.img"));
+
+    android::base::unique_fd fd(open(vbmeta_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_TRUE(fd > 0);
+
+    VBMetaVerifyResult verify_result;
+    std::string out_public_key_data;
+    std::unique_ptr<VBMetaData> vbmeta = VerifyVBMetaData(
+            fd, "vbmeta", "" /*expected_public_key_blob */, &out_public_key_data, &verify_result);
+    EXPECT_TRUE(vbmeta != nullptr);
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
+
+    auto rsa8192_public_key_blob = ExtractPublicKeyAvbBlob(data_dir_.Append("testkey_rsa8192.pem"));
+    EXPECT_EQ(rsa8192_public_key_blob, out_public_key_data);
+
+    // Checkes the returned vbmeta content is the same as that extracted via avbtool.
+    vbmeta->GetVBMetaHeader(true /* update_vbmeta_size */);
+    EXPECT_TRUE(CompareVBMeta(vbmeta_path, *vbmeta));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaDataWithFooter) {
+    const size_t image_size = 10 * 1024 * 1024;
+    const size_t partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", image_size);
+
+    // Appends AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", partition_size, "SHA512_RSA8192", 20,
+                 data_dir_.Append("testkey_rsa8192.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    android::base::unique_fd fd(open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_TRUE(fd > 0);
+
+    VBMetaVerifyResult verify_result;
+    std::string out_public_key_data;
+    std::unique_ptr<VBMetaData> vbmeta = VerifyVBMetaData(
+            fd, "system", "" /*expected_public_key_blob */, &out_public_key_data, &verify_result);
+    EXPECT_TRUE(vbmeta != nullptr);
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
+
+    auto rsa8192_public_key_blob = ExtractPublicKeyAvbBlob(data_dir_.Append("testkey_rsa8192.pem"));
+    EXPECT_EQ(rsa8192_public_key_blob, out_public_key_data);
+
+    // Checkes the returned vbmeta content is the same as that extracted via avbtool.
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+}
+
+// Modifies a random bit for a file, in the range of [offset, offset + length - 1].
+// Length < 0 means only resets previous modification without introducing new modification.
+void AvbUtilTest::ModifyFile(const base::FilePath& file_path, size_t offset, ssize_t length) {
+    static int last_modified_location = -1;
+    static std::string last_file_path;
+
+    int64_t file_size;
+    ASSERT_TRUE(base::GetFileSize(file_path, &file_size));
+
+    std::vector<uint8_t> file_content(file_size);
+    ASSERT_TRUE(base::ReadFile(file_path, reinterpret_cast<char*>(file_content.data()), file_size));
+
+    // Resets previous modification for consecutive calls on the same file.
+    if (last_file_path == file_path.value()) {
+        file_content[last_modified_location] ^= 0x80;
+    }
+
+    // Introduces a new modification.
+    if (length > 0) {
+        int modify_location = base::RandInt(offset, offset + length - 1);
+        file_content[modify_location] ^= 0x80;
+        last_file_path = file_path.value();
+        last_modified_location = modify_location;
+    }
+
+    ASSERT_EQ(file_size, static_cast<const size_t>(base::WriteFile(
+                                 file_path, reinterpret_cast<const char*>(file_content.data()),
+                                 file_content.size())));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaDataError) {
+    const size_t image_size = 10 * 1024 * 1024;
+    const size_t partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", image_size);
+
+    // Appends AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", partition_size, "SHA512_RSA8192", 20,
+                 data_dir_.Append("testkey_rsa8192.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    android::base::unique_fd fd(open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_TRUE(fd > 0);
+
+    std::unique_ptr<AvbFooter> footer = GetAvbFooter(fd);
+    EXPECT_TRUE(footer != nullptr);
+
+    VBMetaVerifyResult verify_result;
+    std::string out_public_key_data;
+    std::unique_ptr<VBMetaData> vbmeta = VerifyVBMetaData(
+            fd, "system", "" /*expected_public_key_blob */, &out_public_key_data, &verify_result);
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
+
+    auto rsa8192_public_key_blob = ExtractPublicKeyAvbBlob(data_dir_.Append("testkey_rsa8192.pem"));
+    EXPECT_EQ(rsa8192_public_key_blob, out_public_key_data);
+
+    // Modifies hash and signature, checks there is verification error.
+    auto header = vbmeta->GetVBMetaHeader(true /* update_vbmeta_size */);
+    size_t header_block_offset = 0;
+    size_t authentication_block_offset = header_block_offset + sizeof(AvbVBMetaImageHeader);
+
+    // Modifies the hash.
+    ModifyFile(system_path,
+               footer->vbmeta_offset + authentication_block_offset + header->hash_offset,
+               header->hash_size);
+    android::base::unique_fd hash_modified_fd(
+            open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_TRUE(hash_modified_fd > 0);
+    // Should return ErrorVerification.
+    vbmeta = VerifyVBMetaData(hash_modified_fd, "system", "" /*expected_public_key_blob */,
+                              nullptr /* out_public_key_data */, &verify_result);
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
+
+    // Modifies the auxiliary data block.
+    size_t auxiliary_block_offset =
+            authentication_block_offset + header->authentication_data_block_size;
+    ModifyFile(system_path, footer->vbmeta_offset + auxiliary_block_offset,
+               header->auxiliary_data_block_size);
+    android::base::unique_fd aux_modified_fd(
+            open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_TRUE(aux_modified_fd > 0);
+    // Should return ErrorVerification.
+    vbmeta = VerifyVBMetaData(aux_modified_fd, "system", "" /*expected_public_key_blob */,
+                              nullptr /* out_public_key_data */, &verify_result);
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
+
+    // Resets previous modification by setting offset to -1, and checks the verification can pass.
+    ModifyFile(system_path, 0 /* offset */, -1 /* length */);
+    android::base::unique_fd ok_fd(open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_TRUE(ok_fd > 0);
+    // Should return ResultOK..
+    vbmeta = VerifyVBMetaData(ok_fd, "system", "" /*expected_public_key_blob */,
+                              nullptr /* out_public_key_data */, &verify_result);
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
+}
+
+TEST_F(AvbUtilTest, GetChainPartitionInfo) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+                 data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates chain partition descriptors.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+    // Makes a vbmeta_system.img including the 'system' chained descriptor.
+    GenerateVBMetaImage("vbmeta_system.img", "SHA256_RSA4096", 0,
+                        data_dir_.Append("testkey_rsa4096.pem"),
+                        {},                                  /* include_descriptor_image_paths */
+                        {{"system", 3, rsa4096_public_key}}, /* chain_partitions */
+                        "--internal_release_string \"unit test\"");
+
+    // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0, data_dir_.Append("testkey_rsa8192.pem"),
+                        {},                               /* include_descriptor_image_paths */
+                        {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                         {"vbmeta_system", 2, rsa4096_public_key}},
+                        "--internal_release_string \"unit test\"");
+
+    // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+    EXPECT_EQ("6f4bf815a651aa35ec7102a88b7906b91aef284bc5e20d0bf527c7d460da3266",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+    // Loads the key blobs for comparison.
+    std::string expected_key_blob_2048;
+    EXPECT_TRUE(base::ReadFileToString(rsa2048_public_key, &expected_key_blob_2048));
+    std::string expected_key_blob_4096;
+    EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));
+
+    // Checks chain descriptors in vbmeta.img
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     1088 bytes\n"
+            "Auxiliary Block:          3840 bytes\n"
+            "Algorithm:                SHA256_RSA8192\n"
+            "Rollback Index:           0\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    Chain Partition descriptor:\n"
+            "      Partition Name:          boot\n"
+            "      Rollback Index Location: 1\n"
+            "      Public key (sha1):       cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
+            "    Chain Partition descriptor:\n"
+            "      Partition Name:          vbmeta_system\n"
+            "      Rollback Index Location: 2\n"
+            "      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\n",
+            InfoImage("vbmeta.img"));
+
+    bool fatal_error = false;
+    auto chained_descriptors = GetChainPartitionInfo(LoadVBMetaData("vbmeta.img"), &fatal_error);
+    EXPECT_EQ(2, chained_descriptors.size());  // contains 'boot' and 'vbmeta_system'.
+    EXPECT_EQ(false, fatal_error);
+
+    EXPECT_EQ("boot", chained_descriptors[0].partition_name);
+    EXPECT_EQ(expected_key_blob_2048, chained_descriptors[0].public_key_blob);
+
+    EXPECT_EQ("vbmeta_system", chained_descriptors[1].partition_name);
+    EXPECT_EQ(expected_key_blob_4096, chained_descriptors[1].public_key_blob);
+
+    // Checks chain descriptors in vbmeta_system.img
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     576 bytes\n"
+            "Auxiliary Block:          2176 bytes\n"
+            "Algorithm:                SHA256_RSA4096\n"
+            "Rollback Index:           0\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    Chain Partition descriptor:\n"
+            "      Partition Name:          system\n"
+            "      Rollback Index Location: 3\n"
+            "      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\n",
+            InfoImage("vbmeta_system.img"));
+
+    chained_descriptors = GetChainPartitionInfo(LoadVBMetaData("vbmeta_system.img"), &fatal_error);
+    EXPECT_EQ(1, chained_descriptors.size());  // contains 'system' only.
+    EXPECT_EQ(false, fatal_error);
+    EXPECT_EQ("system", chained_descriptors[0].partition_name);
+    EXPECT_EQ(expected_key_blob_4096, chained_descriptors[0].public_key_blob);
+}
+
+TEST_F(AvbUtilTest, GetChainPartitionInfoNone) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA4096", 10,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA8192", 20,
+                 data_dir_.Append("testkey_rsa8192.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Makes a vbmeta.img including both 'boot' and 'system' descriptors.
+    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA2048", 0, data_dir_.Append("testkey_rsa2048.pem"),
+                        {boot_path, system_path}, /* include_descriptor_image_paths */
+                        {},                       /* chain_partitions */
+                        "--internal_release_string \"unit test\"");
+    EXPECT_EQ("a069cbfc30c816cddf3b53f1ad53b7ca5d61a3d93845eb596bbb1b40caa1c62f",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     320 bytes\n"
+            "Auxiliary Block:          960 bytes\n"
+            "Algorithm:                SHA256_RSA2048\n"
+            "Rollback Index:           0\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    Hash descriptor:\n"
+            "      Image Size:            5242880 bytes\n"
+            "      Hash Algorithm:        sha256\n"
+            "      Partition Name:        boot\n"
+            "      Salt:                  d00df00d\n"
+            "      Digest:                "
+            "222dd01e98284a1fcd7781f85d1392e43a530511a64eff96db197db90ebc4df1\n"
+            "      Flags:                 0\n"
+            "    Hashtree descriptor:\n"
+            "      Version of dm-verity:  1\n"
+            "      Image Size:            10485760 bytes\n"
+            "      Tree Offset:           10485760\n"
+            "      Tree Size:             86016 bytes\n"
+            "      Data Block Size:       4096 bytes\n"
+            "      Hash Block Size:       4096 bytes\n"
+            "      FEC num roots:         2\n"
+            "      FEC offset:            10571776\n"
+            "      FEC size:              90112 bytes\n"
+            "      Hash Algorithm:        sha1\n"
+            "      Partition Name:        system\n"
+            "      Salt:                  d00df00d\n"
+            "      Root Digest:           a3d5dd307341393d85de356c384ff543ec1ed81b\n"
+            "      Flags:                 0\n",
+            InfoImage("vbmeta.img"));
+
+    // Checks none of chain descriptors is found.
+    bool fatal_error = false;
+    auto chained_descriptors = GetChainPartitionInfo(LoadVBMetaData("vbmeta.img"), &fatal_error);
+    EXPECT_EQ(0, chained_descriptors.size());  // There is no chain descriptors.
+    EXPECT_EQ(false, fatal_error);
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPath) {
+    // Generates a raw system_other.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system_other.img", system_image_size);
+
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system_other", system_partition_size, "SHA512_RSA4096",
+                 20, data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    std::string expected_key_blob_4096 =
+            ExtractPublicKeyAvbBlob(data_dir_.Append("testkey_rsa4096.pem"));
+
+    bool verification_disabled;
+    VBMetaVerifyResult verify_result;
+    std::string out_public_key_data;
+    std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(
+            system_path.value(), "system_other", expected_key_blob_4096,
+            false /* allow_verification_error */, false /* rollback_protection */,
+            false /* is_chained_vbmeta */, &out_public_key_data, &verification_disabled,
+            &verify_result);
+
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
+    EXPECT_EQ(false, verification_disabled);
+    EXPECT_EQ(expected_key_blob_4096, out_public_key_data);
+
+    EXPECT_EQ(2112UL, vbmeta->size());
+    EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path());
+    EXPECT_EQ("system_other", vbmeta->partition());
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPathErrorVerification) {
+    // Generates a raw system_other.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system_other.img", system_image_size);
+
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system_other", system_partition_size, "SHA512_RSA4096",
+                 20, data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    std::string expected_key_blob_4096 =
+            ExtractPublicKeyAvbBlob(data_dir_.Append("testkey_rsa4096.pem"));
+
+    // Modifies the auxiliary data of system_other.img
+    auto fd = OpenUniqueReadFd(system_path);
+    auto system_footer = GetAvbFooter(fd);
+    auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, "system_other-vbmeta.img");
+    auto system_header = system_vbmeta.GetVBMetaHeader(true /* update_vbmeta_size */);
+    size_t header_block_offset = 0;
+    size_t authentication_block_offset = header_block_offset + sizeof(AvbVBMetaImageHeader);
+    size_t auxiliary_block_offset =
+        authentication_block_offset + system_header->authentication_data_block_size;
+
+    // Modifies the hash.
+    ModifyFile(
+        system_path,
+        (system_footer->vbmeta_offset + authentication_block_offset + system_header->hash_offset),
+        system_header->hash_size);
+
+    VBMetaVerifyResult verify_result;
+    // Not allow verification error.
+    std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(
+            system_path.value(), "system_other", expected_key_blob_4096,
+            false /* allow_verification_error */, false /* rollback_protection */,
+            false /* is_chained_vbmeta */, nullptr /* out_public_key_data */,
+            nullptr /* verification_disabled */, &verify_result);
+    EXPECT_EQ(nullptr, vbmeta);
+
+    // Allow verification error.
+    vbmeta = LoadAndVerifyVbmetaByPath(
+            system_path.value(), "system_other", expected_key_blob_4096,
+            true /* allow_verification_error */, false /* rollback_protection */,
+            false /* is_chained_vbmeta */, nullptr /* out_public_key_data */,
+            nullptr /* verification_disabled */, &verify_result);
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
+
+    EXPECT_EQ(2112UL, vbmeta->size());
+    EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path());
+    EXPECT_EQ("system_other", vbmeta->partition());
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+
+    // Modifies the auxiliary data block.
+    ModifyFile(system_path, system_footer->vbmeta_offset + auxiliary_block_offset,
+               system_header->auxiliary_data_block_size);
+
+    // Not allow verification error.
+    vbmeta = LoadAndVerifyVbmetaByPath(
+            system_path.value(), "system_other", expected_key_blob_4096,
+            false /* allow_verification_error */, false /* rollback_protection */,
+            false /* is_chained_vbmeta */, nullptr /* out_public_key_data */,
+            nullptr /* verification_disabled */, &verify_result);
+    EXPECT_EQ(nullptr, vbmeta);
+
+    // Allow verification error.
+    vbmeta = LoadAndVerifyVbmetaByPath(
+            system_path.value(), "system_other", expected_key_blob_4096,
+            true /* allow_verification_error */, false /* rollback_protection */,
+            false /* is_chained_vbmeta */, nullptr /* out_public_key_data */,
+            nullptr /* verification_disabled */, &verify_result);
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPathUnexpectedPublicKey) {
+    // Generates a raw system_other.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system_other.img", system_image_size);
+
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system_other", system_partition_size, "SHA512_RSA4096",
+                 20, data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    std::string unexpected_key_blob_2048 =
+            ExtractPublicKeyAvbBlob(data_dir_.Append("testkey_rsa2048.pem"));
+    std::string expected_key_blob_4096 =
+            ExtractPublicKeyAvbBlob(data_dir_.Append("testkey_rsa4096.pem"));
+
+    // Uses the correct expected public key.
+    VBMetaVerifyResult verify_result;
+    std::string out_public_key_data;
+    std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(
+            system_path.value(), "system_other", expected_key_blob_4096,
+            false /* allow_verification_error */, false /* rollback_protection */,
+            false /* is_chained_vbmeta */, &out_public_key_data,
+            nullptr /* verification_disabled */, &verify_result);
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_EQ(verify_result, VBMetaVerifyResult::kSuccess);
+    EXPECT_EQ(expected_key_blob_4096, out_public_key_data);
+    EXPECT_EQ(2112UL, vbmeta->size());
+    EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path());
+    EXPECT_EQ("system_other", vbmeta->partition());
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+
+    // Uses the wrong expected public key with allow_verification_error set to false.
+    vbmeta = LoadAndVerifyVbmetaByPath(
+            system_path.value(), "system_other", unexpected_key_blob_2048,
+            false /* allow_verification_error */, false /* rollback_protection */,
+            false /* is_chained_vbmeta */, &out_public_key_data,
+            nullptr /* verification_disabled */, &verify_result);
+    EXPECT_EQ(nullptr, vbmeta);
+    // Checks out_public_key_data is still loaded properly, if the error is due
+    // to an unexpected public key instead of vbmeta image verification error.
+    EXPECT_EQ(expected_key_blob_4096, out_public_key_data);
+
+    // Uses the wrong expected public key with allow_verification_error set to true.
+    vbmeta = LoadAndVerifyVbmetaByPath(
+            system_path.value(), "system_other", unexpected_key_blob_2048,
+            true /* allow_verification_error */, false /* rollback_protection */,
+            false /* is_chained_vbmeta */, &out_public_key_data,
+            nullptr /* verification_disabled */, &verify_result);
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_EQ(expected_key_blob_4096, out_public_key_data);
+    EXPECT_EQ(verify_result, VBMetaVerifyResult::kErrorVerification);
+    EXPECT_EQ(2112UL, vbmeta->size());
+    EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path());
+    EXPECT_EQ("system_other", vbmeta->partition());
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPathVerificationDisabled) {
+    // Generates a raw system_other.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system_other.img", system_image_size);
+
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system_other", system_partition_size, "SHA512_RSA4096",
+                 20, data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    base::FilePath rsa4096_public_key =
+        ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+    std::string expected_key_blob_4096;
+    EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));
+
+    // Sets disabled flag and expect the returned verification_disabled is true.
+    SetVBMetaFlags(system_path, AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
+    bool verification_disabled;
+    VBMetaVerifyResult verify_result;
+    std::string out_public_key_data;
+    std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(
+            system_path.value(), "system_other", expected_key_blob_4096,
+            true /* allow_verification_error */, false /* rollback_protection */,
+            false /* is_chained_vbmeta */, nullptr /* out_public_key_data */,
+            &verification_disabled, &verify_result);
+
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
+    EXPECT_EQ(true, verification_disabled);  // should be true.
+
+    EXPECT_EQ(2112UL, vbmeta->size());
+    EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path());
+    EXPECT_EQ("system_other", vbmeta->partition());
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+
+    // Since the vbmeta flags is modified, vbmeta will be nullptr
+    // if verification error isn't allowed.
+    vbmeta = LoadAndVerifyVbmetaByPath(
+            system_path.value(), "system_other", expected_key_blob_4096,
+            false /* allow_verification_error */, false /* rollback_protection */,
+            false /* is_chained_vbmeta */, nullptr /* out_public_key_data */,
+            &verification_disabled, &verify_result);
+    EXPECT_EQ(nullptr, vbmeta);
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartition) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+                 data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates chain partition descriptors.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+    // Makes a vbmeta_system.img including the 'system' chained descriptor.
+    auto vbmeta_system_path = GenerateVBMetaImage(
+            "vbmeta_system.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+            {},                                  /* include_descriptor_image_paths */
+            {{"system", 3, rsa4096_public_key}}, /* chain_partitions */
+            "--internal_release_string \"unit test\"");
+
+    // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+    auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+                                           data_dir_.Append("testkey_rsa8192.pem"),
+                                           {}, /* include_descriptor_image_paths */
+                                           {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                                            {"vbmeta_system", 2, rsa4096_public_key}},
+                                           "--internal_release_string \"unit test\"");
+
+    // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+    EXPECT_EQ("6f4bf815a651aa35ec7102a88b7906b91aef284bc5e20d0bf527c7d460da3266",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+    // Starts to test LoadAndVerifyVbmetaByPartition.
+    std::vector<VBMetaData> vbmeta_images;
+    auto vbmeta_image_path = [this](const std::string& partition_name) {
+        return test_dir_.Append(partition_name + ".img").value();
+    };
+
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+                  false /* is_chained_vbmeta*/, &vbmeta_images));
+
+    EXPECT_EQ(4UL, vbmeta_images.size());  // vbmeta, boot, vbmeta_system and system
+    // Binary comparison for each vbmeta image.
+    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+    EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+    EXPECT_TRUE(CompareVBMeta(vbmeta_system_path, vbmeta_images[2]));
+    EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[3]));
+
+    // Skip loading chained vbmeta images.
+    vbmeta_images.clear();
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+                  false /* load_chained_vbmeta */, true /* rollback_protection */,
+                  vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+    // Only vbmeta is loaded.
+    EXPECT_EQ(1UL, vbmeta_images.size());
+    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartitionWithSuffixes) {
+    // Tests the following chained partitions.
+    // vbmeta_a.img
+    // |--> boot_b.img (boot_other)
+    // |--> vbmeta_system_b.img (vbmeta_system_other)
+    //      |--> system_a.img
+
+    // Generates a raw boot_b.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot_b.img", boot_image_size);
+
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+                 data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system_a.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system_a.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates chain partition descriptors.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+    // Makes a vbmeta_system_b.img including the 'system' chained descriptor.
+    auto vbmeta_system_path = GenerateVBMetaImage(
+            "vbmeta_system_b.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+            {},                                  /* include_descriptor_image_paths */
+            {{"system", 3, rsa4096_public_key}}, /* chain_partitions */
+            "--internal_release_string \"unit test\"");
+
+    // Makes a vbmeta_a.img includeing 'boot_other' and 'vbmeta_system_other' chained descriptors.
+    auto vbmeta_path = GenerateVBMetaImage(
+            "vbmeta_a.img", "SHA256_RSA8192", 0, data_dir_.Append("testkey_rsa8192.pem"),
+            {},                                     /* include_descriptor_image_paths */
+            {{"boot_other", 1, rsa2048_public_key}, /* chain_partitions */
+             {"vbmeta_system_other", 2, rsa4096_public_key}},
+            "--internal_release_string \"unit test\"");
+
+    // Starts to test LoadAndVerifyVbmetaByPartition with ab_suffix and ab_other_suffix.
+    auto vbmeta_image_path = [this](const std::string& partition_name) {
+        return test_dir_.Append(partition_name + ".img").value();
+    };
+
+    std::vector<VBMetaData> vbmeta_images;
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "_a" /* ab_suffix */, "_b" /* other_suffix */,
+                  "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+                  false /* is_chained_vbmeta*/, &vbmeta_images));
+
+    EXPECT_EQ(4UL, vbmeta_images.size());  // vbmeta, boot_other, vbmeta_system_other and system
+    // Binary comparison for each vbmeta image.
+    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+    EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+    EXPECT_TRUE(CompareVBMeta(vbmeta_system_path, vbmeta_images[2]));
+    EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[3]));
+
+    // Skips loading chained vbmeta images.
+    vbmeta_images.clear();
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "_a" /* ab_suffix */, "_b" /* other_suffix */,
+                  "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+                  false /* load_chained_vbmeta */, true /* rollback_protection */,
+                  vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+    // Only vbmeta is loaded.
+    EXPECT_EQ(1UL, vbmeta_images.size());
+    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+
+    // Using an invalid suffix for 'other' slot, checks it returns error.
+    EXPECT_EQ(VBMetaVerifyResult::kError,
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "_a" /* ab_suffix */,
+                  "_invalid_suffix" /* other_suffix */, "" /* expected_public_key_blob*/,
+                  false /* allow_verification_error */, true /* load_chained_vbmeta */,
+                  true /* rollback_protection */, vbmeta_image_path, false /* is_chained_vbmeta*/,
+                  &vbmeta_images));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartitionErrorVerification) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+                 data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates chain partition descriptors.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+    // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+    auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+                                           data_dir_.Append("testkey_rsa8192.pem"),
+                                           {}, /* include_descriptor_image_paths */
+                                           {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                                            {"system", 2, rsa4096_public_key}},
+                                           "--internal_release_string \"unit test\"");
+
+    // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+    EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+    auto vbmeta = LoadVBMetaData("vbmeta.img");
+
+    // Modifies hash, checks there is error if allow_verification_error is false.
+    auto header = vbmeta.GetVBMetaHeader(true /* update_vbmeta_size */);
+    size_t header_block_offset = 0;
+    size_t authentication_block_offset = header_block_offset + sizeof(AvbVBMetaImageHeader);
+
+    // Modifies the hash.
+    ModifyFile(vbmeta_path, authentication_block_offset + header->hash_offset, header->hash_size);
+
+    // Starts to test LoadAndVerifyVbmetaByPartition.
+    std::vector<VBMetaData> vbmeta_images;
+    auto vbmeta_image_path = [this](const std::string& partition_name) {
+        return test_dir_.Append(partition_name + ".img").value();
+    };
+    EXPECT_EQ(VBMetaVerifyResult::kError,
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+                  false /* is_chained_vbmeta*/, &vbmeta_images));
+    // Stops to load vbmeta because the top-level vbmeta has verification error.
+    EXPECT_EQ(0UL, vbmeta_images.size());
+
+    // Tries again with verification error allowed.
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "", /* other_suffix */
+                  "" /* expected_public_key_blob*/, true /* allow_verification_error */,
+                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+                  false /* is_chained_vbmeta*/, &vbmeta_images));
+
+    EXPECT_EQ(3UL, vbmeta_images.size());  // vbmeta, boot, and system
+    // Binary comparison for each vbmeta image.
+    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+    EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+    EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[2]));
+
+    // Resets the modification of the hash.
+    ModifyFile(vbmeta_path, 0 /* offset */, -1 /* length */);
+
+    // Modifies the auxiliary data of system.img
+    auto fd = OpenUniqueReadFd(system_path);
+    auto system_footer = GetAvbFooter(fd);
+    auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, "system-vbmeta.img");
+    auto system_header = system_vbmeta.GetVBMetaHeader(true /* update_vbmeta_size */);
+    size_t auxiliary_block_offset =
+            authentication_block_offset + system_header->authentication_data_block_size;
+
+    // Modifies the auxiliary data block.
+    ModifyFile(system_path, system_footer->vbmeta_offset + auxiliary_block_offset,
+               system_header->auxiliary_data_block_size);
+    vbmeta_images.clear();
+    EXPECT_EQ(VBMetaVerifyResult::kError,
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+                  false /* is_chained_vbmeta*/, &vbmeta_images));
+    // 'vbmeta', 'boot' but no 'system', because of verification error.
+    EXPECT_EQ(2UL, vbmeta_images.size());
+    // Binary comparison for the loaded 'vbmeta' and 'boot'.
+    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+    EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+
+    // Resets the modification of the auxiliary data.
+    ModifyFile(system_path, 0 /* offset */, -1 /* length */);
+
+    // Sets the vbmeta header flags on a chained partition, which introduces an error.
+    ModifyFile(system_path, system_footer->vbmeta_offset + offsetof(AvbVBMetaImageHeader, flags),
+               sizeof(uint32_t));
+    EXPECT_EQ(VBMetaVerifyResult::kError,
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  "" /* expected_public_key_blob*/, true /* allow_verification_error */,
+                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+                  false /* is_chained_vbmeta*/, &vbmeta_images));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartitionVerificationDisabled) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+                 data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates chain partition descriptors.
+    base::FilePath rsa2048_public_key =
+        ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+        ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+    // Makes a vbmeta_system.img including the 'system' chained descriptor.
+    auto vbmeta_system_path = GenerateVBMetaImage(
+        "vbmeta_system.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+        {},                                  /* include_descriptor_image_paths */
+        {{"system", 3, rsa4096_public_key}}, /* chain_partitions */
+        "--internal_release_string \"unit test\"");
+
+    // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+    auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+                                           data_dir_.Append("testkey_rsa8192.pem"),
+                                           {}, /* include_descriptor_image_paths */
+                                           {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                                            {"vbmeta_system", 2, rsa4096_public_key}},
+                                           "--internal_release_string \"unit test\"");
+
+    // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+    EXPECT_EQ("6f4bf815a651aa35ec7102a88b7906b91aef284bc5e20d0bf527c7d460da3266",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+    // Starts to test LoadAndVerifyVbmetaByPartition.
+    std::vector<VBMetaData> vbmeta_images;
+    auto vbmeta_image_path = [this](const std::string& partition_name) {
+        return test_dir_.Append(partition_name + ".img").value();
+    };
+
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+                  false /* is_chained_vbmeta*/, &vbmeta_images));
+
+    EXPECT_EQ(4UL, vbmeta_images.size());  // vbmeta, boot, vbmeta_system and system
+    // Binary comparison for each vbmeta image.
+    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+    EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+    EXPECT_TRUE(CompareVBMeta(vbmeta_system_path, vbmeta_images[2]));
+    EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[3]));
+
+    // Sets VERIFICATION_DISABLED to the top-level vbmeta.img
+    SetVBMetaFlags(vbmeta_path, AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
+    vbmeta_images.clear();
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  "" /* expected_public_key_blob*/, true /* allow_verification_error */,
+                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+                  false /* is_chained_vbmeta*/, &vbmeta_images));
+    EXPECT_EQ(1UL, vbmeta_images.size());  // Only vbmeta is loaded
+    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+
+    // HASHTREE_DISABLED still loads the chained vbmeta.
+    SetVBMetaFlags(vbmeta_path, AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+    vbmeta_images.clear();
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  "" /* expected_public_key_blob*/, true /* allow_verification_error */,
+                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+                  false /* is_chained_vbmeta*/, &vbmeta_images));
+    EXPECT_EQ(4UL, vbmeta_images.size());  // vbmeta, boot, vbmeta_system and system
+    // Binary comparison for each vbmeta image.
+    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+    EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+    EXPECT_TRUE(CompareVBMeta(vbmeta_system_path, vbmeta_images[2]));
+    EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[3]));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartitionUnexpectedPublicKey) {
+    // Generates chain partition descriptors.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+    base::FilePath rsa8192_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa8192.pem"));
+
+    // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+    auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+                                           data_dir_.Append("testkey_rsa8192.pem"),
+                                           {}, /* include_descriptor_image_paths */
+                                           {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                                            {"system", 2, rsa4096_public_key}},
+                                           "--internal_release_string \"unit test\"");
+    std::string expected_key_blob_4096;
+    EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));
+    std::string expected_key_blob_8192;
+    EXPECT_TRUE(base::ReadFileToString(rsa8192_public_key, &expected_key_blob_8192));
+
+    auto vbmeta_image_path = [this](const std::string& partition_name) {
+        return test_dir_.Append(partition_name + ".img").value();
+    };
+    std::vector<VBMetaData> vbmeta_images;
+    // Uses the correct expected public key.
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  expected_key_blob_8192, true /* allow_verification_error */,
+                  false /* load_chained_vbmeta */, true /* rollback_protection */,
+                  vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+
+    // Uses the wrong expected public key with allow_verification_error set to true.
+    vbmeta_images.clear();
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  expected_key_blob_4096, true /* allow_verification_error */,
+                  false /* load_chained_vbmeta */, true /* rollback_protection */,
+                  vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+
+    // Uses the wrong expected public key with allow_verification_error set to false.
+    vbmeta_images.clear();
+    EXPECT_EQ(VBMetaVerifyResult::kError,
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  expected_key_blob_4096, false /* allow_verification_error */,
+                  false /* load_chained_vbmeta */, true /* rollback_protection */,
+                  vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+}
+
+}  // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/tests/basic_test.cpp b/fs_mgr/libfs_avb/tests/basic_test.cpp
new file mode 100644
index 0000000..5a1cd0d
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/basic_test.cpp
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "fs_avb_test_util.h"
+
+#include <stdlib.h>
+
+#include <android-base/file.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+
+namespace fs_avb_host_test {
+
+TEST_F(BaseFsAvbTest, GenerateImage) {
+    const size_t image_size = 5 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", image_size);
+    EXPECT_NE(0U, boot_path.value().size());
+
+    // Checks file size is as expected.
+    int64_t file_size;
+    ASSERT_TRUE(base::GetFileSize(boot_path, &file_size));
+    EXPECT_EQ(file_size, image_size);
+
+    // Checks file content is as expected.
+    std::vector<uint8_t> expected_content;
+    expected_content.resize(image_size);
+    for (size_t n = 0; n < image_size; n++) {
+        expected_content[n] = uint8_t(n);
+    }
+    std::vector<uint8_t> actual_content;
+    actual_content.resize(image_size);
+    EXPECT_TRUE(
+            base::ReadFile(boot_path, reinterpret_cast<char*>(actual_content.data()), image_size));
+    EXPECT_EQ(expected_content, actual_content);
+}
+
+TEST_F(BaseFsAvbTest, GenerateVBMetaImage) {
+    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA2048", 0, data_dir_.Append("testkey_rsa2048.pem"),
+                        {}, /* include_descriptor_image_paths */
+                        {}, /* chain_partitions */
+                        "--internal_release_string \"unit test\"");
+    EXPECT_EQ("5eba9ad4e775645e7eac441a563c200681ae868158d06f6a6cd36d06c07bd781",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     320 bytes\n"
+            "Auxiliary Block:          576 bytes\n"
+            "Algorithm:                SHA256_RSA2048\n"
+            "Rollback Index:           0\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    (none)\n",
+            InfoImage("vbmeta.img"));
+}
+
+TEST_F(BaseFsAvbTest, AddHashFooter) {
+    // Generates a raw boot.img
+    const size_t image_size = 5 * 1024 * 1024;
+    const size_t partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", image_size);
+    EXPECT_NE(0U, boot_path.value().size());
+    // Checks file size is as expected.
+    int64_t file_size;
+    ASSERT_TRUE(base::GetFileSize(boot_path, &file_size));
+    EXPECT_EQ(file_size, image_size);
+    // Appends AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", partition_size, "SHA256_RSA4096", 10,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+    // Extracts boot vbmeta from boot.img into boot-vbmeta.img.
+    ExtractVBMetaImage(boot_path, "boot-vbmeta.img");
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     576 bytes\n"
+            "Auxiliary Block:          1216 bytes\n"
+            "Algorithm:                SHA256_RSA4096\n"
+            "Rollback Index:           10\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    Hash descriptor:\n"
+            "      Image Size:            5242880 bytes\n"
+            "      Hash Algorithm:        sha256\n"
+            "      Partition Name:        boot\n"
+            "      Salt:                  d00df00d\n"
+            "      Digest:                "
+            "222dd01e98284a1fcd7781f85d1392e43a530511a64eff96db197db90ebc4df1\n"
+            "      Flags:                 0\n",
+            InfoImage("boot-vbmeta.img"));
+}
+
+TEST_F(BaseFsAvbTest, AddHashtreeFooter) {
+    // Generates a raw system.img
+    const size_t image_size = 50 * 1024 * 1024;
+    const size_t partition_size = 60 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", image_size);
+    EXPECT_NE(0U, system_path.value().size());
+    // Checks file size is as expected.
+    int64_t file_size;
+    ASSERT_TRUE(base::GetFileSize(system_path, &file_size));
+    EXPECT_EQ(file_size, image_size);
+    // Appends AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", partition_size, "SHA512_RSA8192", 20,
+                 data_dir_.Append("testkey_rsa8192.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+    // Extracts system vbmeta from system.img into system-vbmeta.img.
+    ExtractVBMetaImage(system_path, "system-vbmeta.img");
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     1088 bytes\n"
+            "Auxiliary Block:          2304 bytes\n"
+            "Algorithm:                SHA512_RSA8192\n"
+            "Rollback Index:           20\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    Hashtree descriptor:\n"
+            "      Version of dm-verity:  1\n"
+            "      Image Size:            52428800 bytes\n"
+            "      Tree Offset:           52428800\n"
+            "      Tree Size:             413696 bytes\n"
+            "      Data Block Size:       4096 bytes\n"
+            "      Hash Block Size:       4096 bytes\n"
+            "      FEC num roots:         2\n"
+            "      FEC offset:            52842496\n"
+            "      FEC size:              417792 bytes\n"
+            "      Hash Algorithm:        sha1\n"
+            "      Partition Name:        system\n"
+            "      Salt:                  d00df00d\n"
+            "      Root Digest:           d20d40c02298e385ab6d398a61a3b91dc9947d99\n"
+            "      Flags:                 0\n",
+            InfoImage("system-vbmeta.img"));
+}
+
+TEST_F(BaseFsAvbTest, GenerateVBMetaImageWithDescriptors) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA4096", 10,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA8192", 20,
+                 data_dir_.Append("testkey_rsa8192.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Makes a vbmeta.img including both 'boot' and 'system' descriptors.
+    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA2048", 0, data_dir_.Append("testkey_rsa2048.pem"),
+                        {boot_path, system_path}, /* include_descriptor_image_paths */
+                        {},                       /* chain_partitions */
+                        "--internal_release_string \"unit test\"");
+    EXPECT_EQ("a069cbfc30c816cddf3b53f1ad53b7ca5d61a3d93845eb596bbb1b40caa1c62f",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     320 bytes\n"
+            "Auxiliary Block:          960 bytes\n"
+            "Algorithm:                SHA256_RSA2048\n"
+            "Rollback Index:           0\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    Hash descriptor:\n"
+            "      Image Size:            5242880 bytes\n"
+            "      Hash Algorithm:        sha256\n"
+            "      Partition Name:        boot\n"
+            "      Salt:                  d00df00d\n"
+            "      Digest:                "
+            "222dd01e98284a1fcd7781f85d1392e43a530511a64eff96db197db90ebc4df1\n"
+            "      Flags:                 0\n"
+            "    Hashtree descriptor:\n"
+            "      Version of dm-verity:  1\n"
+            "      Image Size:            10485760 bytes\n"
+            "      Tree Offset:           10485760\n"
+            "      Tree Size:             86016 bytes\n"
+            "      Data Block Size:       4096 bytes\n"
+            "      Hash Block Size:       4096 bytes\n"
+            "      FEC num roots:         2\n"
+            "      FEC offset:            10571776\n"
+            "      FEC size:              90112 bytes\n"
+            "      Hash Algorithm:        sha1\n"
+            "      Partition Name:        system\n"
+            "      Salt:                  d00df00d\n"
+            "      Root Digest:           a3d5dd307341393d85de356c384ff543ec1ed81b\n"
+            "      Flags:                 0\n",
+            InfoImage("vbmeta.img"));
+}
+
+TEST_F(BaseFsAvbTest, GenerateVBMetaImageWithChainDescriptors) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+                 data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Make a vbmeta image with chain partitions.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0, data_dir_.Append("testkey_rsa8192.pem"),
+                        {},                               /* include_descriptor_image_paths */
+                        {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                         {"system", 2, rsa4096_public_key}},
+                        "--internal_release_string \"unit test\"");
+
+    // vbmeta digest calculation includes the chained vbmeta from boot.img and system.img.
+    EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     1088 bytes\n"
+            "Auxiliary Block:          3840 bytes\n"
+            "Algorithm:                SHA256_RSA8192\n"
+            "Rollback Index:           0\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    Chain Partition descriptor:\n"
+            "      Partition Name:          boot\n"
+            "      Rollback Index Location: 1\n"
+            "      Public key (sha1):       cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
+            "    Chain Partition descriptor:\n"
+            "      Partition Name:          system\n"
+            "      Rollback Index Location: 2\n"
+            "      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\n",
+            InfoImage("vbmeta.img"));
+}
+
+}  // namespace fs_avb_host_test
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/fs_mgr/libfs_avb/tests/data/testkey_rsa2048.pem b/fs_mgr/libfs_avb/tests/data/testkey_rsa2048.pem
new file mode 100644
index 0000000..867dcff
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/data/testkey_rsa2048.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAxlVR3TIkouAOvH79vaJTgFhpfvVKQIeVkFRZPVXK/zY0Gvrh
+4JAqGjJoW/PfrQv5sdD36qtHH3a+G5hLZ6Ni+t/mtfjucxZfuLGC3kmJ1T3XqEKZ
+gXXI2IR7vVSoImREvDQGEDyJwtHzLANlkbGg0cghVhWZSCAndO8BenalC2v94/rt
+DfkPekH6dgU3Sf40T0sBSeSY94mOzTaqOR2pfV1rWlLRdWmo33zeHBv52Rlbt0dM
+uXAureXWiHztkm5GCBC1dgM+CaxNtizNEgC91KcD0xuRCCM2WxH+r1lpszyIJDct
+YbrFmVEYl/kjQpafhy7Nsk1fqSTyRdriZSYmTQIDAQABAoIBAQC+kJgaCuX8wYAn
+SXWQ0fmdZlXnMNRpcF0a0pD0SAzGb1RdYBXMaXiqtyhiwc53PPxsCDdNecjayIMd
+jJVXPTwLhTruOgMS/bp3gcgWwV34UHV4LJXGOGAE+jbS0hbDBMiudOYmj6RmVshp
+z9G1zZCSQNMXHaWsEYkX59XpzzoB384nRul2QgEtwzUNR9XlpzgtJBLk3SACkvsN
+mQ/DW8IWHXLg8vLn1LzVJ2e3B16H4MoE2TCHxqfMgr03IDRRJogkenQuQsFhevYT
+o/mJyHSWavVgzMHG9I5m+eepF4Wyhj1Y4WyKAuMI+9dHAX/h7Lt8XFCQCh5DbkVG
+zGr34sWBAoGBAOs7n7YZqNaaguovfIdRRsxxZr1yJAyDsr6w3yGImDZYju4c4WY9
+5esO2kP3FA4p0c7FhQF5oOb1rBuHEPp36cpL4aGeK87caqTfq63WZAujoTZpr9Lp
+BRbkL7w/xG7jpQ/clpA8sHzHGQs/nelxoOtC7E118FiRgvD/jdhlMyL9AoGBANfX
+vyoN1pplfT2xR8QOjSZ+Q35S/+SAtMuBnHx3l0qH2bbBjcvM1MNDWjnRDyaYhiRu
+i+KA7tqfib09+XpB3g5D6Ov7ls/Ldx0S/VcmVWtia2HK8y8iLGtokoBZKQ5AaFX2
+iQU8+tC4h69GnJYQKqNwgCUzh8+gHX5Y46oDiTmRAoGAYpOx8lX+czB8/Da6MNrW
+mIZNT8atZLEsDs2ANEVRxDSIcTCZJId7+m1W+nRoaycLTWNowZ1+2ErLvR10+AGY
+b7Ys79Wg9idYaY9yGn9lnZsMzAiuLeyIvXcSqgjvAKlVWrhOQFOughvNWvFl85Yy
+oWSCMlPiTLtt7CCsCKsgKuECgYBgdIp6GZsIfkgclKe0hqgvRoeU4TR3gcjJlM9A
+lBTo+pKhaBectplx9RxR8AnsPobbqwcaHnIfAuKDzjk5mEvKZjClnFXF4HAHbyAF
+nRzZEy9XkWFhc80T5rRpZO7C7qdxmu2aiKixM3V3L3/0U58qULEDbubHMw9bEhAT
+PudI8QKBgHEEiMm/hr9T41hbQi/LYanWnlFw1ue+osKuF8bXQuxnnHNuFT/c+9/A
+vWhgqG6bOEHu+p/IPrYm4tBMYlwsyh4nXCyGgDJLbLIfzKwKAWCtH9LwnyDVhOow
+GH9shdR+sW3Ew97xef02KAH4VlNANEmBV4sQNqWWvsYrcFm2rOdL
+-----END RSA PRIVATE KEY-----
diff --git a/fs_mgr/libfs_avb/tests/data/testkey_rsa4096.pem b/fs_mgr/libfs_avb/tests/data/testkey_rsa4096.pem
new file mode 100644
index 0000000..26db5c3
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/data/testkey_rsa4096.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEA2ASv49OEbH4NiT3CjNMSVeliyfEPXswWcqtEfCxlSpS1FisA
+uwbvEwdTTPlkuSh6G4SYiNhnpCP5p0vcSg/3OhiuVKgV/rCtrDXaO60nvK/o0y83
+NNZRK2xaJ9eWBq9ruIDK+jC0sYWzTaqqwxY0Grjnx/r5CXerl5PrRK7PILzwgBHb
+IwxHcblt1ntgR4cWVpO3wiqasEwBDDDYk4fw7W6LvjBb9qav3YB8RV6PkZNeRP64
+ggfuecq/MXNiWOPNxLzCER2hSr/+J32h9jWjXsrcVy8+8Mldhmr4r2an7c247aFf
+upuFGtUJrpROO8/LXMl5gPfMpkqoatjTMRH59gJjKhot0RpmGxZBvb33TcBK5SdJ
+X39Y4yct5clmDlI4Fjj7FutTP+b96aJeJVnYeUX/A0wmogBajsJRoRX5e/RcgZsY
+RzXYLQXprQ81dBWjjovMJ9p8XeT6BNMFC7o6sklFL0fHDUE/l4BNP8G1u3Bfpzev
+SCISRS71D4eS4oQB+RIPFBUkzomZ7rnEF3BwFeq+xmwfYrP0LRaH+1YeRauuMuRe
+ke1TZl697a3mEjkNg8noa2wtpe7EWmaujJfXDWxJx/XEkjGLCe4z2qk3tkkY+A5g
+Rcgzke8gVxC+eC2DJtbKYfkv4L8FMFJaEhwAp13MfC7FlYujO/BDLl7dANsCAwEA
+AQKCAgAWoL8P/WsktjuSwb5sY/vKtgzcHH1Ar942GsysuTXPDy686LpF3R8T/jNy
+n7k2UBAia8xSoWCR6BbRuHeV5oA+PLGeOpE7QaSfonB+yc+cy0x3Or3ssfqEsu/q
+toGHp75/8DXS6WE0K04x94u1rdC9b9sPrrGBlWCLGzqM0kbuJfyHXdd3n2SofAUO
+b5QRSgxD+2tHUpEroHqHnWJCaf4J0QegX45yktlfOYNK/PHLDQXV8ly/ejc32M4Y
+Tv7hUtOOJTuq8VCg9OWZm2Zo1QuM9XEJTPCp5l3+o5vzO6yhk2gotDvD32CdA+3k
+tLJRP54M1Sn+IXb1gGKN9rKAtGJbenWIPlNObhQgkbwG89Qd+5rfMXsiPv1Hl1tK
++tqwjD82/H3/ElaaMnwHCpeoGSp95OblAoBjzjMP2KsbvKSdL8O/rf1c3uOw9+DF
+cth0SA8y3ZzI11gJtb2QMGUrCny5n4sPGGbc3x38NdLhwbkPKZy60OiT4g2kNpdY
+dIitmAML2otttiF4AJM6AraPk8YVzkPLTksoL3azPBya5lIoDI2H3QvTtSvpXkXP
+yKchsDSWYbdqfplqC/X0Djp2/Zd8jpN5I6+1aSmpTmbwx/JTllY1N89FRZLIdxoh
+2k81LPiXhE6uRbjioJUlbnEWIpY2y2N2Clmxpjh0/IcXd1XImQKCAQEA7Zai+yjj
+8xit24aO9Tf3mZBXBjSaDodjC2KS1yCcAIXp6S7aH0wZipyZpQjys3zaBQyMRYFG
+bQqIfVAa6inWyDoofbAJHMu5BVcHFBPZvSS5YhDjc8XZ5dqSCxzIz9opIqAbm+b4
+aEV/3A3Jki5Dy8y/5j21GAK4Y4mqQOYzne7bDGi3Hyu041MGM4qfIcIkS5N1eHW4
+sDZJh6+K5tuxN5TX3nDZSpm9luNH8mLGgKAZ15b1LqXAtM5ycoBY9Hv082suPPom
+O+r0ybdRX6nDSH8+11y2KiP2kdVIUHCGkwlqgrux5YZyjCZPwOvEPhzSoOS+vBiF
+UVXA8idnxNLk1QKCAQEA6MIihDSXx+350fWqhQ/3Qc6gA/t2C15JwJ9+uFWA+gjd
+c/hn5HcmnmBJN4R04nLG/aU9SQur87a4mnC/Mp9JIARjHlZ/WNT4U0sJyPEVRg5U
+Z9VajAucWwi0JyJYCO1EMMy68Jp8qlTriK/L7nbD86JJ5ASxjojiN/0psK/Pk60F
+Rr+shKPi3jRQ1BDjDtAxOfo4ctf/nFbUM4bY0FNPQMP7WesoSKU0NBCRR6d0d2tq
+YflMjIQHx+N74P5jEdSCHTVGQm+dj47pUt3lLPLWc0bX1G/GekwXP4NUsR/70Hsi
+bwxkNnK2TSGzkt2rcOnutP125rJu6WpV7SNrq9rm7wKCAQAfMROcnbWviKHqnDPQ
+hdR/2K9UJTvEhInASOS2UZWpi+s1rez9BuSjigOx4wbaAZ4t44PW7C3uyt84dHfU
+HkIQb3I5bg8ENMrJpK9NN33ykwuzkDwMSwFcZ+Gci97hSubzoMl/IkeiiN1MapL4
+GhLUgsD+3UMVL+Y9SymK8637IgyoCGdiND6/SXsa8SwLJo3VTjqx4eKpX7cvlSBL
+RrRxc50TmwUsAhsd4CDl9YnSATLjVvJBeYlfM2tbFPaYwl1aR8v+PWkfnK0efm60
+fHki33HEnGteBPKuGq4vwVYpn6bYGwQz+f6335/A2DMfZHFSpjVURHPcRcHbCMla
+0cUxAoIBAQC25eYNkO478mo+bBbEXJlkoqLmvjAyGrNFo48F9lpVH6Y0vNuWkXJN
+PUgLUhAu6RYotjGENqG17rz8zt/PPY9Ok2P3sOx8t00y1mIn/hlDZXs55FM0fOMu
+PZaiscAPs7HDzvyOmDah+fzi+ZD8H2M3DS2W+YE0iaeJa2vZJS2t02W0BGXiDI33
+IZDqMyLYvwwPjOnShJydEzXID4xLl0tNjzLxo3GSNA7jYqlmbtV8CXIc7rMSL6WV
+ktIDKKJcnmpn3TcKeX6MEjaSIT82pNOS3fY3PmXuL+CMzfw8+u77Eecq78fHaTiL
+P5JGM93F6mzi19EY0tmInUBMCWtQLcENAoIBAQCg0KaOkb8T36qzPrtgbfou0E2D
+ufdpL1ugmD4edOFKQB5fDFQhLnSEVSJq3KUg4kWsXapQdsBd6kLdxS+K6MQrLBzr
+4tf0c7UCF1AzWk6wXMExZ8mRb2RkGZYQB2DdyhFB3TPmnq9CW8JCq+6kxg/wkU4s
+vM4JXzgcqVoSf42QJl+B9waeWhg0BTWx01lal4ds88HvEKmE0ik5GwiDbr7EvDDw
+E6UbZtQcIoSTIIZDgYqVFfR2DAho3wXJRsOXh433lEJ8X7cCDzrngFbQnlKrpwML
+Xgm0SIUc+Nf5poMM3rfLFK77t/ob4w+5PwRKcoSniyAxrHd6bwykYA8Vuydv
+-----END RSA PRIVATE KEY-----
diff --git a/fs_mgr/libfs_avb/tests/data/testkey_rsa8192.pem b/fs_mgr/libfs_avb/tests/data/testkey_rsa8192.pem
new file mode 100644
index 0000000..a383428
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/data/testkey_rsa8192.pem
@@ -0,0 +1,99 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIISKgIBAAKCBAEA0D3T+dISsmCHm797wsX0vVfqUWDJ/3mvDYozlCabDhnGLlSE
+pAQbf1Z8Ts+OM4pVRHOJUJL0WebNdmPPGjsyWQz6zZE96lQZL3avCEXqYVQR66V5
+3wdK/ohaMSRnGyEMBrqkVVbF3gCr+/irxD3YK+VowO2WKs/6GrMdqTA8Y5CTF/Je
+ptwsSg5MMjr6UaK4qDcrej3hkgBVGvRV3cj1snK6Br8HuYdFnpGGTS0d7UJlHFgl
+trGHU/CBO923hkHgJaWEjC0giSGjhKKtLzrVcpDV2y/lWQP9T/T4djEAIaHqQ++P
+SdOSR6psIGR6hVgSigt7HCnE7nW711/rfV5Ur9EiVpB040mDImKZcy8//TMnXydN
+1KYTVd/34fdpzMpSw5iblErbwOLXVTUmOztYnpl41feHSv/jPesHstPlfklIF2vo
+GZEohf9scQvcuM7wEBfC/aTA9K39zMmkBbcvSZjLyhmcSZWMPPOZyIcl3zY53QhW
+QC/abmIcBfI1S4+r7mC4i2Jn++oEvuGNVGr2SY2Z0ZZxXGL1HI/08D/3+Tcumrcn
+4YjPK/DMFi0F+e+1x41lipuf+cx/2qRNQX/m02STrLYdM6e0g33KvlnFdi2b752y
+/OIaMwxDaJvunMh6EMDWKM1AHbY/ioAoK7eS26HeJLEDllqO4+SWP37c8lMvSEWy
+1GiErR0HcsOj/QwWGPFseoVroMiA2sUQ0Ic/tgVjCTlXg+12XpUnouIweCi8KcL/
+ad2zJkju9hBhJLBQ/2GnivJi3lFgF4Gd//TSJ6rgWuXFfMKt/9z2Sz35ohEX4yA0
+flqlCeLInFEoevbz+XT9aRfDe65MZ79yw3TfP9CrV74hf1RRzveD4zpi3F+hcY2i
+JWsH7gROZeCm6fAX5Trecd3hOxJOfA4N4rvSSCq6BwCvebT8FY25Z/VF7cQrHYDS
+ij5w6lqhMzXHeUEY90Ga9AK4XzaWwGgezq+R7Zs00YSKqFv9qYNKdR7tz3cjijWf
+9q/3R1uh6EQKTMZKo4SEClJiGyjOBvmPK09jMFZTJv00hDxagDPZBl7XpLDJ5/Ln
+1uppvLCNWWY1zeJfaElMyq3/PqKZLidF9rVoA1SIwk2lpdUvPote2oFiwCZoXlwZ
+J2ncjmXgQNs76/8unDJA0rj4JPqccw4M5GxQ7okbgm3F4rmzriCuv8BeMSCkr2ry
+0mY3UhpohX4wCMq0G4x5sEUAz9FVVPZKjxnYBmLDzrJAR+4+G7gZsct01XDJYgDd
+JVYInFP22/cIre8VrFWYtHbgOFdNqUiVq58de6PdZG/E+uaWmEThSlRrgEjTxupi
+OXfgdKW/20j1qAtjOlqFwsY094Q5rqULQ6wPxQIDAQABAoIEAQChmkmlhrRBv42d
+fYUiyxK52b8ath0saJdDz6tlXmxYDgJxM9/XlORt9oTzeDknoEO5olu+rrx4BBgQ
+tzYiaiwRVXRREVTWQ7tjzRvaNL/GFkLt93XTccpuKwyrNE/bitLVagRbwcI+HZFa
+MknCOihHMHoRto8h3FKAY94xzSAgODMek1WG8jhgpCXXmVNnBPt+d4oDDIDAGAfz
+qgf03J5nhIb+80KgZOzPOKnbvJaL6EmlLHbgB3c42dzAw7hHtVmofYGWcvLb2MIY
+DVKO435/sQx1U/8NDH6JjVdACZjLgObXH9K3/Tt46DWPEcrPLmD8xhoc6gFM+Qr0
+AhkzKoBYDNk0CljbhdIBXjktXU6wRQFZ45uP2e4JZ4zrzGBLr/t4lTavZ0SQtLld
+A6kOsGh+dCWFDtnshxYnl/xad/yR+3a5zmDJbo/fJTBXrlf1B4rfQkFtK20etOPQ
+B++FC/rjh3Mm/Kb/p9Gz/2upZdArH97ZvD2LBFfj77lFmAhqAi3wCRlN+ekuYxaZ
+t1pBV9yXig8Dyldg1d7X8pOn2kyrF3rQUDDf4pa7x9vpnbkUlEUifoV9gnYsmdni
+qDzYBtTv2g6MKqwQySXaIUW0YOBPbOellWEwxJqGYQ7y4IfVHfM0iyHnehk2tZcr
++XazLnwGe+Bz4vcguFhJXLyIu//lAOhZtbk6r1QJEUuxaOOQX3wzyceE6nkDsgmr
+P5dj3Zpd7fS2VV2vyGHIFnBJ88LRxreVvgr6Q28UT27SB82zMb7mRZTVE2zeuubT
+5D2D1XbZ0wBo6WiK6eRRrDQ2Haeetkj/uoRy6PWXwnAaTmmIrrXwLqaoJh/U1e+D
+tfsDLWd6IxLjfXvGglrHsrtAz0oprpixUTeVhgTrGk9IQRd5rvxuGUYhFujVaYI6
++QUf+33AFdtncb8y9C9jZmgx8AKbJk+e73SLhB5JVos+WteU7b8d/Mim5mALjnO6
+Z1n/uimsT79sSDqy3XSymtKWXo/22UlrvGCpoEuELPMb6dSFWR7vwrsvhFngY4/K
+UnitnvxboEflQnaIQ4IfRLRzZsX+sC5Esqw9U5tHt4oI+91Dv3KbdbcERgV73K6B
+ZQgC4lkAQquFXiZ5AICkxjiMyZwTtU9KJ7xv17Xu6oywF/3AtbVGETW1D+3maHsD
+y3DASWojyqZdLj+WGzKQRa+swgCDAYKeek2fIAXFSdF63zxJ2RxOJ4GijSaoh+mr
+4HVvcpDaTj+A8T1+QdByM4s98gu4GD7kVtVQGBZdWjutyHvh0hWv1gtVmbhQ/413
+gDMFFDzHIjLTYGYes4hHL22169jVR9sZ1eQxwvTIg3N4pD5cFm0rRuZZTS+oJToF
+G27aBFihAoICAQDyVB62ZDnbxQthk+zITKIzRUrJbLoXrUcANcSHfaN7inF87Ova
+ze7ejT9DNSEhbtfZFJ1G6diOYoSw+2MzFXv0gEkLKY0dETydKgHEu6nVq5eivMgv
+D4hc9YkJMHDSlmv2FDkpL3AXCAmnW9rKp+ddttBZECnmlPEpHLoj6xgBw3pNa1Xs
+IcLVfdugH86Hexj6o0oKgYfcqrX8UUHtUI2/XQqgFrIj8ksjf1fFVWJRJFWmBXqp
+nMEsYarzATeM1kQ/kDeT1ZUpoGPQt02/XqXT4B5A3ATiEtpM2u+l48xtogWWg2Ry
+G9l938StAmhUiW1m7GnKE6EIFvQY85WvbzxOR0JYVUSr7MrasF6nnQlhYxFuIJoJ
+2h/KJQao5GCTvG4+GtbJJm4c2nyZgwyhizMsdgsdcls79aXiMkrZZkamLVUZWOtE
+3pA/oBuz2qnO9HwjbH1HGOccq0TXfmpFScEV3CQGYJdno6Fy7cbmupaL4U9agQ4e
+w+ygL18nq5HV++LStFnVrgs5YijjskfRdE9GUMVDh5pCsd9Y23Fymaad4O/2SRCC
+YkSsyH5OvyDOLpoyUJ6g6Q+45Hqm/3lG4YjNpzFUiMcnp7+3xU35qC0LK8xEfeei
+Ms1mTVEiHNIp6xH/TqRdX73WD7+YuKZSLIfRG7dgrirU6w+mhhvxD51uHQKCAgEA
+2/1mBCR5qm3/0Lt++RQbeyE3tiw40UeyQqucG/+VvY77sSLkI/Lx8iwRlywXcLBn
++A4TvgukmAdWzCs8ndgKNxPA+gfohvBsMOGN9KOB1Ug5vvg2J2kiI64vwYCwzhdZ
+NTUUmL+GMFHUqSsWYg6i7iBFcZmznr4W2T3bBxyTMZki7JStB86e35KXrzc2/W/b
++/p5U2HCSazDHI5mMyuClHc6GmUSVJ7f7LHjL94jviNqobp0Vj603tScHISmNrZw
+TBavkvZGYXsoWKvqavk7jBB9QzaBL+unaFRslg5jTaiKnISj44Us1fjFKu84xifL
+nJaEzjDPt7PBxko7LPgEY7wF39nM9VpoetI7bwR6NwDLSX8UU97MGd+HY+MO1Wi1
+pd2Lapwrx/EK7Oxz335VRK4Je0aZna4j2TyQdMJac9fsGPXv4ZsLfDLj/wD6l1j+
+lLLbBv3ImdSj32LBbhsgF4iCGeXO8HpPO+Q/h9XVsnY52Um2XdNMn03PCGm6ZvtM
+7DXiS+lPF90HjolJVHZTBNtdVRrLr53zLuWEfqT4FeKrDaxdtiXkxLjrB+5/VYu7
+ntyk01ZQ63VNfEwS1irmKl9+qZkTHk3HHV9jNV5RzWViwmJI7Wpr1YzBwmcKCB1O
+oGUADDs8QpnkCz0xkMVtYwHj9qKZlqfbHzrFDUUcF8kCggIAdYvUcgjf//ju8mA8
+5VQ3AcPE6TvycPW+kR2DvW12VcDsF/sc1UA7dHzziPhGn98SmNxlBjb8suSbFPZ8
+QhVT0WBBDkcTilwIGPx9ax7U3S6lGW2VdS6FqQH5fRmgQKZyrCVXLOEz8BgYBrSJ
+xu/3TQAWxH0QtibdbGHg8Pdi58gYlWFRhn9B8Slh1aRYHGPb1AhNLBd0/ddY+5G2
+9xSyDXdmZg1cUA+B3zAwNSqbzFxhp2zU+V1uXsbpk4KtnYV6CZM9QlrCRjTk9iNU
+dVXF/qaiRjfzrm4SsmEpCkEbsrp7F22Y1bkooORglMOsNAWNqfVXw4wN+syXj1ro
+6vZ8PERYrFyAOR1dsQMIhymnmTPjCpaJ4emKrhWTy20sY71thHakZWJc22YoNpbZ
+E6tgIVsJPTlxg/4+fyCCKj5wWr92nhsB1KBZPGO/zFhvMlJpvQ0tH8W2pbN2a0mI
+5x9FqALm/qjwCHfZItSwPM+ZozSht3cOkGHdcD5KXAXfcfsDJc4SHZKVIzq4NusN
+504R/jvD1GP8sglyG7omp75ckgzAmakLdxOP2HhQvIX9tcXpSirNJ6Sl2bwKuuMF
+wxo3r/o/9Y97e4LlfpEYp9eqMdcG+NpR993IwK0UhAWS9H5wdnWBSUHd5e4xtDUt
+iILNRuO46g7R/AIhz1cSSraWWQkCggIBAMhhPP5C9yt9PIm1b0eTwCBctnFSQIKo
+KsA9rll2ab+bMLk9jc8M6MLszy0CtWso09sHf4YY9tifvrkEHRethEh8zscwUuYu
+sm2n1fTixk0ul6LSVgl54uXbMJayENn4PIKRkew8cA8tSma43497w37hmD+MgCb1
+ALzqcco9hfmkgkI6fo1g8Ce3UEECKy2YKSmREdgYcK9JFQO61W6AkFWJcDxAmfzI
+JjFkKwsb7TSw79zWiEdSoM9jm7sCPKATd6Bm/ZAAkUUTuEFkfobn9Ax1rJN/Xxb2
+MKuAUtQv0NYY0gEVdG62jItuKLId6nncH8PG+rsRjPLIYpWqYdJpKx5pUnR+4AkQ
+S6CsRASwcF4PdBvDDBIFG6XpjFo4pPdQhDzL2sTF8b8SWSBLlJQbb7G6UNqgCSau
+SusCFpazvU5NfDmUMuctob2EYVaSXq9jGaj6bTUmDwXHwWilfIk9XfLxnYfXYrJ6
+xhdIpXGmHhuLQtAgK2O1JtLoPc9s9qP8/SkfP7xjjG6xHsP/WvL7QE1pPs9ZM/UI
+C01JNHFi9LKCn8o5mbZjN8jUowi7ffK+76wZUG1L7zM5ytWQOYwo0TQBfc8fpmFw
++RBRJX2kJyDO27ExczoGOKjwqEDaODIB9+9zcCK0BgSoRibSm4ZBvoxzWWD65Kls
+xdPhZUHcFGW5AoICAQC8iG27aD8aRUt94Oek66gFOJx84QVZehWPqtZjWyVenDuc
+T8dink8oejGjcK2UJuQDa83azv90ocVqE0n0ronYyszt9Ib1jlYC+CK1Ar9TYGFg
+WU5OWEDyCzCpqW/w/aG68U8qhKm0MvkLJR+G6evan9TwEhFEVAm3iWllNXs9x29s
+BucwyMMC23zsimxYlS7dA4DtyvVA+zL1omLpSWHbU/qtuI3HV1NeJzsy+gC4mwPh
+j52tdl669fyWLzHzBRLeq6dVOedjnCo+jlU3dL20DEk9SaW08D1CPuZekV1jVPMw
+JoaDcIRh4KLtQ0BYZ7UJeFUTsx1CS/+UqzqYSPOi57a5kvr0Y8YwRnSB8dHVFttX
+JTv83wTQXHPFSBgfnHNe7lsRTfIQfuIkr2bpiU7h85UQ7LsqcI6YHaC07URcsGFF
+FrLWGh91qzAd1diSHla2RnY3n8PPuMnCkguNhLUrYdmyMol7FfWFa9lwplsuTzBq
+B6yj8iaiE3LL+Q/eulJ7S6QPfAI2bU0UJO23Y4koeoIibEEDMSCQ6KYZ2NClRRRT
+ga5fS1YfkDFEcHUQ1/KIkdYHGBKBjoKGExzi8+CgiSySVSYDZl6wIOhLjH2OZ3ol
+ldPN7iNAHirrxg9v8QO6OQlpLUk5Lhp/1dSlZ6sy3UjFqvax3tw6ZjrL88YP5g==
+-----END RSA PRIVATE KEY-----
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp b/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp
new file mode 100644
index 0000000..c8605d7
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/properties.h>
+#include <fs_avb/fs_avb.h>
+#include <fs_avb/fs_avb_util.h>
+#include <fstab/fstab.h>
+#include <gtest/gtest.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+using android::fs_mgr::AvbHandle;
+using android::fs_mgr::AvbHandleStatus;
+using android::fs_mgr::Fstab;
+using android::fs_mgr::FstabEntry;
+using android::fs_mgr::VBMetaData;
+using android::fs_mgr::VBMetaVerifyResult;
+
+namespace fs_avb_device_test {
+
+// system vbmeta might not be at the end of /system when dynamic partition is
+// enabled. Therefore, disable it by default.
+TEST(FsAvbUtilTest, DISABLED_LoadAndVerifyVbmeta_SystemVbmeta) {
+    Fstab fstab;
+    EXPECT_TRUE(ReadDefaultFstab(&fstab));
+
+    FstabEntry* system_entry = GetEntryForMountPoint(&fstab, "/system");
+    EXPECT_NE(nullptr, system_entry);
+
+    std::string out_public_key_data;
+    std::string out_avb_partition_name;
+    VBMetaVerifyResult out_verify_result;
+    std::unique_ptr<VBMetaData> vbmeta =
+            LoadAndVerifyVbmeta(*system_entry, "" /* expected_public_key_blob */,
+                                &out_public_key_data, &out_avb_partition_name, &out_verify_result);
+
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess, out_verify_result);
+    EXPECT_EQ("system", out_avb_partition_name);
+    EXPECT_NE("", out_public_key_data);
+}
+
+TEST(FsAvbUtilTest, GetHashtreeDescriptor_SystemOther) {
+    // Non-A/B device doesn't have system_other partition.
+    if (fs_mgr_get_slot_suffix() == "") return;
+
+    // Skip running this test if system_other is a logical partition.
+    // Note that system_other is still a physical partition on "retrofit" devices.
+    if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) &&
+        !android::base::GetBoolProperty("ro.boot.dynamic_partitions_retrofit", false)) {
+        return;
+    }
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile("/system/etc/fstab.postinstall", &fstab));
+
+    // It should have two lines in the fstab, the first for logical system_other,
+    // the other for physical system_other.
+    EXPECT_EQ(2UL, fstab.size());
+
+    // Use the 2nd fstab entry, which is for physical system_other partition.
+    FstabEntry* system_other = &fstab[1];
+    EXPECT_NE(nullptr, system_other);
+
+    std::string out_public_key_data;
+    std::string out_avb_partition_name;
+    VBMetaVerifyResult out_verify_result;
+    std::unique_ptr<VBMetaData> system_other_vbmeta =
+            LoadAndVerifyVbmeta(*system_other, "" /* expected_public_key_blob */,
+                                &out_public_key_data, &out_avb_partition_name, &out_verify_result);
+
+    EXPECT_NE(nullptr, system_other_vbmeta);
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess, out_verify_result);
+    EXPECT_EQ("system_other", out_avb_partition_name);
+    EXPECT_NE("", out_public_key_data);
+
+    auto hashtree_desc =
+            GetHashtreeDescriptor(out_avb_partition_name, std::move(*system_other_vbmeta));
+    EXPECT_NE(nullptr, hashtree_desc);
+}
+
+TEST(AvbHandleTest, LoadAndVerifyVbmeta_SystemOther) {
+    // Non-A/B device doesn't have system_other partition.
+    if (fs_mgr_get_slot_suffix() == "") return;
+
+    // Skip running this test if system_other is a logical partition.
+    // Note that system_other is still a physical partition on "retrofit" devices.
+    if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) &&
+        !android::base::GetBoolProperty("ro.boot.dynamic_partitions_retrofit", false)) {
+        return;
+    }
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile("/system/etc/fstab.postinstall", &fstab));
+
+    // It should have two lines in the fstab, the first for logical system_other,
+    // the other for physical system_other.
+    EXPECT_EQ(2UL, fstab.size());
+
+    // Use the 2nd fstab entry, which is for physical system_other partition.
+    FstabEntry* system_other_entry = &fstab[1];
+    // Assign the default key if it's not specified in the fstab.
+    if (system_other_entry->avb_keys.empty()) {
+        system_other_entry->avb_keys = "/system/etc/security/avb/system_other.avbpubkey";
+    }
+    auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(*system_other_entry);
+    EXPECT_NE(nullptr, avb_handle) << "Failed to load system_other vbmeta. Try 'adb root'?";
+    EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+}
+
+TEST(AvbHandleTest, GetSecurityPatchLevel) {
+    Fstab fstab;
+    EXPECT_TRUE(ReadDefaultFstab(&fstab));
+
+    auto avb_handle = AvbHandle::LoadAndVerifyVbmeta();
+    EXPECT_NE(nullptr, avb_handle) << "Failed to load inline vbmeta. Try 'adb root'?";
+    EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+
+    // Gets security patch level with format: YYYY-MM-DD (e.g., 2019-04-05).
+    FstabEntry* system_entry = GetEntryForMountPoint(&fstab, "/system");
+    EXPECT_NE(nullptr, system_entry);
+    EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*system_entry).length());
+
+    FstabEntry* vendor_entry = GetEntryForMountPoint(&fstab, "/vendor");
+    EXPECT_NE(nullptr, vendor_entry);
+    EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*vendor_entry).length());
+
+    FstabEntry* product_entry = GetEntryForMountPoint(&fstab, "/product");
+    EXPECT_NE(nullptr, product_entry);
+    EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*product_entry).length());
+}
+
+}  // namespace fs_avb_device_test
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_test.cpp b/fs_mgr/libfs_avb/tests/fs_avb_test.cpp
new file mode 100644
index 0000000..2c819a9
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/fs_avb_test.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <endian.h>
+#include <stdlib.h>
+
+#include <android-base/file.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+#include <fs_avb/fs_avb.h>
+#include <libavb/libavb.h>
+
+#include "fs_avb_test_util.h"
+
+// Target classes or functions to test:
+using android::fs_mgr::AvbHandle;
+using android::fs_mgr::AvbHandleStatus;
+using android::fs_mgr::HashAlgorithm;
+
+namespace fs_avb_host_test {
+
+class PublicFsAvbTest : public BaseFsAvbTest {
+  public:
+    PublicFsAvbTest(){};
+
+  protected:
+    ~PublicFsAvbTest(){};
+    // Modifies |flags| field in the vbmeta header in an Avb image.
+    // e.g., AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED.
+    void ModifyVBMetaHeaderFlags(const base::FilePath& vbmeta_image_path, uint32_t flags);
+};
+
+void PublicFsAvbTest::ModifyVBMetaHeaderFlags(const base::FilePath& vbmeta_image_path,
+                                              uint32_t flags) {
+    if (!base::PathExists(vbmeta_image_path)) return;
+
+    // Only support modifying the flags in vbmeta*.img.
+    std::string image_file_name = vbmeta_image_path.RemoveExtension().BaseName().value();
+    ASSERT_TRUE(base::StartsWith(image_file_name, "vbmeta", base::CompareCase::INSENSITIVE_ASCII));
+
+    android::base::unique_fd fd(open(vbmeta_image_path.value().c_str(), O_RDWR | O_CLOEXEC));
+    EXPECT_TRUE(fd > 0);
+
+    auto flags_offset = offsetof(AvbVBMetaImageHeader, flags);
+    uint32_t flags_data = htobe32(flags);
+    EXPECT_EQ(flags_offset, lseek64(fd, flags_offset, SEEK_SET));
+    EXPECT_EQ(sizeof flags_data, write(fd, &flags_data, sizeof flags_data));
+}
+
+TEST_F(PublicFsAvbTest, LoadAndVerifyVbmeta) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+                 data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates chain partition descriptors.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+    // Makes a vbmeta image includeing 'boot' and 'system' chained descriptors.
+    auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+                                           data_dir_.Append("testkey_rsa8192.pem"),
+                                           {}, /* include_descriptor_image_paths */
+                                           {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                                            {"system", 2, rsa4096_public_key}},
+                                           "--internal_release_string \"unit test\"");
+
+    // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+    EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+    // Invokes the public API from fs_avb.h.
+    auto vbmeta_image_path = [this](const std::string& partition_name) {
+        return test_dir_.Append(partition_name + ".img").value();
+    };
+    auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+            "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+            "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+            false /* allow_verification_error */, true /* load_chained_vbmeta */,
+            true /* rollback_protection */, vbmeta_image_path);
+    EXPECT_NE(nullptr, avb_handle);
+    EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+
+    // Checks the summary info for all vbmeta images.
+    // Checks the digest matches the value calculated by CalcVBMetaDigest().
+    EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
+              avb_handle->vbmeta_info().digest);
+    EXPECT_EQ(8576UL, avb_handle->vbmeta_info().total_size);
+    EXPECT_EQ(HashAlgorithm::kSHA256, avb_handle->vbmeta_info().hash_algorithm);
+
+    // Skip loading chained vbmeta.
+    avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+            "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+            "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+            false /* allow_verification_error */, false /* load_chained_vbmeta */,
+            true /* rollback_protection */, vbmeta_image_path);
+    EXPECT_NE(nullptr, avb_handle);
+    EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+    EXPECT_EQ("5c31197992b3c72a854ec7dc0eb9609ffebcffab7917ffd381a99ecee328f09c",
+              avb_handle->vbmeta_info().digest);
+    EXPECT_EQ(5184UL, avb_handle->vbmeta_info().total_size);
+    EXPECT_EQ(HashAlgorithm::kSHA256, avb_handle->vbmeta_info().hash_algorithm);
+}
+
+TEST_F(PublicFsAvbTest, LoadAndVerifyVbmetaWithModifications) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+                 data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates chain partition descriptors.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+    // Makes a vbmeta image includeing 'boot' and 'system' chained descriptors.
+    auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+                                           data_dir_.Append("testkey_rsa8192.pem"),
+                                           {}, /* include_descriptor_image_paths */
+                                           {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                                            {"system", 2, rsa4096_public_key}},
+                                           "--internal_release_string \"unit test\"");
+
+    // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+    EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+    // Sets AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED in the vbmeta.img.
+    ModifyVBMetaHeaderFlags(vbmeta_path, AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+
+    auto vbmeta_image_path = [this](const std::string& partition_name) {
+        return test_dir_.Append(partition_name + ".img").value();
+    };
+    auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+            "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+            "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+            false /* allow_verification_error */, true /* load_chained_vbmeta */,
+            true /* rollback_protection */, vbmeta_image_path);
+    // Returns a null handler because allow_verification is not True.
+    EXPECT_EQ(nullptr, avb_handle);
+
+    // Try again with allow_verification_error set to true.
+    avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+            "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+            "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+            true /* allow_verification_error */, true /* load_chained_vbmeta */,
+            true /* rollback_protection */, vbmeta_image_path);
+    EXPECT_NE(nullptr, avb_handle);
+    EXPECT_EQ(AvbHandleStatus::kHashtreeDisabled, avb_handle->status());
+
+    // Checks the summary info for all vbmeta images.
+    // Checks the digest matches the value calculated by CalcVBMetaDigest().
+    EXPECT_EQ("ae8f7ad95cbb7ce4f0feeeedc2a0a39824af5cd29dad4d028597cab4b8c2e83c",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+    EXPECT_EQ("ae8f7ad95cbb7ce4f0feeeedc2a0a39824af5cd29dad4d028597cab4b8c2e83c",
+              avb_handle->vbmeta_info().digest);
+    EXPECT_EQ(8576UL, avb_handle->vbmeta_info().total_size);
+    EXPECT_EQ(HashAlgorithm::kSHA256, avb_handle->vbmeta_info().hash_algorithm);
+
+    // Sets AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED in the vbmeta.img.
+    ModifyVBMetaHeaderFlags(vbmeta_path, AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
+    // Loads the vbmeta with allow_verification_error set to true.
+    avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+            "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+            "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+            true /* allow_verification_error */, true /* load_chained_vbmeta */,
+            true /* rollback_protection */, vbmeta_image_path);
+    EXPECT_NE(nullptr, avb_handle);
+    EXPECT_EQ(AvbHandleStatus::kVerificationDisabled, avb_handle->status());
+    // Only the top-level vbmeta.img is loaded, when VERIFICATION_DISABLED is set.
+    // However, CalcVBMetaDigest() reads all vbmeta structs to calculate the digest,
+    // including vbmeta.img, boot.img and syste.img. So we don't compare the digest here.
+    EXPECT_EQ(5184UL, avb_handle->vbmeta_info().total_size);
+
+    // Sets a unknown flag in the vbmeta.imgm and expects to get
+    // AvbHandleStatus::kVerificationError.
+    ModifyVBMetaHeaderFlags(vbmeta_path, 0x10000000);
+    // Loads the vbmeta with allow_verification_error set to true.
+    avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+            "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+            "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+            true /* allow_verification_error */, true /* load_chained_vbmeta */,
+            true /* rollback_protection */, vbmeta_image_path);
+    EXPECT_NE(nullptr, avb_handle);
+    EXPECT_EQ(AvbHandleStatus::kVerificationError, avb_handle->status());
+    // Checks the digest matches the value calculated by CalcVBMetaDigest().
+    EXPECT_EQ("8fb99c4f54500053c3582df5eaf04e9a533137398879188aad9968ec19a664f1",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+    EXPECT_EQ("8fb99c4f54500053c3582df5eaf04e9a533137398879188aad9968ec19a664f1",
+              avb_handle->vbmeta_info().digest);
+    EXPECT_EQ(8576UL, avb_handle->vbmeta_info().total_size);
+    EXPECT_EQ(HashAlgorithm::kSHA256, avb_handle->vbmeta_info().hash_algorithm);
+}
+
+TEST_F(PublicFsAvbTest, LoadAndVerifyVbmetaWithPublicKeys) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+                 data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates chain partition descriptors.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+    base::FilePath rsa8192_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa8192.pem"));
+
+    // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+    auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+                                           data_dir_.Append("testkey_rsa8192.pem"),
+                                           {}, /* include_descriptor_image_paths */
+                                           {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                                            {"system", 2, rsa4096_public_key}},
+                                           "--internal_release_string \"unit test\"");
+
+    auto vbmeta_image_path = [this](const std::string& partition_name) {
+        return test_dir_.Append(partition_name + ".img").value();
+    };
+    std::vector<VBMetaData> vbmeta_images;
+    // Uses the correct expected public key.
+    auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+            "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+            rsa8192_public_key.value(), HashAlgorithm::kSHA256, true /* allow_verification_error */,
+            true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path);
+    EXPECT_NE(nullptr, avb_handle);
+    EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+
+    // Uses a non-existed public key.
+    avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+            "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+            "/path/to/non-existed/key", HashAlgorithm::kSHA256, true /* allow_verification_error */,
+            true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path);
+    EXPECT_EQ(nullptr, avb_handle);
+
+    // Uses an incorrect public key, with allow_verification_error false.
+    avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+            "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+            rsa4096_public_key.value(), HashAlgorithm::kSHA256,
+            false /* allow_verification_error */, true /* load_chained_vbmeta */,
+            true /* rollback_protection */, vbmeta_image_path);
+    EXPECT_EQ(nullptr, avb_handle);
+
+    // Uses an incorrect public key, with allow_verification_error true.
+    avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+            "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+            rsa4096_public_key.value(), HashAlgorithm::kSHA256, true /* allow_verification_error */,
+            true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path);
+    EXPECT_NE(nullptr, avb_handle);
+    EXPECT_EQ(AvbHandleStatus::kVerificationError, avb_handle->status());
+}
+
+}  // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp b/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp
new file mode 100644
index 0000000..17f4c4e
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "fs_avb_test_util.h"
+
+#include <stdlib.h>
+
+#include <android-base/file.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+
+namespace fs_avb_host_test {
+
+// Need to match the data setting in Android.bp:
+//     data: ["tests/data/*"]
+base::FilePath BaseFsAvbTest::data_dir_ = base::FilePath("tests/data");
+
+void BaseFsAvbTest::SetUp() {
+    // Changes current directory to test executable directory so that relative path
+    // references to test dependencies don't rely on being manually run from
+    // the executable directory. With this, we can just open "./tests/data/testkey_rsa2048.pem"
+    // from the source.
+    base::SetCurrentDirectory(base::FilePath(android::base::GetExecutableDirectory()));
+
+    // Creates a temporary directory, e.g., /tmp/libfs_avb-tests.XXXXXX to stash images in.
+    base::FilePath tmp_dir;
+    ASSERT_TRUE(GetTempDir(&tmp_dir));
+    base::CreateTemporaryDirInDir(tmp_dir, "libfs_avb-tests.", &test_dir_);
+}
+
+void BaseFsAvbTest::TearDown() {
+    // Nukes temporary directory.
+    ASSERT_NE(std::string::npos, test_dir_.value().find("libfs_avb-tests"));
+    ASSERT_TRUE(base::DeleteFile(test_dir_, true /* recursive */));
+}
+
+std::string BaseFsAvbTest::CalcVBMetaDigest(const std::string& file_name,
+                                            const std::string& hash_algorithm) {
+    auto iter = vbmeta_images_.find(file_name);
+    EXPECT_NE(iter, vbmeta_images_.end());  // ensures file_name is generated before.
+
+    // Gets the image path from iterator->second.path: VBMetaImage.path.
+    base::FilePath image_path = iter->second.path;
+    base::FilePath vbmeta_digest_path = test_dir_.Append("vbmeta_digest");
+    EXPECT_COMMAND(0,
+                   "avbtool calculate_vbmeta_digest --image %s --hash_algorithm %s"
+                   " --output %s",
+                   image_path.value().c_str(), hash_algorithm.c_str(),
+                   vbmeta_digest_path.value().c_str());
+    // Reads the content of the output digest file.
+    std::string vbmeta_digest_data;
+    EXPECT_TRUE(base::ReadFileToString(vbmeta_digest_path, &vbmeta_digest_data));
+    // Returns the trimmed digest.
+    std::string trimmed_digest_data;
+    base::TrimString(vbmeta_digest_data, " \t\n", &trimmed_digest_data);
+    return trimmed_digest_data;
+}
+
+base::FilePath BaseFsAvbTest::GenerateVBMetaImage(
+        const std::string& file_name, const std::string& avb_algorithm, uint64_t rollback_index,
+        const base::FilePath& key_path,
+        const std::vector<base::FilePath>& include_descriptor_image_paths,
+        const std::vector<ChainPartitionConfig>& chain_partitions,
+        const std::string& additional_options) {
+    // --algorithm and --key
+    std::string signing_options;
+    if (avb_algorithm == "") {
+        signing_options = " --algorithm NONE ";
+    } else {
+        signing_options =
+                std::string(" --algorithm ") + avb_algorithm + " --key " + key_path.value() + " ";
+    }
+    // --include_descriptors_from_image
+    std::string include_descriptor_options;
+    for (const auto& path : include_descriptor_image_paths) {
+        include_descriptor_options += " --include_descriptors_from_image " + path.value();
+    }
+    // --chain_partitions
+    std::string chain_partition_options;
+    for (const auto& partition : chain_partitions) {
+        chain_partition_options += base::StringPrintf(
+                " --chain_partition %s:%u:%s", partition.partition_name.c_str(),
+                partition.rollback_index_location, partition.key_blob_path.value().c_str());
+    }
+    // Starts to 'make_vbmeta_image'.
+    VBMetaImage vbmeta_image;
+    vbmeta_image.path = test_dir_.Append(file_name);
+    EXPECT_COMMAND(0,
+                   "avbtool make_vbmeta_image"
+                   " --rollback_index %" PRIu64
+                   " %s %s %s %s"
+                   " --output %s",
+                   rollback_index, signing_options.c_str(), include_descriptor_options.c_str(),
+                   chain_partition_options.c_str(), additional_options.c_str(),
+                   vbmeta_image.path.value().c_str());
+    int64_t file_size;
+    EXPECT_TRUE(base::GetFileSize(vbmeta_image.path, &file_size));
+    vbmeta_image.content.resize(file_size);
+    EXPECT_TRUE(base::ReadFile(vbmeta_image.path,
+                               reinterpret_cast<char*>(vbmeta_image.content.data()), file_size));
+    // Stores the generated vbmeta image into vbmeta_images_ member object.
+    vbmeta_images_.emplace(file_name, std::move(vbmeta_image));
+
+    return vbmeta_images_[file_name].path;  // returns the path.
+}
+
+base::FilePath BaseFsAvbTest::ExtractVBMetaImage(const base::FilePath& image_path,
+                                                 const std::string& output_file_name,
+                                                 const size_t padding_size) {
+    VBMetaImage vbmeta_image;
+    vbmeta_image.path = test_dir_.Append(output_file_name);
+    EXPECT_COMMAND(0,
+                   "avbtool extract_vbmeta_image"
+                   " --image %s"
+                   " --output %s"
+                   " --padding_size %zu",
+                   image_path.value().c_str(), vbmeta_image.path.value().c_str(), padding_size);
+    int64_t file_size;
+    EXPECT_TRUE(base::GetFileSize(vbmeta_image.path, &file_size));
+    vbmeta_image.content.resize(file_size);
+    EXPECT_TRUE(base::ReadFile(vbmeta_image.path,
+                               reinterpret_cast<char*>(vbmeta_image.content.data()), file_size));
+    // Stores the extracted vbmeta image into vbmeta_images_ member object.
+    vbmeta_images_.emplace(output_file_name, std::move(vbmeta_image));
+
+    // Returns the output file path.
+    return vbmeta_images_[output_file_name].path;
+}
+
+// Generates a file with name |file_name| of size |image_size| with
+// known content (0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
+base::FilePath BaseFsAvbTest::GenerateImage(const std::string& file_name, size_t image_size,
+                                            uint8_t start_byte) {
+    std::vector<uint8_t> image;
+    image.resize(image_size);
+    for (size_t n = 0; n < image_size; n++) {
+        image[n] = uint8_t(n + start_byte);
+    }
+    base::FilePath image_path = test_dir_.Append(file_name);
+    EXPECT_EQ(image_size,
+              static_cast<const size_t>(base::WriteFile(
+                      image_path, reinterpret_cast<const char*>(image.data()), image.size())));
+    return image_path;
+}
+
+void BaseFsAvbTest::AddAvbFooter(const base::FilePath& image_path, const std::string& footer_type,
+                                 const std::string& partition_name, const uint64_t partition_size,
+                                 const std::string& avb_algorithm, uint64_t rollback_index,
+                                 const base::FilePath& key_path, const std::string& salt,
+                                 const std::string& additional_options) {
+    // 'add_hash_footer' or 'add_hashtree_footer'.
+    EXPECT_TRUE(footer_type == "hash" or footer_type == "hashtree");
+    std::string add_footer_option = "add_" + footer_type + "_footer";
+
+    std::string signing_options;
+    if (avb_algorithm == "") {
+        signing_options = " --algorithm NONE ";
+    } else {
+        signing_options =
+                std::string(" --algorithm ") + avb_algorithm + " --key " + key_path.value() + " ";
+    }
+    EXPECT_COMMAND(0,
+                   "avbtool %s"
+                   " --image %s"
+                   " --partition_name %s "
+                   " --partition_size %" PRIu64 " --rollback_index %" PRIu64
+                   " --salt %s"
+                   " %s %s",
+                   add_footer_option.c_str(), image_path.value().c_str(), partition_name.c_str(),
+                   partition_size, rollback_index, salt.c_str(), signing_options.c_str(),
+                   additional_options.c_str());
+}
+
+VBMetaData BaseFsAvbTest::GenerateImageAndExtractVBMetaData(
+        const std::string& partition_name, const size_t image_size, const size_t partition_size,
+        const std::string& footer_type, const base::FilePath& avb_signing_key,
+        const std::string& avb_algorithm, const uint64_t rollback_index) {
+    // Generates a raw image first
+    base::FilePath image_path = GenerateImage(partition_name + ".img", image_size);
+
+    // Appends AVB Hashtree Footer.
+    AddAvbFooter(image_path, footer_type, partition_name, partition_size, avb_algorithm,
+                 rollback_index, avb_signing_key, "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Extracts vbmeta from the ram image into another *-vbmeta.img.
+    auto vbmeta_image = ExtractVBMetaImage(image_path, partition_name + "-vbmeta.img");
+
+    // Loads *-vbmeta.img into a VBMetaData.
+    std::string vbmeta_buffer;
+    EXPECT_TRUE(base::ReadFileToString(vbmeta_image, &vbmeta_buffer));
+
+    return {(const uint8_t*)vbmeta_buffer.data(), vbmeta_buffer.size(), partition_name};
+}
+
+VBMetaData BaseFsAvbTest::LoadVBMetaData(const std::string& file_name) {
+    auto iter = vbmeta_images_.find(file_name);
+    EXPECT_NE(iter, vbmeta_images_.end());  // ensures file_name is generated before.
+
+    // Gets the image path from iterator->second.path: VBMetaImage.path.
+    base::FilePath image_path = iter->second.path;
+
+    // Loads the vbmeta_image into a VBMetaData.
+    std::string vbmeta_buffer;
+    EXPECT_TRUE(base::ReadFileToString(image_path, &vbmeta_buffer));
+
+    std::string partition_name = image_path.RemoveExtension().BaseName().value();
+    return {(const uint8_t*)vbmeta_buffer.data(), vbmeta_buffer.size(), partition_name};
+}
+
+VBMetaData BaseFsAvbTest::ExtractAndLoadVBMetaData(const base::FilePath& image_path,
+                                                   const std::string& output_file_name) {
+    ExtractVBMetaImage(image_path, output_file_name);
+    return LoadVBMetaData(output_file_name);
+}
+
+std::string BaseFsAvbTest::InfoImage(const base::FilePath& image_path) {
+    base::FilePath tmp_path = test_dir_.Append("info_output.txt");
+    EXPECT_COMMAND(0, "avbtool info_image --image %s --output %s", image_path.value().c_str(),
+                   tmp_path.value().c_str());
+    std::string info_data;
+    EXPECT_TRUE(base::ReadFileToString(tmp_path, &info_data));
+    return info_data;
+}
+
+std::string BaseFsAvbTest::InfoImage(const std::string& file_name) {
+    auto iter = vbmeta_images_.find(file_name);
+    EXPECT_NE(iter, vbmeta_images_.end());  // ensures file_name is generated before.
+    // Gets the image path from iterator->second.path: VBMetaImage.path.
+    base::FilePath image_path = iter->second.path;
+    return InfoImage(image_path);
+}
+
+base::FilePath BaseFsAvbTest::ExtractPublicKeyAvb(const base::FilePath& key_path) {
+    std::string file_name = key_path.RemoveExtension().BaseName().value();
+    base::FilePath tmp_path = test_dir_.Append(file_name + "public_key.bin");
+    EXPECT_COMMAND(0,
+                   "avbtool extract_public_key --key %s"
+                   " --output %s",
+                   key_path.value().c_str(), tmp_path.value().c_str());
+    return tmp_path;
+}
+
+std::string BaseFsAvbTest::ExtractPublicKeyAvbBlob(const base::FilePath& key_path) {
+    base::FilePath tmp_path = test_dir_.Append("public_key.bin");
+    EXPECT_COMMAND(0,
+                   "avbtool extract_public_key --key %s"
+                   " --output %s",
+                   key_path.value().c_str(), tmp_path.value().c_str());
+    std::string key_data;
+    EXPECT_TRUE(base::ReadFileToString(tmp_path, &key_data));
+    return key_data;
+}
+
+}  // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_test_util.h b/fs_mgr/libfs_avb/tests/fs_avb_test_util.h
new file mode 100644
index 0000000..ab1980b
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/fs_avb_test_util.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <fcntl.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <base/files/file_path.h>
+#include <base/strings/stringprintf.h>
+#include <fs_avb/types.h>
+#include <gtest/gtest.h>
+
+// Utility macro to run the command expressed by the printf()-style string
+// |command_format| using the system(3) utility function. Will assert unless
+// the command exits normally with exit status |expected_exit_status|.
+#define EXPECT_COMMAND(expected_exit_status, command_format, ...)                   \
+    do {                                                                            \
+        int rc = system(base::StringPrintf(command_format, ##__VA_ARGS__).c_str()); \
+        EXPECT_TRUE(WIFEXITED(rc));                                                 \
+        EXPECT_EQ(WEXITSTATUS(rc), expected_exit_status);                           \
+    } while (0);
+
+using android::fs_mgr::VBMetaData;
+
+namespace fs_avb_host_test {
+
+struct VBMetaImage {
+    // Path to vbmeta image generated with GenerateVBMetaImage().
+    base::FilePath path;
+    // Contents of the image generated with GenerateVBMetaImage().
+    std::vector<uint8_t> content;
+};
+
+struct ChainPartitionConfig {
+    std::string partition_name;
+    uint32_t rollback_index_location;
+    base::FilePath key_blob_path;
+};
+
+inline android::base::unique_fd OpenUniqueReadFd(const base::FilePath& file_path) {
+    return android::base::unique_fd(open(file_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+}
+
+/* Base-class used for unit test. */
+class BaseFsAvbTest : public ::testing::Test {
+  public:
+    BaseFsAvbTest() {}
+
+  protected:
+    virtual ~BaseFsAvbTest() {}
+
+    // Calculates the vbmeta digest using 'avbtool calc_vbmeta_digest' command.
+    // Note that the calculation includes chained vbmeta images.
+    std::string CalcVBMetaDigest(const std::string& file_name, const std::string& hash_algorithm);
+
+    // Generates a vbmeta image with |file_name| by avbtool.
+    // The generated vbmeta image will be written to disk, see the
+    // |vbmeta_images_| variable for its path and the content.
+    base::FilePath GenerateVBMetaImage(
+            const std::string& file_name, const std::string& avb_algorithm, uint64_t rollback_index,
+            const base::FilePath& key_path,
+            const std::vector<base::FilePath>& include_descriptor_image_paths,
+            const std::vector<ChainPartitionConfig>& chain_partitions,
+            const std::string& additional_options = "");
+    // Similar to above, but extracts a vbmeta image from the given image_path.
+    // The extracted vbmeta image will be written to disk, with |output_file_name|.
+    // See the |vbmeta_images_| variable for its path and the content.
+    base::FilePath ExtractVBMetaImage(const base::FilePath& image_path,
+                                      const std::string& output_file_name,
+                                      const size_t padding_size = 0);
+
+    // Generate a file with name |file_name| of size |image_size| with
+    // known content (0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
+    base::FilePath GenerateImage(const std::string& file_name, size_t image_size,
+                                 uint8_t start_byte = 0);
+    // Invokes 'avbtool add_hash_footer' or 'avbtool add_hashtree_footer' to sign
+    // the |image_path|. The |footer_type| can be either "hash" or "hashtree".
+    void AddAvbFooter(const base::FilePath& image_path, const std::string& footer_type,
+                      const std::string& partition_name, const uint64_t partition_size,
+                      const std::string& avb_algorithm, uint64_t rollback_index,
+                      const base::FilePath& avb_signing_key, const std::string& salt = "d00df00d",
+                      const std::string& additional_options = "");
+
+    VBMetaData GenerateImageAndExtractVBMetaData(
+            const std::string& partition_name, const size_t image_size, const size_t partition_size,
+            const std::string& footer_type, const base::FilePath& avb_signing_key,
+            const std::string& avb_algorithm, const uint64_t rollback_index);
+
+    VBMetaData ExtractAndLoadVBMetaData(const base::FilePath& image_path,
+                                        const std::string& output_file_name);
+
+    VBMetaData LoadVBMetaData(const std::string& file_name);
+
+    // Returns the output of 'avbtool info_image' for the |image_path|.
+    std::string InfoImage(const base::FilePath& image_path);
+    // Same as above, but for an internal vbmeta image with |file_name| in |vbmeta_images_|.
+    std::string InfoImage(const std::string& file_name);
+
+    // Extracts public key blob in AVB format for a .pem key, then returns the
+    // file path: a .bin file.
+    base::FilePath ExtractPublicKeyAvb(const base::FilePath& key_path);
+    // Same as above, but returns the key blob binary instead.
+    std::string ExtractPublicKeyAvbBlob(const base::FilePath& key_path);
+
+    void SetUp() override;
+    void TearDown() override;
+
+    // Temporary directory created in SetUp().
+    base::FilePath test_dir_;
+    // Maps vbmeta image name (e.g., vbmeta_a.img, system_a.img) to VBMetaImage.
+    std::map<std::string, VBMetaImage> vbmeta_images_;
+
+    static base::FilePath data_dir_;
+};
+
+}  // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_util_test.cpp b/fs_mgr/libfs_avb/tests/fs_avb_util_test.cpp
new file mode 100644
index 0000000..7c34009
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/fs_avb_util_test.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <fs_avb/fs_avb_util.h>
+
+#include "fs_avb_test_util.h"
+
+namespace fs_avb_host_test {
+
+class PublicFsAvbUtilTest : public BaseFsAvbTest {
+  public:
+    PublicFsAvbUtilTest(){};
+
+  protected:
+    ~PublicFsAvbUtilTest(){};
+};
+
+TEST_F(PublicFsAvbUtilTest, GetHashtreeDescriptor) {
+    // Generates a raw system_other.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, "system-vbmeta.img");
+
+    auto hashtree_desc =
+            GetHashtreeDescriptor("system" /* avb_partition_name */, std::move(system_vbmeta));
+    EXPECT_NE(nullptr, hashtree_desc);
+
+    // Checks the returned hashtree_desc matches the following info returned by avbtool.
+    EXPECT_EQ(
+            "Footer version:           1.0\n"
+            "Image size:               15728640 bytes\n"
+            "Original image size:      10485760 bytes\n"
+            "VBMeta offset:            10661888\n"
+            "VBMeta size:              2112 bytes\n"
+            "--\n"
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     576 bytes\n"
+            "Auxiliary Block:          1280 bytes\n"
+            "Algorithm:                SHA512_RSA4096\n"
+            "Rollback Index:           20\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    Hashtree descriptor:\n"
+            "      Version of dm-verity:  1\n"
+            "      Image Size:            10485760 bytes\n"
+            "      Tree Offset:           10485760\n"
+            "      Tree Size:             86016 bytes\n"
+            "      Data Block Size:       4096 bytes\n"
+            "      Hash Block Size:       4096 bytes\n"
+            "      FEC num roots:         2\n"
+            "      FEC offset:            10571776\n"
+            "      FEC size:              90112 bytes\n"
+            "      Hash Algorithm:        sha1\n"
+            "      Partition Name:        system\n"
+            "      Salt:                  d00df00d\n"
+            "      Root Digest:           a3d5dd307341393d85de356c384ff543ec1ed81b\n"
+            "      Flags:                 0\n",
+            InfoImage(system_path));
+
+    EXPECT_EQ(1UL, hashtree_desc->dm_verity_version);
+    EXPECT_EQ(10485760UL, hashtree_desc->image_size);
+    EXPECT_EQ(10485760UL, hashtree_desc->tree_offset);
+    EXPECT_EQ(86016UL, hashtree_desc->tree_size);
+    EXPECT_EQ(4096UL, hashtree_desc->data_block_size);
+    EXPECT_EQ(4096UL, hashtree_desc->hash_block_size);
+    EXPECT_EQ(2UL, hashtree_desc->fec_num_roots);
+    EXPECT_EQ(10571776UL, hashtree_desc->fec_offset);
+    EXPECT_EQ(90112UL, hashtree_desc->fec_size);
+    EXPECT_EQ(std::string("sha1"),
+              std::string(reinterpret_cast<const char*>(hashtree_desc->hash_algorithm)));
+    EXPECT_EQ(std::string("system").length(), hashtree_desc->partition_name_len);
+    EXPECT_EQ(hashtree_desc->partition_name, "system");
+    EXPECT_EQ(hashtree_desc->salt, "d00df00d");
+    EXPECT_EQ(hashtree_desc->root_digest, "a3d5dd307341393d85de356c384ff543ec1ed81b");
+
+    // Checks it's null if partition name doesn't match.
+    EXPECT_EQ(nullptr, GetHashtreeDescriptor("system_not_exist" /* avb_partition_name */,
+                                             std::move(system_vbmeta)));
+}
+
+TEST_F(PublicFsAvbUtilTest, GetHashtreeDescriptor_NotFound) {
+    // Generates a raw boot.img
+    const size_t image_size = 5 * 1024 * 1024;
+    const size_t partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", image_size);
+    // Appends AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", partition_size, "SHA256_RSA4096", 10,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+    // Extracts boot vbmeta from boot.img into boot-vbmeta.img.
+    auto boot_vbmeta = ExtractAndLoadVBMetaData(boot_path, "boot-vbmeta.img");
+
+    auto hashtree_desc =
+            GetHashtreeDescriptor("boot" /* avb_partition_name */, std::move(boot_vbmeta));
+    EXPECT_EQ(nullptr, hashtree_desc);
+}
+
+}  // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/tests/util_test.cpp b/fs_mgr/libfs_avb/tests/util_test.cpp
new file mode 100644
index 0000000..12b5acb
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/util_test.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <unistd.h>
+
+#include <future>
+#include <string>
+#include <thread>
+
+#include <base/files/file_util.h>
+
+#include "fs_avb_test_util.h"
+#include "util.h"
+
+// Target functions to test:
+using android::fs_mgr::BytesToHex;
+using android::fs_mgr::FileWaitMode;
+using android::fs_mgr::HexToBytes;
+using android::fs_mgr::NibbleValue;
+using android::fs_mgr::WaitForFile;
+
+namespace fs_avb_host_test {
+
+TEST(BasicUtilTest, NibbleValue09) {
+    uint8_t value;
+
+    EXPECT_TRUE(NibbleValue('0', &value));
+    EXPECT_EQ(0, value);
+    EXPECT_TRUE(NibbleValue('1', &value));
+    EXPECT_EQ(1, value);
+    EXPECT_TRUE(NibbleValue('2', &value));
+    EXPECT_EQ(2, value);
+    EXPECT_TRUE(NibbleValue('3', &value));
+    EXPECT_EQ(3, value);
+    EXPECT_TRUE(NibbleValue('4', &value));
+    EXPECT_EQ(4, value);
+    EXPECT_TRUE(NibbleValue('5', &value));
+    EXPECT_EQ(5, value);
+    EXPECT_TRUE(NibbleValue('6', &value));
+    EXPECT_EQ(6, value);
+    EXPECT_TRUE(NibbleValue('7', &value));
+    EXPECT_EQ(7, value);
+    EXPECT_TRUE(NibbleValue('8', &value));
+    EXPECT_EQ(8, value);
+    EXPECT_TRUE(NibbleValue('9', &value));
+    EXPECT_EQ(9, value);
+}
+
+TEST(BasicUtilTest, NibbleValueAF) {
+    uint8_t value;
+
+    EXPECT_TRUE(NibbleValue('a', &value));
+    EXPECT_EQ(10, value);
+    EXPECT_TRUE(NibbleValue('b', &value));
+    EXPECT_EQ(11, value);
+    EXPECT_TRUE(NibbleValue('c', &value));
+    EXPECT_EQ(12, value);
+    EXPECT_TRUE(NibbleValue('d', &value));
+    EXPECT_EQ(13, value);
+    EXPECT_TRUE(NibbleValue('e', &value));
+    EXPECT_EQ(14, value);
+    EXPECT_TRUE(NibbleValue('f', &value));
+    EXPECT_EQ(15, value);
+
+    EXPECT_TRUE(NibbleValue('A', &value));
+    EXPECT_EQ(10, value);
+    EXPECT_TRUE(NibbleValue('B', &value));
+    EXPECT_EQ(11, value);
+    EXPECT_TRUE(NibbleValue('C', &value));
+    EXPECT_EQ(12, value);
+    EXPECT_TRUE(NibbleValue('D', &value));
+    EXPECT_EQ(13, value);
+    EXPECT_TRUE(NibbleValue('E', &value));
+    EXPECT_EQ(14, value);
+    EXPECT_TRUE(NibbleValue('F', &value));
+    EXPECT_EQ(15, value);
+}
+
+TEST(BasicUtilTest, NibbleValueInvalid) {
+    uint8_t value;
+
+    EXPECT_FALSE(NibbleValue('G', &value));
+    EXPECT_FALSE(NibbleValue('H', &value));
+    EXPECT_FALSE(NibbleValue('I', &value));
+    EXPECT_FALSE(NibbleValue('x', &value));
+    EXPECT_FALSE(NibbleValue('y', &value));
+    EXPECT_FALSE(NibbleValue('z', &value));
+}
+
+TEST(BasicUtilTest, HexToBytes) {
+    std::string hex = "000102030405060708090A0B0C0D0E0F";
+    uint8_t bytes[16];
+
+    EXPECT_TRUE(HexToBytes((uint8_t*)bytes, sizeof(bytes), hex));
+    for (size_t i = 0; i < sizeof(bytes); i++) {
+        EXPECT_EQ(i, bytes[i]);
+    }
+}
+
+TEST(BasicUtilTest, HexToBytes2) {
+    std::string hex = "101112131415161718191A1B1C1D1E1F";
+    uint8_t bytes[16];
+
+    EXPECT_TRUE(HexToBytes((uint8_t*)bytes, sizeof(bytes), hex));
+    for (size_t i = 0; i < sizeof(bytes); i++) {
+        EXPECT_EQ(16 + i, bytes[i]);
+    }
+}
+
+TEST(BasicUtilTest, BytesToHex) {
+    const uint8_t bytes[16]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
+
+    EXPECT_EQ("0102", BytesToHex((uint8_t*)bytes, 2));
+    EXPECT_EQ("01020304", BytesToHex((uint8_t*)bytes, 4));
+    EXPECT_EQ("0102030405060708", BytesToHex((uint8_t*)bytes, 8));
+    EXPECT_EQ("0102030405060708090a0b0c0d0e0f10", BytesToHex((uint8_t*)bytes, 16));
+
+    EXPECT_EQ("01", BytesToHex((uint8_t*)bytes, 1));
+    EXPECT_EQ("010203", BytesToHex((uint8_t*)bytes, 3));
+    EXPECT_EQ("0102030405", BytesToHex((uint8_t*)bytes, 5));
+}
+
+TEST(BasicUtilTest, HexToBytesInValidOddLenHex) {
+    std::string hex = "12345";
+    uint8_t bytes[16];
+
+    EXPECT_FALSE(HexToBytes((uint8_t*)bytes, sizeof(bytes), hex));
+}
+
+TEST(BasicUtilTest, HexToBytesInsufficientByteLen) {
+    std::string hex = "101112131415161718191A1B1C1D1E1F";
+    uint8_t bytes[8];
+
+    EXPECT_FALSE(HexToBytes((uint8_t*)bytes, sizeof(bytes), hex));
+}
+
+TEST(BasicUtilTest, WaitForFile) {
+    // Gets system tmp dir.
+    base::FilePath tmp_dir;
+    ASSERT_TRUE(GetTempDir(&tmp_dir));
+
+    // Waits this path.
+    base::FilePath wait_path = tmp_dir.Append("libfs_avb-test-exist-dir");
+    ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));
+
+    EXPECT_TRUE(base::CreateDirectory(wait_path));
+    EXPECT_TRUE(WaitForFile(wait_path.value(), 1s));
+
+    // Removes the wait_path.
+    ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));
+}
+
+TEST(BasicUtilTest, WaitForFileNonExist) {
+    base::FilePath wait_path("/path/not/exist");
+    EXPECT_FALSE(WaitForFile(wait_path.value(), 200ms));
+}
+
+TEST(BasicUtilTest, WaitForFileDeferCreation) {
+    // Gets system tmp dir.
+    base::FilePath tmp_dir;
+    ASSERT_TRUE(GetTempDir(&tmp_dir));
+
+    // Waits this path.
+    base::FilePath wait_path = tmp_dir.Append("libfs_avb-test-exist-dir");
+    ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));
+    auto wait_file = std::async(WaitForFile, wait_path.value(), 500ms, FileWaitMode::Exists);
+
+    // Sleeps 100ms before creating the wait_path.
+    std::this_thread::sleep_for(100ms);
+    EXPECT_TRUE(base::CreateDirectory(wait_path));
+
+    // Checks WaitForFile() returns success.
+    EXPECT_TRUE(wait_file.get());
+
+    // Removes the wait_path.
+    ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));
+}
+
+TEST(BasicUtilTest, WaitForFileDeferCreationFailure) {
+    // Gets system tmp dir.
+    base::FilePath tmp_dir;
+    ASSERT_TRUE(GetTempDir(&tmp_dir));
+
+    // Waits this path.
+    base::FilePath wait_path = tmp_dir.Append("libfs_avb-test-exist-dir");
+    ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));
+    auto wait_file = std::async(WaitForFile, wait_path.value(), 50ms, FileWaitMode::Exists);
+
+    // Sleeps 100ms before creating the wait_path.
+    std::this_thread::sleep_for(100ms);
+    EXPECT_TRUE(base::CreateDirectory(wait_path));
+
+    // Checks WaitForFile() returns failure, because it only waits 50ms.
+    EXPECT_FALSE(wait_file.get());
+
+    // Removes the wait_path.
+    ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));
+}
+
+}  // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/types.cpp b/fs_mgr/libfs_avb/types.cpp
new file mode 100644
index 0000000..3c277f3
--- /dev/null
+++ b/fs_mgr/libfs_avb/types.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "fs_avb/types.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Helper functions to print enum class VBMetaVerifyResult.
+const char* VBMetaVerifyResultToString(VBMetaVerifyResult result) {
+    // clang-format off
+    static const char* const name[] = {
+        "ResultSuccess",
+        "ResultError",
+        "ResultErrorVerification",
+        "ResultUnknown",
+    };
+    // clang-format on
+
+    uint32_t index = static_cast<uint32_t>(result);
+    uint32_t unknown_index = sizeof(name) / sizeof(char*) - 1;
+    if (index >= unknown_index) {
+        index = unknown_index;
+    }
+
+    return name[index];
+}
+
+std::ostream& operator<<(std::ostream& os, VBMetaVerifyResult result) {
+    os << VBMetaVerifyResultToString(result);
+    return os;
+}
+
+// Helper functions to dump enum class AvbHandleStatus.
+const char* AvbHandleStatusToString(AvbHandleStatus status) {
+    // clang-format off
+    static const char* const name[] = {
+        "Success",
+        "Uninitialized",
+        "HashtreeDisabled",
+        "VerificationDisabled",
+        "VerificationError",
+        "Unknown",
+    };
+    // clang-format on
+
+    uint32_t index = static_cast<uint32_t>(status);
+    uint32_t unknown_index = sizeof(name) / sizeof(char*) - 1;
+    if (index >= unknown_index) {
+        index = unknown_index;
+    }
+
+    return name[index];
+}
+
+std::ostream& operator<<(std::ostream& os, AvbHandleStatus status) {
+    os << AvbHandleStatusToString(status);
+    return os;
+}
+
+// class VBMetaData
+// ----------------
+std::unique_ptr<AvbVBMetaImageHeader> VBMetaData::GetVBMetaHeader(bool update_vbmeta_size) {
+    auto vbmeta_header = std::make_unique<AvbVBMetaImageHeader>();
+
+    if (!vbmeta_header) return nullptr;
+
+    /* Byteswap the header. */
+    avb_vbmeta_image_header_to_host_byte_order((AvbVBMetaImageHeader*)vbmeta_ptr_.get(),
+                                               vbmeta_header.get());
+    if (update_vbmeta_size) {
+        vbmeta_size_ = sizeof(AvbVBMetaImageHeader) +
+                       vbmeta_header->authentication_data_block_size +
+                       vbmeta_header->auxiliary_data_block_size;
+    }
+
+    return vbmeta_header;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libfs_avb/util.cpp b/fs_mgr/libfs_avb/util.cpp
new file mode 100644
index 0000000..d214b5b
--- /dev/null
+++ b/fs_mgr/libfs_avb/util.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "util.h"
+
+#include <sys/ioctl.h>
+
+#include <thread>
+
+#include <android-base/unique_fd.h>
+#include <linux/fs.h>
+
+namespace android {
+namespace fs_mgr {
+
+bool NibbleValue(const char& c, uint8_t* value) {
+    CHECK(value != nullptr);
+
+    switch (c) {
+        case '0' ... '9':
+            *value = c - '0';
+            break;
+        case 'a' ... 'f':
+            *value = c - 'a' + 10;
+            break;
+        case 'A' ... 'F':
+            *value = c - 'A' + 10;
+            break;
+        default:
+            return false;
+    }
+
+    return true;
+}
+
+bool HexToBytes(uint8_t* bytes, size_t bytes_len, const std::string& hex) {
+    CHECK(bytes != nullptr);
+
+    if (hex.size() % 2 != 0) {
+        return false;
+    }
+    if (hex.size() / 2 > bytes_len) {
+        return false;
+    }
+    for (size_t i = 0, j = 0, n = hex.size(); i < n; i += 2, ++j) {
+        uint8_t high;
+        if (!NibbleValue(hex[i], &high)) {
+            return false;
+        }
+        uint8_t low;
+        if (!NibbleValue(hex[i + 1], &low)) {
+            return false;
+        }
+        bytes[j] = (high << 4) | low;
+    }
+    return true;
+}
+
+std::string BytesToHex(const uint8_t* bytes, size_t bytes_len) {
+    CHECK(bytes != nullptr);
+
+    static const char* hex_digits = "0123456789abcdef";
+    std::string hex;
+
+    for (size_t i = 0; i < bytes_len; i++) {
+        hex.push_back(hex_digits[(bytes[i] & 0xF0) >> 4]);
+        hex.push_back(hex_digits[bytes[i] & 0x0F]);
+    }
+    return hex;
+}
+
+// TODO: remove duplicate code with fs_mgr_wait_for_file
+bool WaitForFile(const std::string& filename, const std::chrono::milliseconds relative_timeout,
+                 FileWaitMode file_wait_mode) {
+    auto start_time = std::chrono::steady_clock::now();
+
+    while (true) {
+        int rv = access(filename.c_str(), F_OK);
+        if (file_wait_mode == FileWaitMode::Exists) {
+            if (!rv || errno != ENOENT) return true;
+        } else if (file_wait_mode == FileWaitMode::DoesNotExist) {
+            if (rv && errno == ENOENT) return true;
+        }
+
+        std::this_thread::sleep_for(50ms);
+
+        auto now = std::chrono::steady_clock::now();
+        auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+        if (time_elapsed > relative_timeout) return false;
+    }
+}
+
+bool IsDeviceUnlocked() {
+    std::string verified_boot_state;
+
+    if (fs_mgr_get_boot_config("verifiedbootstate", &verified_boot_state)) {
+        return verified_boot_state == "orange";
+    }
+    return false;
+}
+
+bool SetBlockDeviceReadOnly(const std::string& blockdev) {
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blockdev.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd < 0) {
+        return false;
+    }
+
+    int ON = 1;
+    return ioctl(fd, BLKROSET, &ON) == 0;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libfs_avb/util.h b/fs_mgr/libfs_avb/util.h
new file mode 100644
index 0000000..7763da5
--- /dev/null
+++ b/fs_mgr/libfs_avb/util.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <chrono>
+#include <string>
+
+#ifdef HOST_TEST
+#include <base/logging.h>
+#else
+#include <android-base/logging.h>
+#endif
+
+#define FS_AVB_TAG "[libfs_avb]"
+
+// Logs a message to kernel
+#define LINFO LOG(INFO) << FS_AVB_TAG
+#define LWARNING LOG(WARNING) << FS_AVB_TAG
+#define LERROR LOG(ERROR) << FS_AVB_TAG
+#define LFATAL LOG(FATAL) << FS_AVB_TAG
+
+// Logs a message with strerror(errno) at the end
+#define PINFO PLOG(INFO) << FS_AVB_TAG
+#define PWARNING PLOG(WARNING) << FS_AVB_TAG
+#define PERROR PLOG(ERROR) << FS_AVB_TAG
+#define PFATAL PLOG(FATAL) << FS_AVB_TAG
+
+extern bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace fs_mgr {
+
+bool NibbleValue(const char& c, uint8_t* value);
+
+bool HexToBytes(uint8_t* bytes, size_t bytes_len, const std::string& hex);
+
+std::string BytesToHex(const uint8_t* bytes, size_t bytes_len);
+
+enum class FileWaitMode { Exists, DoesNotExist };
+bool WaitForFile(const std::string& filename, const std::chrono::milliseconds relative_timeout,
+                 FileWaitMode wait_mode = FileWaitMode::Exists);
+
+bool IsDeviceUnlocked();
+
+bool SetBlockDeviceReadOnly(const std::string& blockdev);
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
new file mode 100644
index 0000000..b504161
--- /dev/null
+++ b/fs_mgr/liblp/Android.bp
@@ -0,0 +1,82 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+liblp_lib_deps = [
+    "libbase",
+    "liblog",
+    "libcrypto",
+    "libcrypto_utils",
+    "libsparse",
+    "libext4_utils",
+    "libz",
+]
+
+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",
+        "partition_opener.cpp",
+        "reader.cpp",
+        "utility.cpp",
+        "writer.cpp",
+    ],
+    shared_libs: liblp_lib_deps,
+    target: {
+        windows: {
+            enabled: true,
+        },
+        android: {
+            shared_libs: [
+                "libcutils",
+            ],
+        },
+    },
+    export_include_dirs: ["include"],
+}
+
+cc_test {
+    name: "liblp_test_static",
+    defaults: ["fs_mgr_defaults"],
+    cppflags: [
+        "-Wno-unused-parameter",
+    ],
+    static_libs: [
+        "libgmock",
+        "libfs_mgr",
+        "liblp",
+    ] + liblp_lib_deps,
+    stl: "libc++_static",
+    srcs: [
+        "builder_test.cpp",
+        "io_test.cpp",
+        "test_partition_opener.cpp",
+        "utility_test.cpp",
+    ],
+    target: {
+        android: {
+            static_libs: [
+                "libcutils",
+            ],
+        },
+    },
+}
diff --git a/fs_mgr/liblp/Android.mk b/fs_mgr/liblp/Android.mk
new file mode 100644
index 0000000..7f7f891
--- /dev/null
+++ b/fs_mgr/liblp/Android.mk
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := VtsKernelLiblpTest
+-include test/vts/tools/build/Android.host_config.mk
diff --git a/fs_mgr/liblp/AndroidTest.xml b/fs_mgr/liblp/AndroidTest.xml
new file mode 100644
index 0000000..fe1002c
--- /dev/null
+++ b/fs_mgr/liblp/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for VTS VtsKernelLiblpTest">
+    <option name="config-descriptor:metadata" key="plan" value="vts-kernel" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+        <option name="abort-on-push-failure" value="false"/>
+        <option name="push-group" value="HostDrivenTest.push"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+      <option name="test-module-name" value="VtsKernelLiblpTest"/>
+        <option name="binary-test-source" value="_32bit::DATA/nativetest/liblp_test_static/liblp_test_static" />
+        <option name="binary-test-source" value="_64bit::DATA/nativetest64/liblp_test_static/liblp_test_static" />
+        <option name="binary-test-type" value="gtest"/>
+        <option name="test-timeout" value="1m"/>
+        <option name="precondition-first-api-level" value="29" />
+    </test>
+</configuration>
+
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
new file mode 100644
index 0000000..25a042f
--- /dev/null
+++ b/fs_mgr/liblp/builder.cpp
@@ -0,0 +1,1120 @@
+/*
+ * 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"
+
+#include <string.h>
+
+#include <algorithm>
+
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+
+#include "liblp/liblp.h"
+#include "reader.h"
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+bool MetadataBuilder::sABOverrideSet;
+bool MetadataBuilder::sABOverrideValue;
+
+static const std::string kDefaultGroup = "default";
+
+bool LinearExtent::AddTo(LpMetadata* out) const {
+    if (device_index_ >= out->block_devices.size()) {
+        LERROR << "Extent references unknown block device.";
+        return false;
+    }
+    out->extents.emplace_back(
+            LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_, device_index_});
+    return true;
+}
+
+bool ZeroExtent::AddTo(LpMetadata* out) const {
+    out->extents.emplace_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0, 0});
+    return true;
+}
+
+Partition::Partition(const std::string& name, const std::string& group_name, uint32_t attributes)
+    : name_(name), group_name_(group_name), attributes_(attributes), size_(0) {}
+
+void Partition::AddExtent(std::unique_ptr<Extent>&& extent) {
+    size_ += extent->num_sectors() * LP_SECTOR_SIZE;
+
+    if (LinearExtent* new_extent = extent->AsLinearExtent()) {
+        if (!extents_.empty() && extents_.back()->AsLinearExtent()) {
+            LinearExtent* prev_extent = extents_.back()->AsLinearExtent();
+            if (prev_extent->end_sector() == new_extent->physical_sector() &&
+                prev_extent->device_index() == new_extent->device_index()) {
+                // If the previous extent can be merged into this new one, do so
+                // to avoid creating unnecessary extents.
+                extent = std::make_unique<LinearExtent>(
+                        prev_extent->num_sectors() + new_extent->num_sectors(),
+                        prev_extent->device_index(), prev_extent->physical_sector());
+                extents_.pop_back();
+            }
+        }
+    }
+    extents_.push_back(std::move(extent));
+}
+
+void Partition::RemoveExtents() {
+    size_ = 0;
+    extents_.clear();
+}
+
+void Partition::ShrinkTo(uint64_t aligned_size) {
+    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_ == aligned_size);
+}
+
+uint64_t Partition::BytesOnDisk() const {
+    uint64_t sectors = 0;
+    for (const auto& extent : extents_) {
+        if (!extent->AsLinearExtent()) {
+            continue;
+        }
+        sectors += extent->num_sectors();
+    }
+    return sectors * LP_SECTOR_SIZE;
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const IPartitionOpener& opener,
+                                                      const std::string& super_partition,
+                                                      uint32_t slot_number) {
+    std::unique_ptr<LpMetadata> metadata = ReadMetadata(opener, super_partition, slot_number);
+    if (!metadata) {
+        return nullptr;
+    }
+    return New(*metadata.get(), &opener);
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& super_partition,
+                                                      uint32_t slot_number) {
+    return New(PartitionOpener(), super_partition, slot_number);
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(
+        const std::vector<BlockDeviceInfo>& block_devices, const std::string& super_partition,
+        uint32_t metadata_max_size, uint32_t metadata_slot_count) {
+    std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
+    if (!builder->Init(block_devices, super_partition, metadata_max_size, metadata_slot_count)) {
+        return nullptr;
+    }
+    return builder;
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const LpMetadata& metadata,
+                                                      const IPartitionOpener* opener) {
+    std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
+    if (!builder->Init(metadata)) {
+        return nullptr;
+    }
+    if (opener) {
+        for (size_t i = 0; i < builder->block_devices_.size(); i++) {
+            std::string partition_name = GetBlockDevicePartitionName(builder->block_devices_[i]);
+            BlockDeviceInfo device_info;
+            if (opener->GetInfo(partition_name, &device_info)) {
+                builder->UpdateBlockDeviceInfo(i, device_info);
+            }
+        }
+    }
+    return builder;
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::NewForUpdate(const IPartitionOpener& opener,
+                                                               const std::string& source_partition,
+                                                               uint32_t source_slot_number,
+                                                               uint32_t target_slot_number) {
+    auto metadata = ReadMetadata(opener, source_partition, source_slot_number);
+    if (!metadata) {
+        return nullptr;
+    }
+
+    // On non-retrofit devices there is only one location for metadata: the
+    // super partition. update_engine will remove and resize partitions as
+    // needed. On the other hand, for retrofit devices, we'll need to
+    // translate block device and group names to update their slot suffixes.
+    auto super_device = GetMetadataSuperBlockDevice(*metadata.get());
+    if (GetBlockDevicePartitionName(*super_device) == "super") {
+        return New(*metadata.get(), &opener);
+    }
+
+    // Clear partitions and extents, since they have no meaning on the target
+    // slot. We also clear groups since they are re-added during OTA.
+    metadata->partitions.clear();
+    metadata->extents.clear();
+    metadata->groups.clear();
+
+    std::string source_slot_suffix = SlotSuffixForSlotNumber(source_slot_number);
+    std::string target_slot_suffix = SlotSuffixForSlotNumber(target_slot_number);
+
+    // Translate block devices.
+    auto source_block_devices = std::move(metadata->block_devices);
+    for (const auto& source_block_device : source_block_devices) {
+        std::string partition_name = GetBlockDevicePartitionName(source_block_device);
+        std::string slot_suffix = GetPartitionSlotSuffix(partition_name);
+        if (slot_suffix.empty() || slot_suffix != source_slot_suffix) {
+            // This should never happen. It means that the source metadata
+            // refers to a target or unknown block device.
+            LERROR << "Invalid block device for slot " << source_slot_suffix << ": "
+                   << partition_name;
+            return nullptr;
+        }
+        std::string new_name =
+                partition_name.substr(0, partition_name.size() - slot_suffix.size()) +
+                target_slot_suffix;
+
+        auto new_device = source_block_device;
+        if (!UpdateBlockDevicePartitionName(&new_device, new_name)) {
+            LERROR << "Partition name too long: " << new_name;
+            return nullptr;
+        }
+        metadata->block_devices.emplace_back(new_device);
+    }
+
+    return New(*metadata.get(), &opener);
+}
+
+void MetadataBuilder::OverrideABForTesting(bool ab_device) {
+    sABOverrideSet = true;
+    sABOverrideValue = ab_device;
+}
+
+MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false) {
+    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);
+    header_.groups.entry_size = sizeof(LpMetadataPartitionGroup);
+    header_.block_devices.entry_size = sizeof(LpMetadataBlockDevice);
+}
+
+bool MetadataBuilder::Init(const LpMetadata& metadata) {
+    geometry_ = metadata.geometry;
+    block_devices_ = metadata.block_devices;
+
+    for (const auto& group : metadata.groups) {
+        std::string group_name = GetPartitionGroupName(group);
+        if (!AddGroup(group_name, group.maximum_size)) {
+            return false;
+        }
+    }
+
+    for (const auto& partition : metadata.partitions) {
+        std::string group_name = GetPartitionGroupName(metadata.groups[partition.group_index]);
+        Partition* builder =
+                AddPartition(GetPartitionName(partition), group_name, partition.attributes);
+        if (!builder) {
+            return false;
+        }
+        ImportExtents(builder, metadata, partition);
+    }
+    return true;
+}
+
+void MetadataBuilder::ImportExtents(Partition* dest, const LpMetadata& metadata,
+                                    const LpMetadataPartition& source) {
+    for (size_t i = 0; i < source.num_extents; i++) {
+        const LpMetadataExtent& extent = metadata.extents[source.first_extent_index + i];
+        if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
+            auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_source,
+                                                       extent.target_data);
+            dest->AddExtent(std::move(copy));
+        } else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
+            auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);
+            dest->AddExtent(std::move(copy));
+        }
+    }
+}
+
+static bool VerifyDeviceProperties(const BlockDeviceInfo& device_info) {
+    if (device_info.logical_block_size == 0) {
+        LERROR << "Block device " << device_info.partition_name
+               << " logical block size must not be zero.";
+        return false;
+    }
+    if (device_info.logical_block_size % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block device " << device_info.partition_name
+               << " logical block size must be a multiple of 512.";
+        return false;
+    }
+    if (device_info.size % device_info.logical_block_size != 0) {
+        LERROR << "Block device " << device_info.partition_name
+               << " size must be a multiple of its block size.";
+        return false;
+    }
+    if (device_info.alignment_offset % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block device " << device_info.partition_name
+               << " alignment offset is not sector-aligned.";
+        return false;
+    }
+    if (device_info.alignment % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block device " << device_info.partition_name
+               << " partition alignment is not sector-aligned.";
+        return false;
+    }
+    if (device_info.alignment_offset > device_info.alignment) {
+        LERROR << "Block device " << device_info.partition_name
+               << " partition alignment offset is greater than its alignment.";
+        return false;
+    }
+    return true;
+}
+
+bool MetadataBuilder::Init(const std::vector<BlockDeviceInfo>& block_devices,
+                           const std::string& super_partition, 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;
+    }
+    if (block_devices.empty()) {
+        LERROR << "No block devices were specified.";
+        return false;
+    }
+
+    // Align the metadata size up to the nearest sector.
+    metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE);
+
+    // Validate and build the block device list.
+    uint32_t logical_block_size = 0;
+    for (const auto& device_info : block_devices) {
+        if (!VerifyDeviceProperties(device_info)) {
+            return false;
+        }
+
+        if (!logical_block_size) {
+            logical_block_size = device_info.logical_block_size;
+        }
+        if (logical_block_size != device_info.logical_block_size) {
+            LERROR << "All partitions must have the same logical block size.";
+            return false;
+        }
+
+        LpMetadataBlockDevice out = {};
+        out.alignment = device_info.alignment;
+        out.alignment_offset = device_info.alignment_offset;
+        out.size = device_info.size;
+        if (device_info.partition_name.size() > sizeof(out.partition_name)) {
+            LERROR << "Partition name " << device_info.partition_name << " exceeds maximum length.";
+            return false;
+        }
+        strncpy(out.partition_name, device_info.partition_name.c_str(), sizeof(out.partition_name));
+
+        // In the case of the super partition, this field will be adjusted
+        // later. For all partitions, the first 512 bytes are considered
+        // untouched to be compatible code that looks for an MBR. Thus we
+        // start counting free sectors at sector 1, not 0.
+        uint64_t free_area_start = LP_SECTOR_SIZE;
+        if (out.alignment || out.alignment_offset) {
+            free_area_start = AlignTo(free_area_start, out.alignment, out.alignment_offset);
+        } else {
+            free_area_start = AlignTo(free_area_start, logical_block_size);
+        }
+        out.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
+
+        // There must be one logical block of space available.
+        uint64_t minimum_size = out.first_logical_sector * LP_SECTOR_SIZE + logical_block_size;
+        if (device_info.size < minimum_size) {
+            LERROR << "Block device " << device_info.partition_name
+                   << " is too small to hold any logical partitions.";
+            return false;
+        }
+
+        // The "root" of the super partition is always listed first.
+        if (device_info.partition_name == super_partition) {
+            block_devices_.emplace(block_devices_.begin(), out);
+        } else {
+            block_devices_.emplace_back(out);
+        }
+    }
+    if (GetBlockDevicePartitionName(block_devices_[0]) != super_partition) {
+        LERROR << "No super partition was specified.";
+        return false;
+    }
+
+    LpMetadataBlockDevice& super = block_devices_[0];
+
+    // 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 total_reserved = GetTotalMetadataSize(metadata_max_size, metadata_slot_count);
+    if (super.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_start = total_reserved;
+    if (super.alignment || super.alignment_offset) {
+        free_area_start = AlignTo(free_area_start, super.alignment, super.alignment_offset);
+    } else {
+        free_area_start = AlignTo(free_area_start, logical_block_size);
+    }
+    super.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
+
+    // There must be one logical block of free space remaining (enough for one partition).
+    uint64_t minimum_disk_size = (super.first_logical_sector * LP_SECTOR_SIZE) + logical_block_size;
+    if (super.size < minimum_disk_size) {
+        LERROR << "Device must be at least " << minimum_disk_size << " bytes, only has "
+               << super.size;
+        return false;
+    }
+
+    geometry_.metadata_max_size = metadata_max_size;
+    geometry_.metadata_slot_count = metadata_slot_count;
+    geometry_.logical_block_size = logical_block_size;
+
+    if (!AddGroup(kDefaultGroup, 0)) {
+        return false;
+    }
+    return true;
+}
+
+bool MetadataBuilder::AddGroup(const std::string& group_name, uint64_t maximum_size) {
+    if (FindGroup(group_name)) {
+        LERROR << "Group already exists: " << group_name;
+        return false;
+    }
+    groups_.push_back(std::make_unique<PartitionGroup>(group_name, maximum_size));
+    return true;
+}
+
+Partition* MetadataBuilder::AddPartition(const std::string& name, uint32_t attributes) {
+    return AddPartition(name, kDefaultGroup, attributes);
+}
+
+Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& group_name,
+                                         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;
+    }
+    if (!FindGroup(group_name)) {
+        LERROR << "Could not find partition group: " << group_name;
+        return nullptr;
+    }
+    partitions_.push_back(std::make_unique<Partition>(name, group_name, 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;
+}
+
+PartitionGroup* MetadataBuilder::FindGroup(const std::string& group_name) {
+    for (const auto& group : groups_) {
+        if (group->name() == group_name) {
+            return group.get();
+        }
+    }
+    return nullptr;
+}
+
+uint64_t MetadataBuilder::TotalSizeOfGroup(PartitionGroup* group) const {
+    uint64_t total = 0;
+    for (const auto& partition : partitions_) {
+        if (partition->group_name() != group->name()) {
+            continue;
+        }
+        total += partition->BytesOnDisk();
+    }
+    return total;
+}
+
+void MetadataBuilder::RemovePartition(const std::string& name) {
+    for (auto iter = partitions_.begin(); iter != partitions_.end(); iter++) {
+        if ((*iter)->name() == name) {
+            partitions_.erase(iter);
+            return;
+        }
+    }
+}
+
+void MetadataBuilder::ExtentsToFreeList(const std::vector<Interval>& extents,
+                                        std::vector<Interval>* free_regions) const {
+    // Convert the extent list into a list of gaps between the extents; i.e.,
+    // the list of ranges that are free on the disk.
+    for (size_t i = 1; i < extents.size(); i++) {
+        const Interval& previous = extents[i - 1];
+        const Interval& current = extents[i];
+        DCHECK(previous.device_index == current.device_index);
+
+        uint64_t aligned = AlignSector(block_devices_[current.device_index], previous.end);
+        if (aligned >= current.start) {
+            // There is no gap between these two extents, try the next one.
+            // Note that we check with >= instead of >, since alignment may
+            // bump the ending sector past the beginning of the next extent.
+            continue;
+        }
+
+        // The new interval represents the free space starting at the end of
+        // the previous interval, and ending at the start of the next interval.
+        free_regions->emplace_back(current.device_index, aligned, current.start);
+    }
+}
+
+auto MetadataBuilder::GetFreeRegions() const -> std::vector<Interval> {
+    std::vector<Interval> free_regions;
+
+    // Collect all extents in the partition table, per-device, then sort them
+    // by starting sector.
+    std::vector<std::vector<Interval>> device_extents(block_devices_.size());
+    for (const auto& partition : partitions_) {
+        for (const auto& extent : partition->extents()) {
+            LinearExtent* linear = extent->AsLinearExtent();
+            if (!linear) {
+                continue;
+            }
+            CHECK(linear->device_index() < device_extents.size());
+            auto& extents = device_extents[linear->device_index()];
+            extents.emplace_back(linear->device_index(), linear->physical_sector(),
+                                 linear->physical_sector() + extent->num_sectors());
+        }
+    }
+
+    // Add 0-length intervals for the first and last sectors. This will cause
+    // ExtentToFreeList() to treat the space in between as available.
+    for (size_t i = 0; i < device_extents.size(); i++) {
+        auto& extents = device_extents[i];
+        const auto& block_device = block_devices_[i];
+
+        uint64_t first_sector = block_device.first_logical_sector;
+        uint64_t last_sector = block_device.size / LP_SECTOR_SIZE;
+        extents.emplace_back(i, first_sector, first_sector);
+        extents.emplace_back(i, last_sector, last_sector);
+
+        std::sort(extents.begin(), extents.end());
+        ExtentsToFreeList(extents, &free_regions);
+    }
+    return free_regions;
+}
+
+bool MetadataBuilder::ValidatePartitionSizeChange(Partition* partition, uint64_t old_size,
+                                                  uint64_t new_size, bool force_check) {
+    PartitionGroup* group = FindGroup(partition->group_name());
+    CHECK(group);
+
+    if (!force_check && new_size <= old_size) {
+        return true;
+    }
+
+    // Figure out how much we need to allocate, and whether our group has
+    // enough space remaining.
+    uint64_t space_needed = new_size - old_size;
+    if (group->maximum_size() > 0) {
+        uint64_t group_size = TotalSizeOfGroup(group);
+        if (group_size >= group->maximum_size() ||
+            group->maximum_size() - group_size < space_needed) {
+            LERROR << "Partition " << partition->name() << " is part of group " << group->name()
+                   << " which does not have enough space free (" << space_needed << " requested, "
+                   << group_size << " used out of " << group->maximum_size() << ")";
+            return false;
+        }
+    }
+    return true;
+}
+
+bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
+    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);
+
+    std::vector<Interval> free_regions = GetFreeRegions();
+
+    const uint64_t sectors_per_block = geometry_.logical_block_size / LP_SECTOR_SIZE;
+    CHECK_NE(sectors_per_block, 0);
+    CHECK(sectors_needed % sectors_per_block == 0);
+
+    if (IsABDevice() && !IsRetrofitDevice() && GetPartitionSlotSuffix(partition->name()) == "_b") {
+        // Allocate "a" partitions top-down and "b" partitions bottom-up, to
+        // minimize fragmentation during OTA.
+        free_regions = PrioritizeSecondHalfOfSuper(free_regions);
+    }
+
+    // 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;
+
+    // If the last extent in the partition has a size < alignment, then the
+    // difference is unallocatable due to being misaligned. We peek for that
+    // case here to avoid wasting space.
+    if (auto extent = ExtendFinalExtent(partition, free_regions, sectors_needed)) {
+        sectors_needed -= extent->num_sectors();
+        new_extents.emplace_back(std::move(extent));
+    }
+
+    for (auto& region : free_regions) {
+        // Note: this comes first, since we may enter the loop not needing any
+        // more sectors.
+        if (!sectors_needed) {
+            break;
+        }
+
+        if (region.length() % sectors_per_block != 0) {
+            // This should never happen, because it would imply that we
+            // once allocated an extent that was not a multiple of the
+            // block size. That extent would be rejected by DM_TABLE_LOAD.
+            LERROR << "Region " << region.start << ".." << region.end
+                   << " is not a multiple of the block size, " << sectors_per_block;
+
+            // If for some reason the final region is mis-sized we still want
+            // to be able to grow partitions. So just to be safe, round the
+            // region down to the nearest block.
+            region.end = region.start + (region.length() / sectors_per_block) * sectors_per_block;
+            if (!region.length()) {
+                continue;
+            }
+        }
+
+        uint64_t sectors = std::min(sectors_needed, region.length());
+        CHECK(sectors % sectors_per_block == 0);
+
+        auto extent = std::make_unique<LinearExtent>(sectors, region.device_index, region.start);
+        new_extents.push_back(std::move(extent));
+        sectors_needed -= sectors;
+    }
+    if (sectors_needed) {
+        LERROR << "Not enough free space to expand partition: " << partition->name();
+        return false;
+    }
+
+    // Everything succeeded, so commit the new extents.
+    for (auto& extent : new_extents) {
+        partition->AddExtent(std::move(extent));
+    }
+    return true;
+}
+
+std::vector<MetadataBuilder::Interval> MetadataBuilder::PrioritizeSecondHalfOfSuper(
+        const std::vector<Interval>& free_list) {
+    const auto& super = block_devices_[0];
+    uint64_t first_sector = super.first_logical_sector;
+    uint64_t last_sector = super.size / LP_SECTOR_SIZE;
+    uint64_t midpoint = first_sector + (last_sector - first_sector) / 2;
+
+    // Choose an aligned sector for the midpoint. This could lead to one half
+    // being slightly larger than the other, but this will not restrict the
+    // size of partitions (it might lead to one extra extent if "B" overflows).
+    midpoint = AlignSector(super, midpoint);
+
+    std::vector<Interval> first_half;
+    std::vector<Interval> second_half;
+    for (const auto& region : free_list) {
+        // Note: deprioritze if not the main super partition. Even though we
+        // don't call this for retrofit devices, we will allow adding additional
+        // block devices on non-retrofit devices.
+        if (region.device_index != 0 || region.end <= midpoint) {
+            first_half.emplace_back(region);
+            continue;
+        }
+        if (region.start < midpoint && region.end > midpoint) {
+            // Split this into two regions.
+            first_half.emplace_back(region.device_index, region.start, midpoint);
+            second_half.emplace_back(region.device_index, midpoint, region.end);
+        } else {
+            second_half.emplace_back(region);
+        }
+    }
+    second_half.insert(second_half.end(), first_half.begin(), first_half.end());
+    return second_half;
+}
+
+std::unique_ptr<LinearExtent> MetadataBuilder::ExtendFinalExtent(
+        Partition* partition, const std::vector<Interval>& free_list,
+        uint64_t sectors_needed) const {
+    if (partition->extents().empty()) {
+        return nullptr;
+    }
+    LinearExtent* extent = partition->extents().back()->AsLinearExtent();
+    if (!extent) {
+        return nullptr;
+    }
+
+    // If the sector ends where the next aligned chunk begins, then there's
+    // no missing gap to try and allocate.
+    const auto& block_device = block_devices_[extent->device_index()];
+    uint64_t next_aligned_sector = AlignSector(block_device, extent->end_sector());
+    if (extent->end_sector() == next_aligned_sector) {
+        return nullptr;
+    }
+
+    uint64_t num_sectors = std::min(next_aligned_sector - extent->end_sector(), sectors_needed);
+    auto new_extent = std::make_unique<LinearExtent>(num_sectors, extent->device_index(),
+                                                     extent->end_sector());
+    if (IsAnyRegionAllocated(*new_extent.get()) ||
+        IsAnyRegionCovered(free_list, *new_extent.get())) {
+        LERROR << "Misaligned region " << new_extent->physical_sector() << ".."
+               << new_extent->end_sector() << " was allocated or marked allocatable.";
+        return nullptr;
+    }
+    return new_extent;
+}
+
+bool MetadataBuilder::IsAnyRegionCovered(const std::vector<Interval>& regions,
+                                         const LinearExtent& candidate) const {
+    for (const auto& region : regions) {
+        if (region.device_index == candidate.device_index() &&
+            (candidate.OwnsSector(region.start) || candidate.OwnsSector(region.end))) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool MetadataBuilder::IsAnyRegionAllocated(const LinearExtent& candidate) const {
+    for (const auto& partition : partitions_) {
+        for (const auto& extent : partition->extents()) {
+            LinearExtent* linear = extent->AsLinearExtent();
+            if (!linear || linear->device_index() != candidate.device_index()) {
+                continue;
+            }
+            if (linear->OwnsSector(candidate.physical_sector()) ||
+                linear->OwnsSector(candidate.end_sector() - 1)) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+void MetadataBuilder::ShrinkPartition(Partition* partition, uint64_t aligned_size) {
+    partition->ShrinkTo(aligned_size);
+}
+
+std::unique_ptr<LpMetadata> MetadataBuilder::Export() {
+    if (!ValidatePartitionGroups()) {
+        return nullptr;
+    }
+
+    std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
+    metadata->header = header_;
+    metadata->geometry = geometry_;
+
+    // Assign this early so the extent table can read it.
+    for (const auto& block_device : block_devices_) {
+        metadata->block_devices.emplace_back(block_device);
+        if (auto_slot_suffixing_) {
+            metadata->block_devices.back().flags |= LP_BLOCK_DEVICE_SLOT_SUFFIXED;
+        }
+    }
+
+    std::map<std::string, size_t> group_indices;
+    for (const auto& group : groups_) {
+        LpMetadataPartitionGroup out = {};
+
+        if (group->name().size() > sizeof(out.name)) {
+            LERROR << "Partition group name is too long: " << group->name();
+            return nullptr;
+        }
+        if (auto_slot_suffixing_ && group->name() != kDefaultGroup) {
+            out.flags |= LP_GROUP_SLOT_SUFFIXED;
+        }
+        strncpy(out.name, group->name().c_str(), sizeof(out.name));
+        out.maximum_size = group->maximum_size();
+
+        group_indices[group->name()] = metadata->groups.size();
+        metadata->groups.push_back(out);
+    }
+
+    // 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));
+        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();
+        if (auto_slot_suffixing_) {
+            part.attributes |= LP_PARTITION_ATTR_SLOT_SUFFIXED;
+        }
+
+        auto iter = group_indices.find(partition->group_name());
+        if (iter == group_indices.end()) {
+            LERROR << "Partition " << partition->name() << " is a member of unknown group "
+                   << partition->group_name();
+            return nullptr;
+        }
+        part.group_index = iter->second;
+
+        for (const auto& extent : partition->extents()) {
+            if (!extent->AddTo(metadata.get())) {
+                return nullptr;
+            }
+        }
+        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());
+    metadata->header.groups.num_entries = static_cast<uint32_t>(metadata->groups.size());
+    metadata->header.block_devices.num_entries =
+            static_cast<uint32_t>(metadata->block_devices.size());
+    return metadata;
+}
+
+uint64_t MetadataBuilder::AllocatableSpace() const {
+    uint64_t total_size = 0;
+    for (const auto& block_device : block_devices_) {
+        total_size += block_device.size - (block_device.first_logical_sector * LP_SECTOR_SIZE);
+    }
+    return total_size;
+}
+
+uint64_t MetadataBuilder::UsedSpace() const {
+    uint64_t size = 0;
+    for (const auto& partition : partitions_) {
+        size += partition->size();
+    }
+    return size;
+}
+
+uint64_t MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device,
+                                      uint64_t sector) const {
+    // 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, block_device.alignment, block_device.alignment_offset);
+    return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
+}
+
+bool MetadataBuilder::FindBlockDeviceByName(const std::string& partition_name,
+                                            uint32_t* index) const {
+    for (size_t i = 0; i < block_devices_.size(); i++) {
+        if (GetBlockDevicePartitionName(block_devices_[i]) == partition_name) {
+            *index = i;
+            return true;
+        }
+    }
+    return false;
+}
+
+bool MetadataBuilder::HasBlockDevice(const std::string& partition_name) const {
+    uint32_t index;
+    return FindBlockDeviceByName(partition_name, &index);
+}
+
+bool MetadataBuilder::GetBlockDeviceInfo(const std::string& partition_name,
+                                         BlockDeviceInfo* info) const {
+    uint32_t index;
+    if (!FindBlockDeviceByName(partition_name, &index)) {
+        LERROR << "No device named " << partition_name;
+        return false;
+    }
+    info->size = block_devices_[index].size;
+    info->alignment = block_devices_[index].alignment;
+    info->alignment_offset = block_devices_[index].alignment_offset;
+    info->logical_block_size = geometry_.logical_block_size;
+    info->partition_name = partition_name;
+    return true;
+}
+
+bool MetadataBuilder::UpdateBlockDeviceInfo(const std::string& partition_name,
+                                            const BlockDeviceInfo& device_info) {
+    uint32_t index;
+    if (!FindBlockDeviceByName(partition_name, &index)) {
+        LERROR << "No device named " << partition_name;
+        return false;
+    }
+    return UpdateBlockDeviceInfo(index, device_info);
+}
+
+bool MetadataBuilder::UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& device_info) {
+    CHECK(index < block_devices_.size());
+
+    LpMetadataBlockDevice& block_device = block_devices_[index];
+    if (device_info.size != block_device.size) {
+        LERROR << "Device size does not match (got " << device_info.size << ", expected "
+               << block_device.size << ")";
+        return false;
+    }
+    if (geometry_.logical_block_size % device_info.logical_block_size) {
+        LERROR << "Device logical block size is misaligned (block size="
+               << device_info.logical_block_size << ", alignment=" << geometry_.logical_block_size
+               << ")";
+        return false;
+    }
+
+    // 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) {
+        block_device.alignment = device_info.alignment;
+    }
+    if (device_info.alignment_offset) {
+        block_device.alignment_offset = device_info.alignment_offset;
+    }
+    return true;
+}
+
+bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size) {
+    // Align the space needed up to the nearest sector.
+    uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size);
+    uint64_t old_size = partition->size();
+
+    if (!ValidatePartitionSizeChange(partition, old_size, aligned_size, false)) {
+        return false;
+    }
+
+    if (aligned_size > old_size) {
+        if (!GrowPartition(partition, aligned_size)) {
+            return false;
+        }
+    } else if (aligned_size < partition->size()) {
+        ShrinkPartition(partition, aligned_size);
+    }
+
+    if (partition->size() != old_size) {
+        LINFO << "Partition " << partition->name() << " will resize from " << old_size
+              << " bytes to " << aligned_size << " bytes";
+    }
+    return true;
+}
+
+std::vector<std::string> MetadataBuilder::ListGroups() const {
+    std::vector<std::string> names;
+    for (const auto& group : groups_) {
+        names.emplace_back(group->name());
+    }
+    return names;
+}
+
+void MetadataBuilder::RemoveGroupAndPartitions(const std::string& group_name) {
+    if (group_name == kDefaultGroup) {
+        // Cannot remove the default group.
+        return;
+    }
+    std::vector<std::string> partition_names;
+    for (const auto& partition : partitions_) {
+        if (partition->group_name() == group_name) {
+            partition_names.emplace_back(partition->name());
+        }
+    }
+
+    for (const auto& partition_name : partition_names) {
+        RemovePartition(partition_name);
+    }
+    for (auto iter = groups_.begin(); iter != groups_.end(); iter++) {
+        if ((*iter)->name() == group_name) {
+            groups_.erase(iter);
+            break;
+        }
+    }
+}
+
+static bool CompareBlockDevices(const LpMetadataBlockDevice& first,
+                                const LpMetadataBlockDevice& second) {
+    // Note: we don't compare alignment, since it's a performance thing and
+    // won't affect whether old extents continue to work.
+    return first.first_logical_sector == second.first_logical_sector && first.size == second.size &&
+           GetBlockDevicePartitionName(first) == GetBlockDevicePartitionName(second);
+}
+
+bool MetadataBuilder::ImportPartitions(const LpMetadata& metadata,
+                                       const std::set<std::string>& partition_names) {
+    // The block device list must be identical. We do not try to be clever and
+    // allow ordering changes or changes that don't affect partitions. This
+    // process is designed to allow the most common flashing scenarios and more
+    // complex ones should require a wipe.
+    if (metadata.block_devices.size() != block_devices_.size()) {
+        LINFO << "Block device tables does not match.";
+        return false;
+    }
+    for (size_t i = 0; i < metadata.block_devices.size(); i++) {
+        const LpMetadataBlockDevice& old_device = metadata.block_devices[i];
+        const LpMetadataBlockDevice& new_device = block_devices_[i];
+        if (!CompareBlockDevices(old_device, new_device)) {
+            LINFO << "Block device tables do not match";
+            return false;
+        }
+    }
+
+    // Import named partitions. Note that we do not attempt to merge group
+    // information here. If the device changed its group names, the old
+    // partitions will fail to merge. The same could happen if the group
+    // allocation sizes change.
+    for (const auto& partition : metadata.partitions) {
+        std::string partition_name = GetPartitionName(partition);
+        if (partition_names.find(partition_name) == partition_names.end()) {
+            continue;
+        }
+        if (!ImportPartition(metadata, partition)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool MetadataBuilder::ImportPartition(const LpMetadata& metadata,
+                                      const LpMetadataPartition& source) {
+    std::string partition_name = GetPartitionName(source);
+    Partition* partition = FindPartition(partition_name);
+    if (!partition) {
+        std::string group_name = GetPartitionGroupName(metadata.groups[source.group_index]);
+        partition = AddPartition(partition_name, group_name, source.attributes);
+        if (!partition) {
+            return false;
+        }
+    }
+    if (partition->size() > 0) {
+        LINFO << "Importing partition table would overwrite non-empty partition: "
+              << partition_name;
+        return false;
+    }
+
+    ImportExtents(partition, metadata, source);
+
+    // Note: we've already increased the partition size by calling
+    // ImportExtents(). In order to figure out the size before that,
+    // we would have to iterate the extents and add up the linear
+    // segments. Instead, we just force ValidatePartitionSizeChange
+    // to check if the current configuration is acceptable.
+    if (!ValidatePartitionSizeChange(partition, partition->size(), partition->size(), true)) {
+        partition->RemoveExtents();
+        return false;
+    }
+    return true;
+}
+
+void MetadataBuilder::SetAutoSlotSuffixing() {
+    auto_slot_suffixing_ = true;
+}
+
+bool MetadataBuilder::IsABDevice() const {
+    if (sABOverrideSet) {
+        return sABOverrideValue;
+    }
+    return !android::base::GetProperty("ro.boot.slot_suffix", "").empty();
+}
+
+bool MetadataBuilder::IsRetrofitDevice() const {
+    return GetBlockDevicePartitionName(block_devices_[0]) != LP_METADATA_DEFAULT_PARTITION_NAME;
+}
+
+bool MetadataBuilder::AddLinearExtent(Partition* partition, const std::string& block_device,
+                                      uint64_t num_sectors, uint64_t physical_sector) {
+    uint32_t device_index;
+    if (!FindBlockDeviceByName(block_device, &device_index)) {
+        LERROR << "Could not find backing block device for extent: " << block_device;
+        return false;
+    }
+
+    auto extent = std::make_unique<LinearExtent>(num_sectors, device_index, physical_sector);
+    partition->AddExtent(std::move(extent));
+    return true;
+}
+
+std::vector<Partition*> MetadataBuilder::ListPartitionsInGroup(const std::string& group_name) {
+    std::vector<Partition*> partitions;
+    for (const auto& partition : partitions_) {
+        if (partition->group_name() == group_name) {
+            partitions.emplace_back(partition.get());
+        }
+    }
+    return partitions;
+}
+
+bool MetadataBuilder::ChangePartitionGroup(Partition* partition, const std::string& group_name) {
+    if (!FindGroup(group_name)) {
+        LERROR << "Partition cannot change to unknown group: " << group_name;
+        return false;
+    }
+    partition->set_group_name(group_name);
+    return true;
+}
+
+bool MetadataBuilder::ValidatePartitionGroups() const {
+    for (const auto& group : groups_) {
+        if (!group->maximum_size()) {
+            continue;
+        }
+        uint64_t used = TotalSizeOfGroup(group.get());
+        if (used > group->maximum_size()) {
+            LERROR << "Partition group " << group->name() << " exceeds maximum size (" << used
+                   << " bytes used, maximum " << group->maximum_size() << ")";
+            return false;
+        }
+    }
+    return true;
+}
+
+bool MetadataBuilder::ChangeGroupSize(const std::string& group_name, uint64_t maximum_size) {
+    if (group_name == kDefaultGroup) {
+        LERROR << "Cannot change the size of the default group";
+        return false;
+    }
+    PartitionGroup* group = FindGroup(group_name);
+    if (!group) {
+        LERROR << "Cannot change size of unknown partition group: " << group_name;
+        return false;
+    }
+    group->set_maximum_size(maximum_size);
+    return true;
+}
+
+}  // 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..34c68d4
--- /dev/null
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -0,0 +1,905 @@
+/*
+ * 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 <fs_mgr.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <liblp/builder.h>
+
+#include "utility.h"
+
+using namespace std;
+using namespace android::fs_mgr;
+using ::testing::ElementsAre;
+
+class Environment : public ::testing::Environment {
+  public:
+    void SetUp() override { MetadataBuilder::OverrideABForTesting(false); }
+};
+
+int main(int argc, char** argv) {
+    std::unique_ptr<Environment> env(new Environment);
+    ::testing::AddGlobalTestEnvironment(env.get());
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
+
+class BuilderTest : public ::testing::Test {
+  public:
+    void SetUp() override { MetadataBuilder::OverrideABForTesting(false); }
+    void TearDown() override { MetadataBuilder::OverrideABForTesting(false); }
+};
+
+TEST_F(BuilderTest, BuildBasic) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+
+    Partition* partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(partition, nullptr);
+    EXPECT_EQ(partition->name(), "system");
+    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_F(BuilderTest, ResizePartition) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    EXPECT_EQ(builder->ResizePartition(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:
+    //      (LP_PARTITION_RESERVED_BYTES + 4096*2 + 1024*4) / 512
+    // Or, in terms of sectors (reserved + geometry + metadata):
+    //      (8 + 16 + 8) = 32
+    EXPECT_EQ(extent->physical_sector(), 32);
+
+    // Test resizing to the same size.
+    EXPECT_EQ(builder->ResizePartition(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 resizing to a smaller size.
+    EXPECT_EQ(builder->ResizePartition(system, 0), true);
+    EXPECT_EQ(system->size(), 0);
+    EXPECT_EQ(system->extents().size(), 0);
+    // Test resizing to a greater size.
+    builder->ResizePartition(system, 131072);
+    EXPECT_EQ(system->size(), 131072);
+    EXPECT_EQ(system->extents().size(), 1);
+    EXPECT_EQ(system->extents()[0]->num_sectors(), 131072 / LP_SECTOR_SIZE);
+    // Test resizing again, that the extents are merged together.
+    builder->ResizePartition(system, 1024 * 256);
+    EXPECT_EQ(system->size(), 1024 * 256);
+    EXPECT_EQ(system->extents().size(), 1);
+    EXPECT_EQ(system->extents()[0]->num_sectors(), (1024 * 256) / LP_SECTOR_SIZE);
+
+    // Test shrinking within the same extent.
+    builder->ResizePartition(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(), 32);
+
+    auto exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    ASSERT_EQ(FindPartition(*exported.get(), "not found"), nullptr);
+    auto entry = FindPartition(*exported.get(), "system");
+    ASSERT_NE(entry, nullptr);
+    ASSERT_EQ(GetPartitionSize(*exported.get(), *entry), 32768);
+
+    // Test shrinking to 0.
+    builder->ResizePartition(system, 0);
+    EXPECT_EQ(system->size(), 0);
+    EXPECT_EQ(system->extents().size(), 0);
+}
+
+TEST_F(BuilderTest, PartitionAlignment) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+
+    // Test that we align up to one sector.
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    EXPECT_EQ(builder->ResizePartition(system, 10000), true);
+    EXPECT_EQ(system->size(), 12288);
+    EXPECT_EQ(system->extents().size(), 1);
+
+    builder->ResizePartition(system, 7000);
+    EXPECT_EQ(system->size(), 8192);
+    EXPECT_EQ(system->extents().size(), 1);
+}
+
+TEST_F(BuilderTest, 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_F(BuilderTest, MetadataAlignment) {
+    // Make sure metadata sizes get aligned up.
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1000, 2);
+    ASSERT_NE(builder, nullptr);
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    EXPECT_EQ(exported->geometry.metadata_max_size, 1024);
+}
+
+TEST_F(BuilderTest, InternalAlignment) {
+    // Test the metadata fitting within alignment.
+    BlockDeviceInfo device_info("super", 1024 * 1024, 768 * 1024, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    auto super_device = GetMetadataSuperBlockDevice(*exported.get());
+    ASSERT_NE(super_device, nullptr);
+    EXPECT_EQ(super_device->first_logical_sector, 1536);
+
+    // 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);
+    super_device = GetMetadataSuperBlockDevice(*exported.get());
+    ASSERT_NE(super_device, nullptr);
+    EXPECT_EQ(super_device->first_logical_sector, 1472);
+
+    // 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);
+    super_device = GetMetadataSuperBlockDevice(*exported.get());
+    ASSERT_NE(super_device, nullptr);
+    EXPECT_EQ(super_device->first_logical_sector, 174);
+
+    // 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);
+    super_device = GetMetadataSuperBlockDevice(*exported.get());
+    ASSERT_NE(super_device, nullptr);
+    EXPECT_EQ(super_device->first_logical_sector, 160);
+}
+
+TEST_F(BuilderTest, InternalPartitionAlignment) {
+    BlockDeviceInfo device_info("super", 512 * 1024 * 1024, 768 * 1024, 753664, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 32 * 1024, 2);
+
+    Partition* a = builder->AddPartition("a", 0);
+    ASSERT_NE(a, nullptr);
+    Partition* b = builder->AddPartition("b", 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->ResizePartition(a, a->size() + 4096));
+        ASSERT_TRUE(builder->ResizePartition(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, 80);
+
+        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, 3008);
+}
+
+TEST_F(BuilderTest, UseAllDiskSpace) {
+    static constexpr uint64_t total = 1024 * 1024;
+    static constexpr uint64_t metadata = 1024;
+    static constexpr uint64_t slots = 2;
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(total, metadata, slots);
+    // 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.
+    static constexpr uint64_t geometry = 4 * 1024;
+    static constexpr uint64_t allocatable =
+            total - (metadata * slots + geometry) * 2 - LP_PARTITION_RESERVED_BYTES;
+    EXPECT_EQ(builder->AllocatableSpace(), allocatable);
+    EXPECT_EQ(builder->UsedSpace(), 0);
+
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    EXPECT_EQ(builder->ResizePartition(system, allocatable), true);
+    EXPECT_EQ(system->size(), allocatable);
+    EXPECT_EQ(builder->UsedSpace(), allocatable);
+    EXPECT_EQ(builder->AllocatableSpace(), allocatable);
+    EXPECT_EQ(builder->ResizePartition(system, allocatable + 1), false);
+    EXPECT_EQ(system->size(), allocatable);
+    EXPECT_EQ(builder->UsedSpace(), allocatable);
+    EXPECT_EQ(builder->AllocatableSpace(), allocatable);
+}
+
+TEST_F(BuilderTest, BuildComplex) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+    Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    ASSERT_NE(vendor, nullptr);
+    EXPECT_EQ(builder->ResizePartition(system, 65536), true);
+    EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);
+    EXPECT_EQ(builder->ResizePartition(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(), 32);
+    EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);
+    EXPECT_EQ(system2->physical_sector(), 224);
+    EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
+    EXPECT_EQ(vendor1->physical_sector(), 160);
+    EXPECT_EQ(system1->physical_sector() + system1->num_sectors(), vendor1->physical_sector());
+    EXPECT_EQ(vendor1->physical_sector() + vendor1->num_sectors(), system2->physical_sector());
+}
+
+TEST_F(BuilderTest, AddInvalidPartition) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+    Partition* partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(partition, nullptr);
+
+    // Duplicate name.
+    partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+    EXPECT_EQ(partition, nullptr);
+
+    // Empty name.
+    partition = builder->AddPartition("", LP_PARTITION_ATTR_READONLY);
+    EXPECT_EQ(partition, nullptr);
+}
+
+TEST_F(BuilderTest, 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", LP_PARTITION_ATTR_READONLY);
+    Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    ASSERT_NE(vendor, nullptr);
+    EXPECT_EQ(builder->ResizePartition(system, 65536), true);
+    EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);
+    EXPECT_EQ(builder->ResizePartition(system, 98304), true);
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    EXPECT_NE(exported, nullptr);
+
+    auto super_device = GetMetadataSuperBlockDevice(*exported.get());
+    ASSERT_NE(super_device, 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(super_device->first_logical_sector, 32);
+
+    static const size_t kMetadataSpace =
+            ((kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE) * 2;
+    EXPECT_GE(super_device->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);
+        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_F(BuilderTest, BuilderImport) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+    Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    ASSERT_NE(vendor, nullptr);
+    EXPECT_EQ(builder->ResizePartition(system, 65536), true);
+    EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);
+    EXPECT_EQ(builder->ResizePartition(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->attributes(), LP_PARTITION_ATTR_READONLY);
+    EXPECT_EQ(vendor->size(), 32768);
+    ASSERT_EQ(vendor->extents().size(), 1);
+    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(), 32);
+    EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);
+    EXPECT_EQ(system2->physical_sector(), 224);
+    EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
+}
+
+TEST_F(BuilderTest, ExportNameTooLong) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+    std::string name = "abcdefghijklmnopqrstuvwxyz0123456789";
+    Partition* system = builder->AddPartition(name + name, LP_PARTITION_ATTR_READONLY);
+    EXPECT_NE(system, nullptr);
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    EXPECT_EQ(exported, nullptr);
+}
+
+TEST_F(BuilderTest, MetadataTooLarge) {
+    static const size_t kDiskSize = 128 * 1024;
+    static const size_t kMetadataSize = 64 * 1024;
+
+    // No space to store metadata + geometry.
+    BlockDeviceInfo device_info("super", kDiskSize, 0, 0, 4096);
+    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_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2);
+    builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
+    EXPECT_EQ(builder, nullptr);
+
+    // Space for metadata + geometry + one free block.
+    device_info.size += device_info.logical_block_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_F(BuilderTest, block_device_info) {
+    PartitionOpener opener;
+
+    BlockDeviceInfo device_info;
+    ASSERT_TRUE(opener.GetInfo(fs_mgr_get_super_partition_name(), &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);
+    ASSERT_EQ(device_info.logical_block_size % LP_SECTOR_SIZE, 0);
+
+    // Having an alignment offset > alignment doesn't really make sense.
+    ASSERT_LT(device_info.alignment_offset, device_info.alignment);
+}
+
+TEST_F(BuilderTest, UpdateBlockDeviceInfo) {
+    BlockDeviceInfo device_info("super", 1024 * 1024, 4096, 1024, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+
+    BlockDeviceInfo new_info;
+    ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
+
+    EXPECT_EQ(new_info.size, device_info.size);
+    EXPECT_EQ(new_info.alignment, device_info.alignment);
+    EXPECT_EQ(new_info.alignment_offset, device_info.alignment_offset);
+    EXPECT_EQ(new_info.logical_block_size, device_info.logical_block_size);
+
+    device_info.alignment = 0;
+    device_info.alignment_offset = 2048;
+    ASSERT_TRUE(builder->UpdateBlockDeviceInfo("super", device_info));
+    ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
+    EXPECT_EQ(new_info.alignment, 4096);
+    EXPECT_EQ(new_info.alignment_offset, device_info.alignment_offset);
+
+    device_info.alignment = 8192;
+    device_info.alignment_offset = 0;
+    ASSERT_TRUE(builder->UpdateBlockDeviceInfo("super", device_info));
+    ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
+    EXPECT_EQ(new_info.alignment, 8192);
+    EXPECT_EQ(new_info.alignment_offset, 2048);
+
+    new_info.size += 4096;
+    ASSERT_FALSE(builder->UpdateBlockDeviceInfo("super", new_info));
+    ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
+    EXPECT_EQ(new_info.size, 1024 * 1024);
+
+    new_info.logical_block_size = 512;
+    ASSERT_TRUE(builder->UpdateBlockDeviceInfo("super", new_info));
+    ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
+    EXPECT_EQ(new_info.logical_block_size, 4096);
+
+    new_info.logical_block_size = 7;
+    ASSERT_FALSE(builder->UpdateBlockDeviceInfo("super", new_info));
+    ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
+    EXPECT_EQ(new_info.logical_block_size, 4096);
+}
+
+TEST_F(BuilderTest, InvalidBlockSize) {
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 513);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    EXPECT_EQ(builder, nullptr);
+}
+
+TEST_F(BuilderTest, AlignedExtentSize) {
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+
+    Partition* partition = builder->AddPartition("system", 0);
+    ASSERT_NE(partition, nullptr);
+    ASSERT_TRUE(builder->ResizePartition(partition, 512));
+    EXPECT_EQ(partition->size(), 4096);
+}
+
+TEST_F(BuilderTest, AlignedFreeSpace) {
+    // Only one sector free - at least one block is required.
+    BlockDeviceInfo device_info("super", 10240, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 512, 1);
+    ASSERT_EQ(builder, nullptr);
+}
+
+TEST_F(BuilderTest, HasDefaultGroup) {
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+
+    EXPECT_FALSE(builder->AddGroup("default", 0));
+}
+
+TEST_F(BuilderTest, GroupSizeLimits) {
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+
+    ASSERT_TRUE(builder->AddGroup("google", 16384));
+
+    Partition* partition = builder->AddPartition("system", "google", 0);
+    ASSERT_NE(partition, nullptr);
+    EXPECT_TRUE(builder->ResizePartition(partition, 8192));
+    EXPECT_EQ(partition->size(), 8192);
+    EXPECT_TRUE(builder->ResizePartition(partition, 16384));
+    EXPECT_EQ(partition->size(), 16384);
+    EXPECT_FALSE(builder->ResizePartition(partition, 32768));
+    EXPECT_EQ(partition->size(), 16384);
+}
+
+TEST_F(BuilderTest, ListPartitionsInGroup) {
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+
+    ASSERT_TRUE(builder->AddGroup("groupA", 16384));
+    ASSERT_TRUE(builder->AddGroup("groupB", 16384));
+
+    Partition* system = builder->AddPartition("system", "groupA", 0);
+    Partition* vendor = builder->AddPartition("vendor", "groupA", 0);
+    Partition* product = builder->AddPartition("product", "groupB", 0);
+    ASSERT_NE(system, nullptr);
+    ASSERT_NE(vendor, nullptr);
+    ASSERT_NE(product, nullptr);
+
+    auto groupA = builder->ListPartitionsInGroup("groupA");
+    auto groupB = builder->ListPartitionsInGroup("groupB");
+    auto groupC = builder->ListPartitionsInGroup("groupC");
+    ASSERT_THAT(groupA, ElementsAre(system, vendor));
+    ASSERT_THAT(groupB, ElementsAre(product));
+    ASSERT_TRUE(groupC.empty());
+}
+
+TEST_F(BuilderTest, ChangeGroups) {
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+
+    ASSERT_TRUE(builder->AddGroup("groupA", 16384));
+    ASSERT_TRUE(builder->AddGroup("groupB", 32768));
+
+    Partition* system = builder->AddPartition("system", "groupA", 0);
+    Partition* vendor = builder->AddPartition("vendor", "groupB", 0);
+    ASSERT_NE(system, nullptr);
+    ASSERT_NE(vendor, nullptr);
+    ASSERT_NE(builder->Export(), nullptr);
+
+    ASSERT_FALSE(builder->ChangePartitionGroup(system, "groupXYZ"));
+    ASSERT_TRUE(builder->ChangePartitionGroup(system, "groupB"));
+    ASSERT_NE(builder->Export(), nullptr);
+
+    // Violate group constraint by reassigning groups.
+    ASSERT_TRUE(builder->ResizePartition(system, 16384 + 4096));
+    ASSERT_TRUE(builder->ChangePartitionGroup(system, "groupA"));
+    ASSERT_EQ(builder->Export(), nullptr);
+
+    ASSERT_FALSE(builder->ChangeGroupSize("default", 2));
+    ASSERT_FALSE(builder->ChangeGroupSize("unknown", 2));
+    ASSERT_TRUE(builder->ChangeGroupSize("groupA", 32768));
+    ASSERT_NE(builder->Export(), nullptr);
+}
+
+constexpr unsigned long long operator"" _GiB(unsigned long long x) {  // NOLINT
+    return x << 30;
+}
+constexpr unsigned long long operator"" _MiB(unsigned long long x) {  // NOLINT
+    return x << 20;
+}
+
+TEST_F(BuilderTest, RemoveAndAddFirstPartition) {
+    auto builder = MetadataBuilder::New(10_GiB, 65536, 2);
+    ASSERT_NE(nullptr, builder);
+    ASSERT_TRUE(builder->AddGroup("foo_a", 5_GiB));
+    ASSERT_TRUE(builder->AddGroup("foo_b", 5_GiB));
+    android::fs_mgr::Partition* p;
+    p = builder->AddPartition("system_a", "foo_a", 0);
+    ASSERT_TRUE(p && builder->ResizePartition(p, 2_GiB));
+    p = builder->AddPartition("vendor_a", "foo_a", 0);
+    ASSERT_TRUE(p && builder->ResizePartition(p, 1_GiB));
+    p = builder->AddPartition("system_b", "foo_b", 0);
+    ASSERT_TRUE(p && builder->ResizePartition(p, 2_GiB));
+    p = builder->AddPartition("vendor_b", "foo_b", 0);
+    ASSERT_TRUE(p && builder->ResizePartition(p, 1_GiB));
+
+    builder->RemovePartition("system_a");
+    builder->RemovePartition("vendor_a");
+    p = builder->AddPartition("system_a", "foo_a", 0);
+    ASSERT_TRUE(p && builder->ResizePartition(p, 3_GiB));
+    p = builder->AddPartition("vendor_a", "foo_a", 0);
+    ASSERT_TRUE(p && builder->ResizePartition(p, 1_GiB));
+}
+
+TEST_F(BuilderTest, ListGroups) {
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(builder->AddGroup("example", 0));
+
+    std::vector<std::string> groups = builder->ListGroups();
+    ASSERT_THAT(groups, ElementsAre("default", "example"));
+}
+
+TEST_F(BuilderTest, RemoveGroupAndPartitions) {
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(builder->AddGroup("example", 0));
+    ASSERT_NE(builder->AddPartition("system", "default", 0), nullptr);
+    ASSERT_NE(builder->AddPartition("vendor", "example", 0), nullptr);
+
+    builder->RemoveGroupAndPartitions("example");
+    ASSERT_NE(builder->FindPartition("system"), nullptr);
+    ASSERT_EQ(builder->FindPartition("vendor"), nullptr);
+    ASSERT_THAT(builder->ListGroups(), ElementsAre("default"));
+
+    builder->RemoveGroupAndPartitions("default");
+    ASSERT_NE(builder->FindPartition("system"), nullptr);
+}
+
+TEST_F(BuilderTest, MultipleBlockDevices) {
+    std::vector<BlockDeviceInfo> partitions = {
+            BlockDeviceInfo("system_a", 256_MiB, 786432, 229376, 4096),
+            BlockDeviceInfo("vendor_a", 128_MiB, 786432, 753664, 4096),
+            BlockDeviceInfo("product_a", 64_MiB, 786432, 753664, 4096),
+    };
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(partitions, "system_a", 65536, 2);
+    ASSERT_NE(builder, nullptr);
+    EXPECT_EQ(builder->AllocatableSpace(), 467238912);
+
+    // Create a partition that spans 3 devices.
+    Partition* p = builder->AddPartition("system_a", 0);
+    ASSERT_NE(p, nullptr);
+    ASSERT_TRUE(builder->ResizePartition(p, 466976768));
+
+    unique_ptr<LpMetadata> metadata = builder->Export();
+    ASSERT_NE(metadata, nullptr);
+    ASSERT_EQ(metadata->block_devices.size(), 3);
+    EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), "system_a");
+    EXPECT_EQ(metadata->block_devices[0].size, 256_MiB);
+    EXPECT_EQ(metadata->block_devices[0].alignment, 786432);
+    EXPECT_EQ(metadata->block_devices[0].alignment_offset, 229376);
+    EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[1]), "vendor_a");
+    EXPECT_EQ(metadata->block_devices[1].size, 128_MiB);
+    EXPECT_EQ(metadata->block_devices[1].alignment, 786432);
+    EXPECT_EQ(metadata->block_devices[1].alignment_offset, 753664);
+    EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[2]), "product_a");
+    EXPECT_EQ(metadata->block_devices[2].size, 64_MiB);
+    EXPECT_EQ(metadata->block_devices[2].alignment, 786432);
+    EXPECT_EQ(metadata->block_devices[2].alignment_offset, 753664);
+    ASSERT_EQ(metadata->extents.size(), 3);
+    EXPECT_EQ(metadata->extents[0].num_sectors, 522304);
+    EXPECT_EQ(metadata->extents[0].target_type, LP_TARGET_TYPE_LINEAR);
+    EXPECT_EQ(metadata->extents[0].target_data, 1984);
+    EXPECT_EQ(metadata->extents[0].target_source, 0);
+    EXPECT_EQ(metadata->extents[1].num_sectors, 260672);
+    EXPECT_EQ(metadata->extents[1].target_type, LP_TARGET_TYPE_LINEAR);
+    EXPECT_EQ(metadata->extents[1].target_data, 1472);
+    EXPECT_EQ(metadata->extents[1].target_source, 1);
+    EXPECT_EQ(metadata->extents[2].num_sectors, 129088);
+    EXPECT_EQ(metadata->extents[2].target_type, LP_TARGET_TYPE_LINEAR);
+    EXPECT_EQ(metadata->extents[2].target_data, 1472);
+    EXPECT_EQ(metadata->extents[2].target_source, 2);
+}
+
+TEST_F(BuilderTest, ImportPartitionsOk) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+    Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    ASSERT_NE(vendor, nullptr);
+    EXPECT_EQ(builder->ResizePartition(system, 65536), true);
+    EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);
+    EXPECT_EQ(builder->ResizePartition(system, 98304), true);
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+
+    builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+
+    ASSERT_TRUE(builder->ImportPartitions(*exported.get(), {"vendor"}));
+    EXPECT_NE(builder->FindPartition("vendor"), nullptr);
+    EXPECT_EQ(builder->FindPartition("system"), nullptr);
+
+    unique_ptr<LpMetadata> new_metadata = builder->Export();
+    ASSERT_NE(new_metadata, nullptr);
+
+    ASSERT_EQ(exported->partitions.size(), static_cast<size_t>(2));
+    ASSERT_EQ(GetPartitionName(exported->partitions[1]), "vendor");
+    ASSERT_EQ(new_metadata->partitions.size(), static_cast<size_t>(1));
+    ASSERT_EQ(GetPartitionName(new_metadata->partitions[0]), "vendor");
+
+    const LpMetadataExtent& extent_a =
+            exported->extents[exported->partitions[1].first_extent_index];
+    const LpMetadataExtent& extent_b =
+            new_metadata->extents[new_metadata->partitions[0].first_extent_index];
+    EXPECT_EQ(extent_a.num_sectors, extent_b.num_sectors);
+    EXPECT_EQ(extent_a.target_type, extent_b.target_type);
+    EXPECT_EQ(extent_a.target_data, extent_b.target_data);
+    EXPECT_EQ(extent_a.target_source, extent_b.target_source);
+}
+
+TEST_F(BuilderTest, ImportPartitionsFail) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+    Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    ASSERT_NE(vendor, nullptr);
+    EXPECT_EQ(builder->ResizePartition(system, 65536), true);
+    EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);
+    EXPECT_EQ(builder->ResizePartition(system, 98304), true);
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+
+    // Different device size.
+    builder = MetadataBuilder::New(1024 * 2048, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+    EXPECT_FALSE(builder->ImportPartitions(*exported.get(), {"system"}));
+}
+
+TEST_F(BuilderTest, ABExtents) {
+    BlockDeviceInfo device_info("super", 10_GiB, 768 * 1024, 0, 4096);
+
+    // A and B slots should be allocated from separate halves of the partition,
+    // to mitigate allocating too many extents. (b/120433288)
+    MetadataBuilder::OverrideABForTesting(true);
+    auto builder = MetadataBuilder::New(device_info, 65536, 2);
+    ASSERT_NE(builder, nullptr);
+    Partition* system_a = builder->AddPartition("system_a", 0);
+    ASSERT_NE(system_a, nullptr);
+    Partition* system_b = builder->AddPartition("system_b", 0);
+    ASSERT_NE(system_b, nullptr);
+    ASSERT_TRUE(builder->ResizePartition(system_a, 2_GiB));
+    ASSERT_TRUE(builder->ResizePartition(system_b, 2_GiB));
+
+    builder->RemovePartition("system_a");
+    system_a = builder->AddPartition("system_a", 0);
+    ASSERT_NE(system_a, nullptr);
+    ASSERT_TRUE(builder->ResizePartition(system_a, 3_GiB));
+
+    EXPECT_EQ(system_a->extents().size(), static_cast<size_t>(1));
+    EXPECT_EQ(system_b->extents().size(), static_cast<size_t>(1));
+    ASSERT_TRUE(builder->ResizePartition(system_b, 6_GiB));
+    EXPECT_EQ(system_b->extents().size(), static_cast<size_t>(2));
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    ASSERT_EQ(exported->extents.size(), static_cast<size_t>(3));
+    EXPECT_EQ(exported->extents[0].target_data, 10487808);
+    EXPECT_EQ(exported->extents[0].num_sectors, 10483712);
+    EXPECT_EQ(exported->extents[1].target_data, 6292992);
+    EXPECT_EQ(exported->extents[1].num_sectors, 2099200);
+    EXPECT_EQ(exported->extents[2].target_data, 1536);
+    EXPECT_EQ(exported->extents[2].num_sectors, 6291456);
+}
+
+TEST_F(BuilderTest, PartialExtents) {
+    // super has a minimum extent size of 768KiB.
+    BlockDeviceInfo device_info("super", 1_GiB, 768 * 1024, 0, 4096);
+    auto builder = MetadataBuilder::New(device_info, 65536, 1);
+    ASSERT_NE(builder, nullptr);
+    Partition* system = builder->AddPartition("system", 0);
+    ASSERT_NE(system, nullptr);
+    Partition* vendor = builder->AddPartition("vendor", 0);
+    ASSERT_NE(vendor, nullptr);
+    ASSERT_TRUE(builder->ResizePartition(system, device_info.alignment + 4096));
+    ASSERT_TRUE(builder->ResizePartition(vendor, device_info.alignment));
+    ASSERT_EQ(system->size(), device_info.alignment + 4096);
+    ASSERT_EQ(vendor->size(), device_info.alignment);
+
+    ASSERT_TRUE(builder->ResizePartition(system, device_info.alignment * 2));
+    ASSERT_EQ(system->extents().size(), static_cast<size_t>(1));
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    ASSERT_EQ(exported->extents.size(), static_cast<size_t>(2));
+    EXPECT_EQ(exported->extents[0].target_data, 1536);
+    EXPECT_EQ(exported->extents[0].num_sectors, 3072);
+    EXPECT_EQ(exported->extents[1].target_data, 4608);
+    EXPECT_EQ(exported->extents[1].num_sectors, 1536);
+}
+
+TEST_F(BuilderTest, UpdateSuper) {
+    // Build the on-disk metadata that we saw before flashing.
+    auto builder = MetadataBuilder::New(8145338368ULL, 65536, 3);
+    ASSERT_NE(builder, nullptr);
+
+    ASSERT_TRUE(builder->AddGroup("google_dynamic_partitions_a", 4068474880ULL));
+    ASSERT_TRUE(builder->AddGroup("google_dynamic_partitions_b", 4068474880ULL));
+
+    Partition* partition = builder->AddPartition("system_a", "google_dynamic_partitions_a",
+                                                 LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(partition, nullptr);
+    ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 1901568, 3608576));
+
+    partition = builder->AddPartition("vendor_a", "google_dynamic_partitions_a",
+                                      LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(partition, nullptr);
+    ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 1521664, 5510144));
+
+    partition = builder->AddPartition("product_a", "google_dynamic_partitions_a",
+                                      LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(partition, nullptr);
+    ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 3606528, 2048));
+
+    partition = builder->AddPartition("system_b", "google_dynamic_partitions_b",
+                                      LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(partition, nullptr);
+    ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 1901568, 7955456));
+
+    partition = builder->AddPartition("vendor_b", "google_dynamic_partitions_b",
+                                      LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(partition, nullptr);
+    ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 1521664, 9857024));
+
+    partition = builder->AddPartition("product_b", "google_dynamic_partitions_b",
+                                      LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(partition, nullptr);
+    ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 3606528, 11378688));
+
+    auto on_disk = builder->Export();
+    ASSERT_NE(on_disk, nullptr);
+
+    // Build the super_empty from the new build.
+    builder = MetadataBuilder::New(8145338368ULL, 65536, 3);
+    ASSERT_NE(builder, nullptr);
+
+    ASSERT_TRUE(builder->AddGroup("google_dynamic_partitions_a", 4068474880ULL));
+    ASSERT_TRUE(builder->AddGroup("google_dynamic_partitions_b", 4068474880ULL));
+    ASSERT_NE(builder->AddPartition("system_a", "google_dynamic_partitions_a",
+                                    LP_PARTITION_ATTR_READONLY),
+              nullptr);
+    ASSERT_NE(builder->AddPartition("system_b", "google_dynamic_partitions_b",
+                                    LP_PARTITION_ATTR_READONLY),
+              nullptr);
+    ASSERT_NE(builder->AddPartition("vendor_a", "google_dynamic_partitions_a",
+                                    LP_PARTITION_ATTR_READONLY),
+              nullptr);
+    ASSERT_NE(builder->AddPartition("vendor_b", "google_dynamic_partitions_b",
+                                    LP_PARTITION_ATTR_READONLY),
+              nullptr);
+    ASSERT_NE(builder->AddPartition("product_a", "google_dynamic_partitions_a",
+                                    LP_PARTITION_ATTR_READONLY),
+              nullptr);
+    ASSERT_NE(builder->AddPartition("product_b", "google_dynamic_partitions_b",
+                                    LP_PARTITION_ATTR_READONLY),
+              nullptr);
+
+    std::set<std::string> partitions_to_keep{"system_a", "vendor_a", "product_a"};
+    ASSERT_TRUE(builder->ImportPartitions(*on_disk.get(), partitions_to_keep));
+}
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
new file mode 100644
index 0000000..58a88b5
--- /dev/null
+++ b/fs_mgr/liblp/images.cpp
@@ -0,0 +1,455 @@
+/*
+ * 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 "reader.h"
+#include "utility.h"
+#include "writer.h"
+
+namespace android {
+namespace fs_mgr {
+
+using android::base::unique_fd;
+
+#if defined(_WIN32)
+static const int O_NOFOLLOW = 0;
+#endif
+
+std::unique_ptr<LpMetadata> ReadFromImageFile(int fd) {
+    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 nullptr;
+    }
+    if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+        PERROR << __PRETTY_FUNCTION__ << " read failed";
+        return nullptr;
+    }
+    LpMetadataGeometry geometry;
+    if (!ParseGeometry(buffer.get(), &geometry)) {
+        return nullptr;
+    }
+    return ParseMetadata(geometry, fd);
+}
+
+std::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes) {
+    if (bytes < LP_METADATA_GEOMETRY_SIZE) {
+        LERROR << __PRETTY_FUNCTION__ << ": " << bytes << " is smaller than geometry header";
+        return nullptr;
+    }
+
+    LpMetadataGeometry geometry;
+    if (!ParseGeometry(data, &geometry)) {
+        return nullptr;
+    }
+
+    const uint8_t* metadata_buffer =
+            reinterpret_cast<const uint8_t*>(data) + LP_METADATA_GEOMETRY_SIZE;
+    size_t metadata_buffer_size = bytes - LP_METADATA_GEOMETRY_SIZE;
+    return ParseMetadata(geometry, metadata_buffer, metadata_buffer_size);
+}
+
+std::unique_ptr<LpMetadata> ReadFromImageFile(const std::string& image_file) {
+    unique_fd fd = GetControlFileOrOpen(image_file.c_str(), O_RDONLY | O_CLOEXEC);
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << image_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 std::string& file, const LpMetadata& input) {
+    unique_fd fd(open(file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << file;
+        return false;
+    }
+    return WriteToImageFile(fd, input);
+}
+
+ImageBuilder::ImageBuilder(const LpMetadata& metadata, uint32_t block_size,
+                           const std::map<std::string, std::string>& images, bool sparsify)
+    : metadata_(metadata),
+      geometry_(metadata.geometry),
+      block_size_(block_size),
+      sparsify_(sparsify),
+      images_(images) {
+    uint64_t total_size = GetTotalSuperPartitionSize(metadata);
+    if (block_size % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE;
+        return;
+    }
+    if (total_size % block_size != 0) {
+        LERROR << "Device size must be a multiple of the block size, " << block_size;
+        return;
+    }
+    if (metadata.geometry.metadata_max_size % block_size != 0) {
+        LERROR << "Metadata max size must be a multiple of the block size, " << block_size;
+        return;
+    }
+    if (LP_METADATA_GEOMETRY_SIZE % block_size != 0) {
+        LERROR << "Geometry size is not a multiple of the block size, " << block_size;
+        return;
+    }
+    if (LP_PARTITION_RESERVED_BYTES % block_size != 0) {
+        LERROR << "Reserved size is not a multiple of the block size, " << block_size;
+        return;
+    }
+
+    uint64_t num_blocks = total_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;
+    }
+
+    for (const auto& block_device : metadata.block_devices) {
+        SparsePtr file(sparse_file_new(block_size_, block_device.size), sparse_file_destroy);
+        if (!file) {
+            LERROR << "Could not allocate sparse file of size " << block_device.size;
+            return;
+        }
+        device_images_.emplace_back(std::move(file));
+    }
+}
+
+bool ImageBuilder::IsValid() const {
+    return device_images_.size() == metadata_.block_devices.size();
+}
+
+bool ImageBuilder::Export(const std::string& file) {
+    unique_fd fd(open(file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
+    if (fd < 0) {
+        PERROR << "open failed: " << file;
+        return false;
+    }
+    if (device_images_.size() > 1) {
+        LERROR << "Cannot export to a single image on retrofit builds.";
+        return false;
+    }
+    // No gzip compression; no checksum.
+    int ret = sparse_file_write(device_images_[0].get(), fd, false, sparsify_, false);
+    if (ret != 0) {
+        LERROR << "sparse_file_write failed (error code " << ret << ")";
+        return false;
+    }
+    return true;
+}
+
+bool ImageBuilder::ExportFiles(const std::string& output_dir) {
+    for (size_t i = 0; i < device_images_.size(); i++) {
+        std::string name = GetBlockDevicePartitionName(metadata_.block_devices[i]);
+        std::string file_name = "super_" + name + ".img";
+        std::string file_path = output_dir + "/" + file_name;
+
+        static const int kOpenFlags = O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_NOFOLLOW;
+        unique_fd fd(open(file_path.c_str(), kOpenFlags, 0644));
+        if (fd < 0) {
+            PERROR << "open failed: " << file_path;
+            return false;
+        }
+        // No gzip compression; no checksum.
+        int ret = sparse_file_write(device_images_[i].get(), fd, false, sparsify_, false);
+        if (ret != 0) {
+            LERROR << "sparse_file_write failed (error code " << ret << ")";
+            return false;
+        }
+    }
+    return true;
+}
+
+bool ImageBuilder::AddData(sparse_file* file, 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, data, blob.size(), block);
+    if (ret != 0) {
+        LERROR << "sparse_file_add_data failed (error code " << ret << ")";
+        return false;
+    }
+    return true;
+}
+
+bool ImageBuilder::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;
+}
+
+uint64_t ImageBuilder::BlockToSector(uint64_t block) const {
+    return (block * block_size_) / LP_SECTOR_SIZE;
+}
+
+bool ImageBuilder::Build() {
+    if (sparse_file_add_fill(device_images_[0].get(), 0, LP_PARTITION_RESERVED_BYTES, 0) < 0) {
+        LERROR << "Could not add initial sparse block for reserved zeroes";
+        return false;
+    }
+
+    std::string geometry_blob = SerializeGeometry(geometry_);
+    std::string metadata_blob = SerializeMetadata(metadata_);
+    metadata_blob.resize(geometry_.metadata_max_size);
+
+    // Two copies of geometry, then two copies of each metadata slot.
+    all_metadata_ += geometry_blob + geometry_blob;
+    for (size_t i = 0; i < geometry_.metadata_slot_count * 2; i++) {
+        all_metadata_ += metadata_blob;
+    }
+
+    uint64_t first_sector = LP_PARTITION_RESERVED_BYTES / LP_SECTOR_SIZE;
+    if (!AddData(device_images_[0].get(), all_metadata_, first_sector)) {
+        return false;
+    }
+
+    if (!CheckExtentOrdering()) {
+        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;
+    }
+    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 ImageBuilder::AddPartitionImage(const LpMetadataPartition& partition,
+                                     const std::string& file) {
+    // Track which extent we're processing.
+    uint32_t extent_index = partition.first_extent_index;
+
+    const LpMetadataExtent& extent = metadata_.extents[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;
+    }
+    uint64_t partition_size = ComputePartitionSize(partition);
+    if (file_length > partition_size) {
+        LERROR << "Image for partition '" << GetPartitionName(partition)
+               << "' is greater than its size (" << file_length << ", expected " << partition_size
+               << ")";
+        return false;
+    }
+    if (SeekFile64(fd, 0, SEEK_SET)) {
+        PERROR << "lseek failed";
+        return false;
+    }
+
+    // We track the current logical sector and the position the current extent
+    // ends at.
+    uint64_t output_sector = 0;
+    uint64_t extent_last_sector = extent.num_sectors;
+
+    // We also track the output device and the current output block within that
+    // device.
+    uint32_t output_block;
+    if (!SectorToBlock(extent.target_data, &output_block)) {
+        return false;
+    }
+    sparse_file* output_device = device_images_[extent.target_source].get();
+
+    // Proceed to read the file and build sparse images.
+    uint64_t pos = 0;
+    uint64_t remaining = file_length;
+    while (remaining) {
+        // Check if we need to advance to the next extent.
+        if (output_sector == extent_last_sector) {
+            extent_index++;
+            if (extent_index >= partition.first_extent_index + partition.num_extents) {
+                LERROR << "image is larger than extent table";
+                return false;
+            }
+
+            const LpMetadataExtent& extent = metadata_.extents[extent_index];
+            extent_last_sector += extent.num_sectors;
+            output_device = device_images_[extent.target_source].get();
+            if (!SectorToBlock(extent.target_data, &output_block)) {
+                return false;
+            }
+        }
+
+        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(output_device, 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(output_device, 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_sector += block_size_ / LP_SECTOR_SIZE;
+        output_block++;
+    }
+
+    return true;
+}
+
+uint64_t ImageBuilder::ComputePartitionSize(const LpMetadataPartition& partition) const {
+    uint64_t sectors = 0;
+    for (size_t i = 0; i < partition.num_extents; i++) {
+        sectors += metadata_.extents[partition.first_extent_index + i].num_sectors;
+    }
+    return sectors * LP_SECTOR_SIZE;
+}
+
+// For simplicity, we don't allow serializing any configuration: extents must
+// be ordered, such that any extent at position I in the table occurs *before*
+// any extent after position I, for the same block device. We validate that
+// here.
+//
+// Without this, it would be more difficult to find the appropriate extent for
+// an output block. With this guarantee it is a linear walk.
+bool ImageBuilder::CheckExtentOrdering() {
+    std::vector<uint64_t> last_sectors(metadata_.block_devices.size());
+
+    for (const auto& extent : metadata_.extents) {
+        if (extent.target_type != LP_TARGET_TYPE_LINEAR) {
+            LERROR << "Extents must all be type linear.";
+            return false;
+        }
+        if (extent.target_data <= last_sectors[extent.target_source]) {
+            LERROR << "Extents must appear in increasing order.";
+            return false;
+        }
+        if ((extent.num_sectors * LP_SECTOR_SIZE) % block_size_ != 0) {
+            LERROR << "Extents must be aligned to the block size.";
+            return false;
+        }
+        last_sectors[extent.target_source] = extent.target_data;
+    }
+    return true;
+}
+
+int ImageBuilder::OpenImageFile(const std::string& file) {
+    android::base::unique_fd source_fd = GetControlFileOrOpen(file.c_str(), O_RDONLY | O_CLOEXEC);
+    if (source_fd < 0) {
+        PERROR << "open image file failed: " << file;
+        return -1;
+    }
+
+    SparsePtr 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;
+    }
+
+    TemporaryFile tf;
+    if (tf.fd < 0) {
+        PERROR << "make temporary file failed";
+        return -1;
+    }
+
+    // We temporarily unsparse the file, rather than try to merge its chunks.
+    int rv = sparse_file_write(source.get(), tf.fd, false, false, false);
+    if (rv) {
+        LERROR << "sparse_file_write failed with code: " << rv;
+        return -1;
+    }
+    temp_fds_.push_back(android::base::unique_fd(tf.release()));
+    return temp_fds_.back().get();
+}
+
+bool WriteToImageFile(const std::string& file, const LpMetadata& metadata, uint32_t block_size,
+                      const std::map<std::string, std::string>& images, bool sparsify) {
+    ImageBuilder builder(metadata, block_size, images, sparsify);
+    return builder.IsValid() && builder.Build() && builder.Export(file);
+}
+
+bool WriteSplitImageFiles(const std::string& output_dir, const LpMetadata& metadata,
+                          uint32_t block_size, const std::map<std::string, std::string>& images,
+                          bool sparsify) {
+    ImageBuilder builder(metadata, block_size, images, sparsify);
+    return builder.IsValid() && builder.Build() && builder.ExportFiles(output_dir);
+}
+
+}  // 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..a284d2e
--- /dev/null
+++ b/fs_mgr/liblp/images.h
@@ -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 <stdint.h>
+#include <map>
+#include <memory>
+#include <string>
+
+#include <android-base/unique_fd.h>
+#include <liblp/liblp.h>
+#include <sparse/sparse.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);
+
+// We use an object to build the image 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 ImageBuilder {
+  public:
+    ImageBuilder(const LpMetadata& metadata, uint32_t block_size,
+                 const std::map<std::string, std::string>& images, bool sparsify);
+
+    bool Build();
+    bool Export(const std::string& file);
+    bool ExportFiles(const std::string& dir);
+    bool IsValid() const;
+
+    using SparsePtr = std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)>;
+    const std::vector<SparsePtr>& device_images() const { return device_images_; }
+
+  private:
+    bool AddData(sparse_file* file, 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);
+    uint64_t BlockToSector(uint64_t block) const;
+    bool CheckExtentOrdering();
+    uint64_t ComputePartitionSize(const LpMetadataPartition& partition) const;
+
+    const LpMetadata& metadata_;
+    const LpMetadataGeometry& geometry_;
+    uint32_t block_size_;
+    bool sparsify_;
+
+    std::vector<SparsePtr> device_images_;
+    std::string all_metadata_;
+    std::map<std::string, std::string> images_;
+    std::vector<android::base::unique_fd> temp_fds_;
+};
+
+}  // 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..e70c552
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -0,0 +1,357 @@
+//
+// 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 <optional>
+#include <set>
+
+#include "liblp.h"
+#include "partition_opener.h"
+
+namespace android {
+namespace fs_mgr {
+
+class LinearExtent;
+
+// By default, partitions are aligned on a 1MiB boundary.
+static const uint32_t kDefaultPartitionAlignment = 1024 * 1024;
+static const uint32_t kDefaultBlockSize = 4096;
+
+// 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 bool 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, uint32_t device_index, uint64_t physical_sector)
+        : Extent(num_sectors), device_index_(device_index), physical_sector_(physical_sector) {}
+
+    bool AddTo(LpMetadata* metadata) const override;
+    LinearExtent* AsLinearExtent() override { return this; }
+
+    uint64_t physical_sector() const { return physical_sector_; }
+    uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
+    uint32_t device_index() const { return device_index_; }
+
+    bool OwnsSector(uint64_t sector) const {
+        return sector >= physical_sector_ && sector < end_sector();
+    }
+
+  private:
+    uint32_t device_index_;
+    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) {}
+
+    bool AddTo(LpMetadata* out) const override;
+};
+
+class PartitionGroup final {
+    friend class MetadataBuilder;
+
+  public:
+    explicit PartitionGroup(const std::string& name, uint64_t maximum_size)
+        : name_(name), maximum_size_(maximum_size) {}
+
+    const std::string& name() const { return name_; }
+    uint64_t maximum_size() const { return maximum_size_; }
+
+  private:
+    void set_maximum_size(uint64_t maximum_size) { maximum_size_ = maximum_size; }
+
+    std::string name_;
+    uint64_t maximum_size_;
+};
+
+class Partition final {
+    friend class MetadataBuilder;
+
+  public:
+    Partition(const std::string& name, const std::string& group_name, uint32_t attributes);
+
+    // Add a raw extent.
+    void AddExtent(std::unique_ptr<Extent>&& extent);
+
+    // Remove all extents from this partition.
+    void RemoveExtents();
+
+    // Compute the size used by linear extents. This is the same as size(),
+    // but does not factor in extents which do not take up space.
+    uint64_t BytesOnDisk() const;
+
+    const std::string& name() const { return name_; }
+    const std::string& group_name() const { return group_name_; }
+    uint32_t attributes() const { return attributes_; }
+    const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; }
+    uint64_t size() const { return size_; }
+
+  private:
+    void ShrinkTo(uint64_t aligned_size);
+    void set_group_name(const std::string& group_name) { group_name_ = group_name; }
+
+    std::string name_;
+    std::string group_name_;
+    std::vector<std::unique_ptr<Extent>> extents_;
+    uint32_t attributes_;
+    uint64_t size_;
+};
+
+class MetadataBuilder {
+  public:
+    // Construct an empty logical partition table builder given the specified
+    // map of partitions that are available for storing logical partitions.
+    //
+    // At least one partition in the list must be the "super" device, where
+    // metadata will be stored.
+    //
+    // If the parameters would yield invalid metadata, nullptr is returned. This
+    // could happen if the super device is too small to store all required
+    // metadata.
+    static std::unique_ptr<MetadataBuilder> New(const std::vector<BlockDeviceInfo>& block_devices,
+                                                const std::string& super_partition,
+                                                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 IPartitionOpener& opener,
+                                                const std::string& super_partition,
+                                                uint32_t slot_number);
+
+    // Same as above, but use the default PartitionOpener.
+    static std::unique_ptr<MetadataBuilder> New(const std::string& super_partition,
+                                                uint32_t slot_number);
+
+    // This is when performing an A/B update. The source partition must be a
+    // super partition. On a normal device, the metadata for the source slot
+    // is imported and the target slot is ignored. On a retrofit device, the
+    // metadata may not have the target slot's devices listed yet, in which
+    // case, it is automatically upgraded to include all available block
+    // devices.
+    static std::unique_ptr<MetadataBuilder> NewForUpdate(const IPartitionOpener& opener,
+                                                         const std::string& source_partition,
+                                                         uint32_t source_slot_number,
+                                                         uint32_t target_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.
+    //
+    // If an IPartitionOpener is specified, then block device informatiom will
+    // be updated.
+    static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata,
+                                                const IPartitionOpener* opener = nullptr);
+
+    // Helper function for a single super partition, for tests.
+    static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,
+                                                uint32_t metadata_max_size,
+                                                uint32_t metadata_slot_count) {
+        return New({device_info}, device_info.partition_name, metadata_max_size,
+                   metadata_slot_count);
+    }
+
+    // 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(LP_METADATA_DEFAULT_PARTITION_NAME, blockdev_size, 0, 0,
+                                    kDefaultBlockSize);
+        return New(device_info, metadata_max_size, metadata_slot_count);
+    }
+
+    // Used by the test harness to override whether the device is "A/B".
+    static void OverrideABForTesting(bool ab_device);
+
+    // Define a new partition group. By default there is one group called
+    // "default", with an unrestricted size. A non-zero size will restrict the
+    // total space used by all partitions in the group.
+    //
+    // This can fail and return false if the group already exists.
+    bool AddGroup(const std::string& group_name, uint64_t maximum_size);
+
+    // 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& group_name,
+                            uint32_t attributes);
+
+    // Same as AddPartition above, but uses the default partition group which
+    // has no size restrictions.
+    Partition* AddPartition(const std::string& name, 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);
+
+    // Find a group by name. If no group is found, nullptr is returned.
+    PartitionGroup* FindGroup(const std::string& name);
+
+    // Add a predetermined extent to a partition.
+    bool AddLinearExtent(Partition* partition, const std::string& block_device,
+                         uint64_t num_sectors, uint64_t physical_sector);
+
+    // Grow or shrink a partition to the requested size. This size will be
+    // rounded UP to the nearest block (512 bytes).
+    //
+    // When growing a partition, a greedy algorithm is used to find free gaps
+    // in the partition table and allocate them. If not enough space can be
+    // allocated, false is returned, and the parition table will not be
+    // modified.
+    //
+    // Note, this is an in-memory operation, and it does not alter the
+    // underlying filesystem or contents of the partition on disk.
+    bool ResizePartition(Partition* partition, uint64_t requested_size);
+
+    // Return the list of partitions belonging to a group.
+    std::vector<Partition*> ListPartitionsInGroup(const std::string& group_name);
+
+    // Changes a partition's group. Size constraints will not be checked until
+    // the metadata is exported, to avoid errors during potential group and
+    // size shuffling operations. This will return false if the new group does
+    // not exist.
+    bool ChangePartitionGroup(Partition* partition, const std::string& group_name);
+
+    // Changes the size of a partition group. Size constraints will not be
+    // checked until metadata is exported, to avoid errors during group
+    // reshuffling. This will return false if the group does not exist, or if
+    // the group name is "default".
+    bool ChangeGroupSize(const std::string& group_name, uint64_t maximum_size);
+
+    // Amount of space that can be allocated to logical partitions.
+    uint64_t AllocatableSpace() const;
+    uint64_t UsedSpace() const;
+
+    // Return a list of all group names.
+    std::vector<std::string> ListGroups() const;
+
+    // Remove all partitions belonging to a group, then remove the group.
+    void RemoveGroupAndPartitions(const std::string& group_name);
+
+    // Set the LP_METADATA_AUTO_SLOT_SUFFIXING flag.
+    void SetAutoSlotSuffixing();
+
+    // If set, checks for slot suffixes will be ignored internally.
+    void IgnoreSlotSuffixing();
+
+    bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const;
+    bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info);
+
+    // Attempt to preserve the named partitions from an older metadata. If this
+    // is not possible (for example, the block device list has changed) then
+    // false is returned.
+    bool ImportPartitions(const LpMetadata& metadata, const std::set<std::string>& partition_names);
+
+    // Return true if a block device is found, else false.
+    bool HasBlockDevice(const std::string& partition_name) const;
+
+  private:
+    MetadataBuilder();
+    MetadataBuilder(const MetadataBuilder&) = delete;
+    MetadataBuilder(MetadataBuilder&&) = delete;
+    MetadataBuilder& operator=(const MetadataBuilder&) = delete;
+    MetadataBuilder& operator=(MetadataBuilder&&) = delete;
+    bool Init(const std::vector<BlockDeviceInfo>& block_devices, const std::string& super_partition,
+              uint32_t metadata_max_size, uint32_t metadata_slot_count);
+    bool Init(const LpMetadata& metadata);
+    bool GrowPartition(Partition* partition, uint64_t aligned_size);
+    void ShrinkPartition(Partition* partition, uint64_t aligned_size);
+    uint64_t AlignSector(const LpMetadataBlockDevice& device, uint64_t sector) const;
+    uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
+    bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info);
+    bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const;
+    bool ValidatePartitionSizeChange(Partition* partition, uint64_t old_size, uint64_t new_size,
+                                     bool force_check);
+    void ImportExtents(Partition* dest, const LpMetadata& metadata,
+                       const LpMetadataPartition& source);
+    bool ImportPartition(const LpMetadata& metadata, const LpMetadataPartition& source);
+    bool IsABDevice() const;
+    bool IsRetrofitDevice() const;
+    bool ValidatePartitionGroups() const;
+
+    struct Interval {
+        uint32_t device_index;
+        uint64_t start;
+        uint64_t end;
+
+        Interval(uint32_t device_index, uint64_t start, uint64_t end)
+            : device_index(device_index), start(start), end(end) {}
+        uint64_t length() const { return end - start; }
+
+        // Note: the device index is not included in sorting (intervals are
+        // sorted in per-device lists).
+        bool operator<(const Interval& other) const {
+            return (start == other.start) ? end < other.end : start < other.start;
+        }
+    };
+    std::vector<Interval> GetFreeRegions() const;
+    bool IsAnyRegionCovered(const std::vector<Interval>& regions,
+                            const LinearExtent& candidate) const;
+    bool IsAnyRegionAllocated(const LinearExtent& candidate) const;
+    void ExtentsToFreeList(const std::vector<Interval>& extents,
+                           std::vector<Interval>* free_regions) const;
+    std::vector<Interval> PrioritizeSecondHalfOfSuper(const std::vector<Interval>& free_list);
+    std::unique_ptr<LinearExtent> ExtendFinalExtent(Partition* partition,
+                                                    const std::vector<Interval>& free_list,
+                                                    uint64_t sectors_needed) const;
+
+    static bool sABOverrideValue;
+    static bool sABOverrideSet;
+
+    LpMetadataGeometry geometry_;
+    LpMetadataHeader header_;
+    std::vector<std::unique_ptr<Partition>> partitions_;
+    std::vector<std::unique_ptr<PartitionGroup>> groups_;
+    std::vector<LpMetadataBlockDevice> block_devices_;
+    bool auto_slot_suffixing_;
+};
+
+// 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..135a1b3
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -0,0 +1,117 @@
+//
+// 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 <android-base/unique_fd.h>
+
+#include "metadata_format.h"
+#include "partition_opener.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;
+    std::vector<LpMetadataPartitionGroup> groups;
+    std::vector<LpMetadataBlockDevice> block_devices;
+};
+
+// 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 IPartitionOpener& opener, const std::string& super_partition,
+                         const LpMetadata& metadata);
+
+// 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 IPartitionOpener& opener, const std::string& super_partition,
+                          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 IPartitionOpener& opener,
+                                         const std::string& super_partition, uint32_t slot_number);
+
+// Helper functions that use the default PartitionOpener.
+bool FlashPartitionTable(const std::string& super_partition, const LpMetadata& metadata);
+bool UpdatePartitionTable(const std::string& super_partition, const LpMetadata& metadata,
+                          uint32_t slot_number);
+std::unique_ptr<LpMetadata> ReadMetadata(const std::string& super_partition, uint32_t slot_number);
+
+// Read/Write logical partition metadata to an image file, for diagnostics or
+// flashing.
+bool WriteToImageFile(const std::string& file, const LpMetadata& metadata, uint32_t block_size,
+                      const std::map<std::string, std::string>& images, bool sparsify);
+bool WriteToImageFile(const std::string& file, const LpMetadata& metadata);
+std::unique_ptr<LpMetadata> ReadFromImageFile(const std::string& image_file);
+std::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes);
+
+// Similar to WriteToSparseFile, this will generate an image that can be
+// flashed to a device directly. However unlike WriteToSparseFile, it
+// is intended for retrofit devices, and will generate one sparse file per
+// block device (each named super_<name>.img) and placed in the specified
+// output folder.
+bool WriteSplitImageFiles(const std::string& output_dir, const LpMetadata& metadata,
+                          uint32_t block_size, const std::map<std::string, std::string>& images,
+                          bool sparsify);
+
+// Helper to extract safe C++ strings from partition info.
+std::string GetPartitionName(const LpMetadataPartition& partition);
+std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group);
+std::string GetBlockDevicePartitionName(const LpMetadataBlockDevice& block_device);
+
+// Return the block device that houses the super partition metadata; returns
+// null on failure.
+const LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata);
+
+// Return the total size of all partitions comprising the super partition.
+uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata);
+
+// Get the list of block device names required by the given metadata.
+std::vector<std::string> GetBlockDevicePartitionNames(const LpMetadata& metadata);
+
+// Slot suffix helpers.
+uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
+std::string SlotSuffixForSlotNumber(uint32_t slot_number);
+std::string GetPartitionSlotSuffix(const std::string& partition_name);
+
+// Helpers for common functions.
+const LpMetadataPartition* FindPartition(const LpMetadata& metadata, const std::string& name);
+uint64_t GetPartitionSize(const LpMetadata& metadata, const LpMetadataPartition& partition);
+
+}  // 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..8934aaf
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -0,0 +1,342 @@
+/*
+ * 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 10
+#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 (1 << 0)
+
+/* This flag is only intended to be used with super_empty.img and super.img on
+ * retrofit devices. On these devices there are A and B super partitions, and
+ * we don't know ahead of time which slot the image will be applied to.
+ *
+ * If set, the partition name needs a slot suffix applied. The slot suffix is
+ * determined by the metadata slot number (0 = _a, 1 = _b).
+ */
+#define LP_PARTITION_ATTR_SLOT_SUFFIXED (1 << 1)
+
+/* Mask that defines all valid attributes. */
+#define LP_PARTITION_ATTRIBUTE_MASK (LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED)
+
+/* Default name of the physical partition that holds logical partition entries.
+ * The layout of this partition will look like:
+ *
+ *     +--------------------+
+ *     | Disk Geometry      |
+ *     +--------------------+
+ *     | Geometry Backup    |
+ *     +--------------------+
+ *     | Metadata           |
+ *     +--------------------+
+ *     | Backup Metadata    |
+ *     +--------------------+
+ *     | Logical Partitions |
+ *     +--------------------+
+ */
+#define LP_METADATA_DEFAULT_PARTITION_NAME "super"
+
+/* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */
+#define LP_SECTOR_SIZE 512
+
+/* Amount of space reserved at the start of every super partition to avoid
+ * creating an accidental boot sector.
+ */
+#define LP_PARTITION_RESERVED_BYTES 4096
+
+/* This structure is stored at block 0 in the first 4096 bytes of the
+ * partition, and again in the following block. 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: Logical block size. This is the minimal alignment for partition and
+     * extent sizes, and it must be a multiple of LP_SECTOR_SIZE. Note that
+     * this must be equal across all LUNs that comprise the super partition,
+     * and thus this field is stored in the geometry, not per-device.
+     */
+    uint32_t logical_block_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 end of 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;
+    /* 104: Updateable group descriptor. */
+    LpMetadataTableDescriptor groups;
+    /* 116: Block device table. */
+    LpMetadataTableDescriptor block_devices;
+} __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: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
+    uint32_t attributes;
+
+    /* 40: 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;
+
+    /* 44: Number of extents in the partition. Every partition must have at
+     * least one extent.
+     */
+    uint32_t num_extents;
+
+    /* 48: Group this partition belongs to. */
+    uint32_t group_index;
+} __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;
+
+    /* 20: Contents depends on target_type.
+     *
+     * LINEAR: Must be an index into the block devices table.
+     * ZERO: This field must be 0.
+     */
+    uint32_t target_source;
+} __attribute__((packed)) LpMetadataExtent;
+
+/* This struct defines an entry in the groups table. Each group has a maximum
+ * size, and partitions in a group must not exceed that size. There is always
+ * a "default" group of unlimited size, which is used when not using update
+ * groups or when using overlayfs or fastbootd.
+ */
+typedef struct LpMetadataPartitionGroup {
+    /*  0: Name of this group. Any unused characters must be 0. */
+    char name[36];
+
+    /* 36: Flags (see LP_GROUP_*). */
+    uint32_t flags;
+
+    /* 40: Maximum size in bytes. If 0, the group has no maximum size. */
+    uint64_t maximum_size;
+} __attribute__((packed)) LpMetadataPartitionGroup;
+
+/* This flag is only intended to be used with super_empty.img and super.img on
+ * retrofit devices. If set, the group needs a slot suffix to be interpreted
+ * correctly. The suffix is automatically applied by ReadMetadata().
+ */
+#define LP_GROUP_SLOT_SUFFIXED (1 << 0)
+
+/* This struct defines an entry in the block_devices table. There must be at
+ * least one device, and the first device must represent the partition holding
+ * the super metadata.
+ */
+typedef struct LpMetadataBlockDevice {
+    /* 0: First usable sector for allocating logical partitions. this will be
+     * the first sector after the initial geometry blocks, followed by the
+     * space consumed by metadata_max_size*metadata_slot_count*2.
+     */
+    uint64_t first_logical_sector;
+
+    /* 8: 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;
+
+    /* 12: 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;
+
+    /* 16: Block device size, as specified when the metadata was created. This
+     * can be used to verify the geometry against a target device.
+     */
+    uint64_t size;
+
+    /* 24: Partition name in the GPT. Any unused characters must be 0. */
+    char partition_name[36];
+
+    /* 60: Flags (see LP_BLOCK_DEVICE_* flags below). */
+    uint32_t flags;
+} __attribute__((packed)) LpMetadataBlockDevice;
+
+/* This flag is only intended to be used with super_empty.img and super.img on
+ * retrofit devices. On these devices there are A and B super partitions, and
+ * we don't know ahead of time which slot the image will be applied to.
+ *
+ * If set, the block device needs a slot suffix applied before being used with
+ * IPartitionOpener. The slot suffix is determined by the metadata slot number
+ * (0 = _a, 1 = _b).
+ */
+#define LP_BLOCK_DEVICE_SLOT_SUFFIXED (1 << 0)
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LOGICAL_PARTITION_METADATA_FORMAT_H_ */
diff --git a/fs_mgr/liblp/include/liblp/partition_opener.h b/fs_mgr/liblp/include/liblp/partition_opener.h
new file mode 100644
index 0000000..e506bd5
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/partition_opener.h
@@ -0,0 +1,77 @@
+//
+// 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>
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fs_mgr {
+
+struct BlockDeviceInfo {
+    BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0), logical_block_size(0) {}
+    BlockDeviceInfo(const std::string& partition_name, uint64_t size, uint32_t alignment,
+                    uint32_t alignment_offset, uint32_t logical_block_size)
+        : size(size),
+          alignment(alignment),
+          alignment_offset(alignment_offset),
+          logical_block_size(logical_block_size),
+          partition_name(partition_name) {}
+    // 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;
+    // Block size, for aligning extent sizes and partition sizes.
+    uint32_t logical_block_size;
+    // The physical partition name for this block device, as it would appear in
+    // the GPT or under /dev/block/by-name.
+    std::string partition_name;
+};
+
+// Test-friendly interface for interacting with partitions.
+class IPartitionOpener {
+  public:
+    virtual ~IPartitionOpener() = default;
+
+    // Open the given named physical partition with the provided open() flags.
+    // The name can be an absolute path if the full path is already known.
+    virtual android::base::unique_fd Open(const std::string& partition_name, int flags) const = 0;
+
+    // Return block device information about the given named physical partition.
+    // The name can be an absolute path if the full path is already known.
+    virtual bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const = 0;
+};
+
+// Helper class to implement IPartitionOpener. If |partition_name| is not an
+// absolute path, /dev/block/by-name/ will be prepended.
+class PartitionOpener : public IPartitionOpener {
+  public:
+    virtual android::base::unique_fd Open(const std::string& partition_name,
+                                          int flags) const override;
+    virtual bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override;
+};
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
new file mode 100644
index 0000000..fcef1f0
--- /dev/null
+++ b/fs_mgr/liblp/io_test.cpp
@@ -0,0 +1,722 @@
+/*
+ * 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 <fs_mgr.h>
+#include <fstab/fstab.h>
+#include <gtest/gtest.h>
+#include <liblp/builder.h>
+
+#include "images.h"
+#include "reader.h"
+#include "test_partition_opener.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 BlockDeviceInfo kSuperInfo{"super", kDiskSize, 0, 0, 4096};
+
+// 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;
+}
+
+class DefaultPartitionOpener final : public TestPartitionOpener {
+  public:
+    explicit DefaultPartitionOpener(int fd)
+        : TestPartitionOpener({{"super", fd}}, {{"super", kSuperInfo}}) {}
+};
+
+static bool AddDefaultPartitions(MetadataBuilder* builder) {
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_NONE);
+    if (!system) {
+        return false;
+    }
+    return builder->ResizePartition(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 {};
+    }
+
+    DefaultPartitionOpener opener(fd);
+    if (!FlashPartitionTable(opener, "super", *exported.get())) {
+        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);
+
+    DefaultPartitionOpener opener(fd);
+
+    // Verify that we can't read unwritten metadata.
+    ASSERT_EQ(ReadMetadata(opener, "super", 1), nullptr);
+}
+
+// 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 + 4096, 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);
+
+    DefaultPartitionOpener opener(fd);
+
+    EXPECT_FALSE(FlashPartitionTable(opener, "super", *exported.get()));
+}
+
+// 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);
+
+    DefaultPartitionOpener opener(fd);
+
+    // Export and flash.
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    ASSERT_TRUE(FlashPartitionTable(opener, "super", *exported.get()));
+
+    // 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(opener, "super", 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->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(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);
+
+    // Check block devices table.
+    ASSERT_EQ(exported->block_devices.size(), imported->block_devices.size());
+    EXPECT_EQ(exported->block_devices[0].first_logical_sector,
+              imported->block_devices[0].first_logical_sector);
+}
+
+// Test that we can update metadata slots without disturbing others.
+TEST(liblp, UpdateAnyMetadataSlot) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    DefaultPartitionOpener opener(fd);
+
+    unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_EQ(imported->partitions.size(), 1);
+    EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
+
+    // Change the name before writing to the next slot.
+    strncpy(imported->partitions[0].name, "vendor", sizeof(imported->partitions[0].name));
+    ASSERT_TRUE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
+
+    // Read back the original slot, make sure it hasn't changed.
+    imported = ReadMetadata(opener, "super", 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(opener, "super", 1);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_EQ(imported->partitions.size(), 1);
+    EXPECT_EQ(GetPartitionName(imported->partitions[0]), "vendor");
+
+    auto super_device = GetMetadataSuperBlockDevice(*imported.get());
+    ASSERT_NE(super_device, nullptr);
+
+    uint64_t last_sector = super_device->size / LP_SECTOR_SIZE;
+
+    // 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 = super_device->first_logical_sector; i < last_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);
+
+    DefaultPartitionOpener opener(fd);
+
+    // Make sure all slots are filled.
+    unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
+    ASSERT_NE(metadata, nullptr);
+    for (uint32_t i = 1; i < kMetadataSlots; i++) {
+        ASSERT_TRUE(UpdatePartitionTable(opener, "super", *metadata.get(), i));
+    }
+
+    // Verify that we can't read unavailable slots.
+    EXPECT_EQ(ReadMetadata(opener, "super", 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);
+
+    DefaultPartitionOpener opener(fd);
+
+    unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_TRUE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
+
+    imported->geometry.metadata_max_size += LP_SECTOR_SIZE;
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
+
+    imported = ReadMetadata(opener, "super", 0);
+    ASSERT_NE(imported, nullptr);
+    imported->geometry.metadata_slot_count++;
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
+
+    imported = ReadMetadata(opener, "super", 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_EQ(imported->block_devices.size(), 1);
+    imported->block_devices[0].first_logical_sector++;
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
+
+    imported = ReadMetadata(opener, "super", 0);
+    ASSERT_NE(imported, nullptr);
+}
+
+// Test that changing one bit of metadata is enough to break the checksum.
+TEST(liblp, BitFlipGeometry) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    DefaultPartitionOpener opener(fd);
+
+    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(opener, "super", 0);
+    ASSERT_NE(metadata, nullptr);
+    EXPECT_EQ(metadata->geometry.metadata_slot_count, 2);
+}
+
+TEST(liblp, ReadBackupGeometry) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    DefaultPartitionOpener opener(fd);
+
+    char corruption[LP_METADATA_GEOMETRY_SIZE];
+    memset(corruption, 0xff, sizeof(corruption));
+
+    // Corrupt the primary geometry.
+    ASSERT_GE(lseek(fd, GetPrimaryGeometryOffset(), SEEK_SET), 0);
+    ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+    EXPECT_NE(ReadMetadata(opener, "super", 0), nullptr);
+
+    // Corrupt the backup geometry.
+    ASSERT_GE(lseek(fd, GetBackupGeometryOffset(), SEEK_SET), 0);
+    ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+    EXPECT_EQ(ReadMetadata(opener, "super", 0), nullptr);
+}
+
+TEST(liblp, ReadBackupMetadata) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    DefaultPartitionOpener opener(fd);
+
+    unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
+
+    char corruption[kMetadataSize];
+    memset(corruption, 0xff, sizeof(corruption));
+
+    off_t offset = GetPrimaryMetadataOffset(metadata->geometry, 0);
+
+    ASSERT_GE(lseek(fd, offset, SEEK_SET), 0);
+    ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+    EXPECT_NE(ReadMetadata(opener, "super", 0), nullptr);
+
+    offset = GetBackupMetadataOffset(metadata->geometry, 0);
+
+    // Corrupt the backup metadata.
+    ASSERT_GE(lseek(fd, offset, SEEK_SET), 0);
+    ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+    EXPECT_EQ(ReadMetadata(opener, "super", 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 512 bytes of
+    // metadata. By default there is the header, one partition group, and a
+    // block device entry.
+    static const size_t kMaxPartitionTableSize = kMetadataSize - sizeof(LpMetadataHeader) -
+                                                 sizeof(LpMetadataPartitionGroup) -
+                                                 sizeof(LpMetadataBlockDevice);
+    size_t max_partitions = kMaxPartitionTableSize / sizeof(LpMetadataPartition);
+
+    // Add this number of partitions.
+    Partition* partition = nullptr;
+    for (size_t i = 0; i < max_partitions; i++) {
+        partition = builder->AddPartition(to_string(i), LP_PARTITION_ATTR_NONE);
+        ASSERT_NE(partition, nullptr);
+    }
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+
+    unique_fd fd = CreateFakeDisk();
+    ASSERT_GE(fd, 0);
+
+    DefaultPartitionOpener opener(fd);
+
+    // Check that we are able to write our table.
+    ASSERT_TRUE(FlashPartitionTable(opener, "super", *exported.get()));
+
+    // Check that adding one more partition overflows the metadata allotment.
+    partition = builder->AddPartition("final", 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(opener, "super", *exported.get(), 1));
+
+    auto super_device = GetMetadataSuperBlockDevice(*exported.get());
+    ASSERT_NE(super_device, nullptr);
+
+    // 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, super_device->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);
+}
+
+// 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();
+    ASSERT_NE(exported, nullptr);
+
+    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);
+}
+
+// Test that we can read images from buffers.
+TEST(liblp, ImageFilesInMemory) {
+    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()));
+
+    int64_t offset = SeekFile64(fd, 0, SEEK_CUR);
+    ASSERT_GE(offset, 0);
+    ASSERT_EQ(SeekFile64(fd, 0, SEEK_SET), 0);
+
+    size_t bytes = static_cast<size_t>(offset);
+    std::unique_ptr<char[]> buffer = std::make_unique<char[]>(bytes);
+    ASSERT_TRUE(android::base::ReadFully(fd, buffer.get(), bytes));
+    ASSERT_NE(ReadFromImageBlob(buffer.get(), bytes), 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);
+
+    DefaultPartitionOpener opener(fd);
+
+    BadWriter writer;
+
+    // Read and write it back.
+    writer.FailOnWrite(1);
+    unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer));
+
+    // We should still be able to read the backup copy.
+    imported = ReadMetadata(opener, "super", 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(opener, "super", *imported.get(), 0, writer));
+    imported = ReadMetadata(opener, "super", 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);
+
+    DefaultPartitionOpener opener(fd);
+
+    BadWriter writer;
+
+    // Read and write it back.
+    writer.FailOnWrite(2);
+    unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer));
+
+    // We should still be able to read the primary copy.
+    imported = ReadMetadata(opener, "super", 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(opener, "super", *imported.get(), 0, writer));
+    imported = ReadMetadata(opener, "super", 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);
+
+    DefaultPartitionOpener opener(fd);
+
+    BadWriter writer;
+
+    // Change the name of the existing partition.
+    unique_ptr<LpMetadata> new_table = ReadMetadata(opener, "super", 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(opener, "super", *new_table.get(), 0, writer));
+
+    // When we read back, we should get the updated primary copy.
+    unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 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(opener, "super", *new_table.get(), 0, writer));
+
+    imported = ReadMetadata(opener, "super", 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_GE(new_table->partitions.size(), 1);
+    ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));
+}
+
+// Test that writing a sparse image can be read back.
+TEST(liblp, FlashSparseImage) {
+    unique_fd fd = CreateFakeDisk();
+    ASSERT_GE(fd, 0);
+
+    BlockDeviceInfo device_info("super", kDiskSize, 0, 0, 512);
+    unique_ptr<MetadataBuilder> builder =
+            MetadataBuilder::New(device_info, kMetadataSize, kMetadataSlots);
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+
+    // Build the sparse file.
+    ImageBuilder sparse(*exported.get(), 512, {}, true /* sparsify */);
+    ASSERT_TRUE(sparse.IsValid());
+    ASSERT_TRUE(sparse.Build());
+
+    const auto& images = sparse.device_images();
+    ASSERT_EQ(images.size(), static_cast<size_t>(1));
+
+    // Write it to the fake disk.
+    ASSERT_NE(lseek(fd.get(), 0, SEEK_SET), -1);
+    int ret = sparse_file_write(images[0].get(), fd.get(), false, false, false);
+    ASSERT_EQ(ret, 0);
+
+    // Verify that we can read both sets of metadata.
+    LpMetadataGeometry geometry;
+    ASSERT_TRUE(ReadPrimaryGeometry(fd.get(), &geometry));
+    ASSERT_TRUE(ReadBackupGeometry(fd.get(), &geometry));
+    ASSERT_NE(ReadPrimaryMetadata(fd.get(), geometry, 0), nullptr);
+    ASSERT_NE(ReadBackupMetadata(fd.get(), geometry, 0), nullptr);
+}
+
+TEST(liblp, AutoSlotSuffixing) {
+    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+    ASSERT_TRUE(builder->AddGroup("example", 0));
+    builder->SetAutoSlotSuffixing();
+
+    auto fd = CreateFakeDisk();
+    ASSERT_GE(fd, 0);
+
+    // Note: we bind the same fd to both names, since we want to make sure the
+    // exact same bits are getting read back in each test.
+    TestPartitionOpener opener({{"super_a", fd}, {"super_b", fd}},
+                               {{"super_a", kSuperInfo}, {"super_b", kSuperInfo}});
+    auto exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    ASSERT_TRUE(FlashPartitionTable(opener, "super_a", *exported.get()));
+
+    auto metadata = ReadMetadata(opener, "super_b", 1);
+    ASSERT_NE(metadata, nullptr);
+    ASSERT_EQ(metadata->partitions.size(), static_cast<size_t>(1));
+    EXPECT_EQ(GetPartitionName(metadata->partitions[0]), "system_b");
+    ASSERT_EQ(metadata->block_devices.size(), static_cast<size_t>(1));
+    EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), "super_b");
+    ASSERT_EQ(metadata->groups.size(), static_cast<size_t>(2));
+    EXPECT_EQ(GetPartitionGroupName(metadata->groups[0]), "default");
+    EXPECT_EQ(GetPartitionGroupName(metadata->groups[1]), "example_b");
+    EXPECT_EQ(metadata->groups[0].flags, 0);
+    EXPECT_EQ(metadata->groups[1].flags, 0);
+
+    metadata = ReadMetadata(opener, "super_a", 0);
+    ASSERT_NE(metadata, nullptr);
+    ASSERT_EQ(metadata->partitions.size(), static_cast<size_t>(1));
+    EXPECT_EQ(GetPartitionName(metadata->partitions[0]), "system_a");
+    ASSERT_EQ(metadata->block_devices.size(), static_cast<size_t>(1));
+    EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), "super_a");
+    ASSERT_EQ(metadata->groups.size(), static_cast<size_t>(2));
+    EXPECT_EQ(GetPartitionGroupName(metadata->groups[0]), "default");
+    EXPECT_EQ(GetPartitionGroupName(metadata->groups[1]), "example_a");
+    EXPECT_EQ(metadata->groups[0].flags, 0);
+    EXPECT_EQ(metadata->groups[1].flags, 0);
+}
+
+TEST(liblp, UpdateRetrofit) {
+    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+    ASSERT_TRUE(builder->AddGroup("example", 0));
+    builder->SetAutoSlotSuffixing();
+
+    auto fd = CreateFakeDisk();
+    ASSERT_GE(fd, 0);
+
+    // Note: we bind the same fd to both names, since we want to make sure the
+    // exact same bits are getting read back in each test.
+    TestPartitionOpener opener({{"super_a", fd}, {"super_b", fd}},
+                               {{"super_a", kSuperInfo}, {"super_b", kSuperInfo}});
+    auto exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    ASSERT_TRUE(FlashPartitionTable(opener, "super_a", *exported.get()));
+
+    builder = MetadataBuilder::NewForUpdate(opener, "super_a", 0, 1);
+    ASSERT_NE(builder, nullptr);
+    auto updated = builder->Export();
+    ASSERT_NE(updated, nullptr);
+    ASSERT_EQ(updated->block_devices.size(), static_cast<size_t>(1));
+    EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), "super_b");
+    ASSERT_TRUE(updated->groups.empty());
+    ASSERT_TRUE(updated->partitions.empty());
+    ASSERT_TRUE(updated->extents.empty());
+}
+
+TEST(liblp, UpdateNonRetrofit) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    DefaultPartitionOpener opener(fd);
+    auto builder = MetadataBuilder::NewForUpdate(opener, "super", 0, 1);
+    ASSERT_NE(builder, nullptr);
+    auto updated = builder->Export();
+    ASSERT_NE(updated, nullptr);
+    ASSERT_EQ(updated->block_devices.size(), static_cast<size_t>(1));
+    EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), "super");
+}
+
+TEST(liblp, ReadSuperPartition) {
+    auto slot_suffix = fs_mgr_get_slot_suffix();
+    auto slot_number = SlotNumberForSlotSuffix(slot_suffix);
+    auto super_name = fs_mgr_get_super_partition_name(slot_number);
+    auto metadata = ReadMetadata(super_name, slot_number);
+    ASSERT_NE(metadata, nullptr);
+
+    if (!slot_suffix.empty()) {
+        auto other_slot_suffix = fs_mgr_get_other_slot_suffix();
+        auto other_slot_number = SlotNumberForSlotSuffix(other_slot_suffix);
+        auto other_super_name = fs_mgr_get_super_partition_name(other_slot_number);
+        auto other_metadata = ReadMetadata(other_super_name, other_slot_number);
+        ASSERT_NE(other_metadata, nullptr);
+    }
+}
diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp
new file mode 100644
index 0000000..cc4a882
--- /dev/null
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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/partition_opener.h"
+
+#if defined(__linux__)
+#include <linux/fs.h>
+#endif
+#if !defined(_WIN32)
+#include <sys/ioctl.h>
+#endif
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+using android::base::unique_fd;
+
+namespace {
+
+std::string GetPartitionAbsolutePath(const std::string& path) {
+    if (android::base::StartsWith(path, "/")) {
+        return path;
+    }
+    return "/dev/block/by-name/" + path;
+}
+
+bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
+#if defined(__linux__)
+    unique_fd fd = GetControlFileOrOpen(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 on " << block_device;
+        return false;
+    }
+
+    int alignment_offset;
+    if (ioctl(fd, BLKALIGNOFF, &alignment_offset) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "BLKALIGNOFF failed on " << block_device;
+        return false;
+    }
+    // The kernel can return -1 here when misaligned devices are stacked (i.e.
+    // device-mapper).
+    if (alignment_offset == -1) {
+        alignment_offset = 0;
+    }
+
+    int logical_block_size;
+    if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed on " << block_device;
+        return false;
+    }
+
+    device_info->alignment_offset = static_cast<uint32_t>(alignment_offset);
+    device_info->logical_block_size = static_cast<uint32_t>(logical_block_size);
+    device_info->partition_name = android::base::Basename(block_device);
+    return true;
+#else
+    (void)block_device;
+    (void)device_info;
+    LERROR << __PRETTY_FUNCTION__ << ": Not supported on this operating system.";
+    return false;
+#endif
+}
+
+}  // namespace
+
+unique_fd PartitionOpener::Open(const std::string& partition_name, int flags) const {
+    std::string path = GetPartitionAbsolutePath(partition_name);
+    return GetControlFileOrOpen(path.c_str(), flags | O_CLOEXEC);
+}
+
+bool PartitionOpener::GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const {
+    std::string path = GetPartitionAbsolutePath(partition_name);
+    return GetBlockDeviceInfo(path, info);
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
new file mode 100644
index 0000000..dcee6d2d
--- /dev/null
+++ b/fs_mgr/liblp/reader.cpp
@@ -0,0 +1,463 @@
+/*
+ * 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 {
+
+// Helper class for reading descriptors and memory buffers in the same manner.
+class Reader {
+  public:
+    virtual ~Reader(){};
+    virtual bool ReadFully(void* buffer, size_t length) = 0;
+};
+
+class FileReader final : public Reader {
+  public:
+    explicit FileReader(int fd) : fd_(fd) {}
+    bool ReadFully(void* buffer, size_t length) override {
+        return android::base::ReadFully(fd_, buffer, length);
+    }
+
+  private:
+    int fd_;
+};
+
+class MemoryReader final : public Reader {
+  public:
+    MemoryReader(const void* buffer, size_t size)
+        : buffer_(reinterpret_cast<const uint8_t*>(buffer)), size_(size), pos_(0) {}
+    bool ReadFully(void* out, size_t length) override {
+        if (size_ - pos_ < length) {
+            errno = EINVAL;
+            return false;
+        }
+        memcpy(out, buffer_ + pos_, length);
+        pos_ += length;
+        return true;
+    }
+
+  private:
+    const uint8_t* buffer_;
+    size_t size_;
+    size_t pos_;
+};
+
+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;
+    }
+    return true;
+}
+
+bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry) {
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
+    if (SeekFile64(fd, GetPrimaryGeometryOffset(), 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;
+    }
+    return ParseGeometry(buffer.get(), geometry);
+}
+
+bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry) {
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
+    if (SeekFile64(fd, GetBackupGeometryOffset(), SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed";
+        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);
+}
+
+// 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) {
+    if (ReadPrimaryGeometry(fd, geometry)) {
+        return true;
+    }
+    return ReadBackupGeometry(fd, 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) ||
+        !ValidateTableBounds(header, header.groups) ||
+        !ValidateTableBounds(header, header.block_devices)) {
+        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;
+    }
+    if (header.groups.entry_size != sizeof(LpMetadataPartitionGroup)) {
+        LERROR << "Logical partition metadata has invalid group table entry size.";
+        return false;
+    }
+    return true;
+}
+
+// Parse and validate all metadata at the current position in the given file
+// descriptor.
+static std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry,
+                                                 Reader* reader) {
+    // First read and validate the header.
+    std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
+    if (!reader->ReadFully(&metadata->header, sizeof(metadata->header))) {
+        PERROR << __PRETTY_FUNCTION__ << " read " << sizeof(metadata->header) << "bytes failed";
+        return nullptr;
+    }
+    if (!ValidateMetadataHeader(metadata->header)) {
+        return nullptr;
+    }
+    metadata->geometry = geometry;
+
+    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 (!reader->ReadFully(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 < partition.first_extent_index) {
+            LERROR << "Logical partition first_extent_index + num_extents overflowed.";
+            return nullptr;
+        }
+        if (partition.first_extent_index + partition.num_extents > header.extents.num_entries) {
+            LERROR << "Logical partition has invalid extent list.";
+            return nullptr;
+        }
+        if (partition.group_index >= header.groups.num_entries) {
+            LERROR << "Logical partition has invalid group index.";
+            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;
+
+        if (extent.target_type == LP_TARGET_TYPE_LINEAR &&
+            extent.target_source >= header.block_devices.num_entries) {
+            LERROR << "Logical partition extent has invalid block device.";
+            return nullptr;
+        }
+
+        metadata->extents.push_back(extent);
+    }
+
+    cursor = buffer.get() + header.groups.offset;
+    for (size_t i = 0; i < header.groups.num_entries; i++) {
+        LpMetadataPartitionGroup group = {};
+        memcpy(&group, cursor, sizeof(group));
+        cursor += header.groups.entry_size;
+
+        metadata->groups.push_back(group);
+    }
+
+    cursor = buffer.get() + header.block_devices.offset;
+    for (size_t i = 0; i < header.block_devices.num_entries; i++) {
+        LpMetadataBlockDevice device = {};
+        memcpy(&device, cursor, sizeof(device));
+        cursor += header.block_devices.entry_size;
+
+        metadata->block_devices.push_back(device);
+    }
+
+    const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(*metadata.get());
+    if (!super_device) {
+        LERROR << "Metadata does not specify a super device.";
+        return nullptr;
+    }
+
+    // Check that the metadata area and logical partition areas don't overlap.
+    uint64_t metadata_region =
+            GetTotalMetadataSize(geometry.metadata_max_size, geometry.metadata_slot_count);
+    if (metadata_region > super_device->first_logical_sector * LP_SECTOR_SIZE) {
+        LERROR << "Logical partition metadata overlaps with logical partition contents.";
+        return nullptr;
+    }
+    return metadata;
+}
+
+std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer,
+                                          size_t size) {
+    MemoryReader reader(buffer, size);
+    return ParseMetadata(geometry, &reader);
+}
+
+std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, int fd) {
+    FileReader reader(fd);
+    return ParseMetadata(geometry, &reader);
+}
+
+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(geometry, 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_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << offset;
+        return nullptr;
+    }
+    return ParseMetadata(geometry, fd);
+}
+
+namespace {
+
+bool AdjustMetadataForSlot(LpMetadata* metadata, uint32_t slot_number) {
+    std::string slot_suffix = SlotSuffixForSlotNumber(slot_number);
+    for (auto& partition : metadata->partitions) {
+        if (!(partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED)) {
+            continue;
+        }
+        std::string partition_name = GetPartitionName(partition) + slot_suffix;
+        if (partition_name.size() > sizeof(partition.name)) {
+            LERROR << __PRETTY_FUNCTION__ << " partition name too long: " << partition_name;
+            return false;
+        }
+        strncpy(partition.name, partition_name.c_str(), sizeof(partition.name));
+        partition.attributes &= ~LP_PARTITION_ATTR_SLOT_SUFFIXED;
+    }
+    for (auto& block_device : metadata->block_devices) {
+        if (!(block_device.flags & LP_BLOCK_DEVICE_SLOT_SUFFIXED)) {
+            continue;
+        }
+        std::string partition_name = GetBlockDevicePartitionName(block_device) + slot_suffix;
+        if (!UpdateBlockDevicePartitionName(&block_device, partition_name)) {
+            LERROR << __PRETTY_FUNCTION__ << " partition name too long: " << partition_name;
+            return false;
+        }
+        block_device.flags &= ~LP_BLOCK_DEVICE_SLOT_SUFFIXED;
+    }
+    for (auto& group : metadata->groups) {
+        if (!(group.flags & LP_GROUP_SLOT_SUFFIXED)) {
+            continue;
+        }
+        std::string group_name = GetPartitionGroupName(group) + slot_suffix;
+        if (!UpdatePartitionGroupName(&group, group_name)) {
+            LERROR << __PRETTY_FUNCTION__ << " group name too long: " << group_name;
+            return false;
+        }
+        group.flags &= ~LP_GROUP_SLOT_SUFFIXED;
+    }
+    return true;
+}
+
+}  // namespace
+
+std::unique_ptr<LpMetadata> ReadMetadata(const IPartitionOpener& opener,
+                                         const std::string& super_partition, uint32_t slot_number) {
+    android::base::unique_fd fd = opener.Open(super_partition, O_RDONLY);
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
+        return nullptr;
+    }
+
+    LpMetadataGeometry geometry;
+    if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
+        return nullptr;
+    }
+    if (slot_number >= geometry.metadata_slot_count) {
+        LERROR << __PRETTY_FUNCTION__ << " invalid metadata slot number";
+        return nullptr;
+    }
+
+    std::vector<int64_t> offsets = {
+            GetPrimaryMetadataOffset(geometry, slot_number),
+            GetBackupMetadataOffset(geometry, slot_number),
+    };
+    std::unique_ptr<LpMetadata> metadata;
+
+    for (const auto& offset : offsets) {
+        if (SeekFile64(fd, offset, SEEK_SET) < 0) {
+            PERROR << __PRETTY_FUNCTION__ << " lseek failed, offset " << offset;
+            continue;
+        }
+        if ((metadata = ParseMetadata(geometry, fd)) != nullptr) {
+            break;
+        }
+    }
+    if (!metadata || !AdjustMetadataForSlot(metadata.get(), slot_number)) {
+        return nullptr;
+    }
+    return metadata;
+}
+
+std::unique_ptr<LpMetadata> ReadMetadata(const std::string& super_partition, uint32_t slot_number) {
+    return ReadMetadata(PartitionOpener(), super_partition, 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));
+}
+
+std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group) {
+    return NameFromFixedArray(group.name, sizeof(group.name));
+}
+
+std::string GetBlockDevicePartitionName(const LpMetadataBlockDevice& block_device) {
+    return NameFromFixedArray(block_device.partition_name, sizeof(block_device.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..7a2490b
--- /dev/null
+++ b/fs_mgr/liblp/reader.h
@@ -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.
+ */
+
+#ifndef LIBLP_READER_H_
+#define LIBLP_READER_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include <liblp/liblp.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.
+bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry);
+
+// Helper functions for manually reading geometry and metadata.
+std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, int fd);
+std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer,
+                                          size_t size);
+bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry);
+bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry);
+bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry);
+
+// These functions assume a valid geometry and slot number, and do not obey
+// auto-slot-suffixing. They are used for tests and for checking whether
+// the metadata is coherent across primary and backup copies.
+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/test_partition_opener.cpp b/fs_mgr/liblp/test_partition_opener.cpp
new file mode 100644
index 0000000..c796f6c
--- /dev/null
+++ b/fs_mgr/liblp/test_partition_opener.cpp
@@ -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.
+ */
+
+#include "test_partition_opener.h"
+
+#include <errno.h>
+
+namespace android {
+namespace fs_mgr {
+
+using android::base::unique_fd;
+
+TestPartitionOpener::TestPartitionOpener(
+        const std::map<std::string, int>& partition_map,
+        const std::map<std::string, BlockDeviceInfo>& partition_info)
+    : partition_map_(partition_map), partition_info_(partition_info) {}
+
+unique_fd TestPartitionOpener::Open(const std::string& partition_name, int flags) const {
+    auto iter = partition_map_.find(partition_name);
+    if (iter == partition_map_.end()) {
+        errno = ENOENT;
+        return {};
+    }
+    return unique_fd{dup(iter->second)};
+}
+
+bool TestPartitionOpener::GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const {
+    auto iter = partition_info_.find(partition_name);
+    if (iter == partition_info_.end()) {
+        errno = ENOENT;
+        return false;
+    }
+    *info = iter->second;
+    return true;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/test_partition_opener.h b/fs_mgr/liblp/test_partition_opener.h
new file mode 100644
index 0000000..b90fee7
--- /dev/null
+++ b/fs_mgr/liblp/test_partition_opener.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.
+ */
+
+#pragma once
+
+#include <map>
+#include <string>
+
+#include <android-base/unique_fd.h>
+#include <liblp/partition_opener.h>
+
+namespace android {
+namespace fs_mgr {
+
+class TestPartitionOpener : public PartitionOpener {
+  public:
+    explicit TestPartitionOpener(const std::map<std::string, int>& partition_map,
+                                 const std::map<std::string, BlockDeviceInfo>& partition_info = {});
+
+    android::base::unique_fd Open(const std::string& partition_name, int flags) const override;
+    bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override;
+
+  private:
+    std::map<std::string, int> partition_map_;
+    std::map<std::string, BlockDeviceInfo> partition_info_;
+};
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
new file mode 100644
index 0000000..338b525
--- /dev/null
+++ b/fs_mgr/liblp/utility.cpp
@@ -0,0 +1,211 @@
+/*
+ * 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/stat.h>
+#include <unistd.h>
+
+#if defined(__linux__)
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#endif
+
+#include <android-base/file.h>
+#include <ext4_utils/ext4_utils.h>
+#include <openssl/sha.h>
+
+#ifdef __ANDROID__
+#include <cutils/android_get_control_file.h>
+#endif
+
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+bool GetDescriptorSize(int fd, uint64_t* size) {
+#if !defined(_WIN32)
+    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;
+    }
+#endif
+
+    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 GetPrimaryGeometryOffset() {
+    return LP_PARTITION_RESERVED_BYTES;
+}
+
+int64_t GetBackupGeometryOffset() {
+    return GetPrimaryGeometryOffset() + LP_METADATA_GEOMETRY_SIZE;
+}
+
+int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
+    CHECK(slot_number < geometry.metadata_slot_count);
+    int64_t offset = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +
+                     geometry.metadata_max_size * slot_number;
+    return offset;
+}
+
+int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
+    CHECK(slot_number < geometry.metadata_slot_count);
+    int64_t start = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +
+                    int64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
+    return start + int64_t(geometry.metadata_max_size * slot_number);
+}
+
+uint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots) {
+    return LP_PARTITION_RESERVED_BYTES +
+           (LP_METADATA_GEOMETRY_SIZE + metadata_max_size * max_slots) * 2;
+}
+
+const LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata) {
+    if (metadata.block_devices.empty()) {
+        return nullptr;
+    }
+    return &metadata.block_devices[0];
+}
+
+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);
+}
+
+uint32_t SlotNumberForSlotSuffix(const std::string& suffix) {
+    if (suffix.empty() || suffix == "a" || suffix == "_a") {
+        return 0;
+    } else if (suffix == "b" || suffix == "_b") {
+        return 1;
+    } else {
+        LERROR << __PRETTY_FUNCTION__ << "slot '" << suffix
+               << "' does not have a recognized format.";
+        return 0;
+    }
+}
+
+uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata) {
+    uint64_t size = 0;
+    for (const auto& block_device : metadata.block_devices) {
+        size += block_device.size;
+    }
+    return size;
+}
+
+std::vector<std::string> GetBlockDevicePartitionNames(const LpMetadata& metadata) {
+    std::vector<std::string> list;
+    for (const auto& block_device : metadata.block_devices) {
+        list.emplace_back(GetBlockDevicePartitionName(block_device));
+    }
+    return list;
+}
+
+const LpMetadataPartition* FindPartition(const LpMetadata& metadata, const std::string& name) {
+    for (const auto& partition : metadata.partitions) {
+        if (GetPartitionName(partition) == name) {
+            return &partition;
+        }
+    }
+    return nullptr;
+}
+
+uint64_t GetPartitionSize(const LpMetadata& metadata, const LpMetadataPartition& partition) {
+    uint64_t total_size = 0;
+    for (uint32_t i = 0; i < partition.num_extents; i++) {
+        const auto& extent = metadata.extents[partition.first_extent_index + i];
+        total_size += extent.num_sectors * LP_SECTOR_SIZE;
+    }
+    return total_size;
+}
+
+std::string GetPartitionSlotSuffix(const std::string& partition_name) {
+    if (partition_name.size() <= 2) {
+        return "";
+    }
+    std::string suffix = partition_name.substr(partition_name.size() - 2);
+    return (suffix == "_a" || suffix == "_b") ? suffix : "";
+}
+
+std::string SlotSuffixForSlotNumber(uint32_t slot_number) {
+    CHECK(slot_number == 0 || slot_number == 1);
+    return (slot_number == 0) ? "_a" : "_b";
+}
+
+bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name) {
+    if (name.size() > sizeof(device->partition_name)) {
+        return false;
+    }
+    strncpy(device->partition_name, name.c_str(), sizeof(device->partition_name));
+    return true;
+}
+
+bool UpdatePartitionGroupName(LpMetadataPartitionGroup* group, const std::string& name) {
+    if (name.size() > sizeof(group->name)) {
+        return false;
+    }
+    strncpy(group->name, name.c_str(), sizeof(group->name));
+    return true;
+}
+
+bool SetBlockReadonly(int fd, bool readonly) {
+#if defined(__linux__)
+    int val = readonly;
+    return ioctl(fd, BLKROSET, &val) == 0;
+#else
+    (void)fd;
+    (void)readonly;
+    return true;
+#endif
+}
+
+base::unique_fd GetControlFileOrOpen(const char* path, int flags) {
+#if defined(__ANDROID__)
+    int fd = android_get_control_file(path);
+    if (fd >= 0) {
+        int newfd = TEMP_FAILURE_RETRY(dup(fd));
+        if (newfd >= 0) {
+            return base::unique_fd(newfd);
+        }
+        PERROR << "Cannot dup fd for already controlled file: " << path << ", reopening...";
+    }
+#endif
+    return base::unique_fd(open(path, flags));
+}
+
+}  // 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..96f1717
--- /dev/null
+++ b/fs_mgr/liblp/utility.h
@@ -0,0 +1,101 @@
+/*
+ * 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 <android-base/unique_fd.h>
+
+#include "liblp/liblp.h"
+
+#define LP_TAG "[liblp]"
+#define LWARN LOG(WARNING) << LP_TAG
+#define LINFO LOG(INFO) << LP_TAG
+#define LERROR LOG(ERROR) << LP_TAG
+#define PWARNING PLOG(WARNING) << 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 the primary or backup geometry.
+int64_t GetPrimaryGeometryOffset();
+int64_t GetBackupGeometryOffset();
+
+// 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);
+
+// Return the total space at the start of the super partition that must be set
+// aside from headers/metadata and backups.
+uint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots);
+
+// 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;
+}
+
+// Update names from C++ strings.
+bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name);
+bool UpdatePartitionGroupName(LpMetadataPartitionGroup* group, const std::string& name);
+
+// Call BLKROSET ioctl on fd so that fd is readonly / read-writable.
+bool SetBlockReadonly(int fd, bool readonly);
+
+::android::base::unique_fd GetControlFileOrOpen(const char* path, int flags);
+
+}  // 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..15f7fff
--- /dev/null
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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("_a"), 0);
+    EXPECT_EQ(SlotNumberForSlotSuffix("b"), 1);
+    EXPECT_EQ(SlotNumberForSlotSuffix("_b"), 1);
+    EXPECT_EQ(SlotNumberForSlotSuffix("_c"), 0);
+    EXPECT_EQ(SlotNumberForSlotSuffix("_d"), 0);
+}
+
+TEST(liblp, SlotSuffixForSlotNumber) {
+    EXPECT_EQ(SlotSuffixForSlotNumber(0), "_a");
+    EXPECT_EQ(SlotSuffixForSlotNumber(1), "_b");
+}
+
+TEST(liblp, GetMetadataOffset) {
+    LpMetadataGeometry geometry = {LP_METADATA_GEOMETRY_MAGIC,
+                                   sizeof(geometry),
+                                   {0},
+                                   16384,
+                                   4,
+                                   4096};
+    static const uint64_t start = LP_PARTITION_RESERVED_BYTES;
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), start + 8192);
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), start + 8192 + 16384);
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), start + 8192 + 16384 * 2);
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 3), start + 8192 + 16384 * 3);
+
+    static const uint64_t backup_start = start + 8192 + 16384 * 4;
+    EXPECT_EQ(GetBackupMetadataOffset(geometry, 3), backup_start + 16384 * 3);
+    EXPECT_EQ(GetBackupMetadataOffset(geometry, 2), backup_start + 16384 * 2);
+    EXPECT_EQ(GetBackupMetadataOffset(geometry, 1), backup_start + 16384 * 1);
+    EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), backup_start + 16384 * 0);
+}
+
+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);
+}
+
+TEST(liblp, GetPartitionSlotSuffix) {
+    EXPECT_EQ(GetPartitionSlotSuffix("system"), "");
+    EXPECT_EQ(GetPartitionSlotSuffix("_"), "");
+    EXPECT_EQ(GetPartitionSlotSuffix("_a"), "");
+    EXPECT_EQ(GetPartitionSlotSuffix("system_a"), "_a");
+    EXPECT_EQ(GetPartitionSlotSuffix("system_b"), "_b");
+}
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
new file mode 100644
index 0000000..bffcb7e
--- /dev/null
+++ b/fs_mgr/liblp/writer.cpp
@@ -0,0 +1,407 @@
+/*
+ * 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.logical_block_size == g2.logical_block_size;
+}
+
+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));
+    std::string groups(reinterpret_cast<const char*>(metadata.groups.data()),
+                       metadata.groups.size() * sizeof(LpMetadataPartitionGroup));
+    std::string block_devices(reinterpret_cast<const char*>(metadata.block_devices.data()),
+                              metadata.block_devices.size() * sizeof(LpMetadataBlockDevice));
+
+    // Compute positions of tables.
+    header.partitions.offset = 0;
+    header.extents.offset = header.partitions.offset + partitions.size();
+    header.groups.offset = header.extents.offset + extents.size();
+    header.block_devices.offset = header.groups.offset + groups.size();
+    header.tables_size = header.block_devices.offset + block_devices.size();
+
+    // Compute payload checksum.
+    std::string tables = partitions + extents + groups + block_devices;
+    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(const IPartitionOpener& opener, const LpMetadata& metadata,
+                                         const std::string& slot_suffix, std::string* blob) {
+    const LpMetadataGeometry& geometry = metadata.geometry;
+
+    *blob = SerializeMetadata(metadata);
+
+    // Make sure we're writing within the space reserved.
+    if (blob->size() > geometry.metadata_max_size) {
+        LERROR << "Logical partition metadata is too large. " << blob->size() << " > "
+               << geometry.metadata_max_size;
+        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;
+    uint64_t total_reserved = LP_PARTITION_RESERVED_BYTES + reserved_size * 2;
+
+    const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
+    if (!super_device) {
+        LERROR << "Logical partition metadata does not have a super block device.";
+        return false;
+    }
+
+    if (total_reserved > super_device->first_logical_sector * LP_SECTOR_SIZE) {
+        LERROR << "Not enough space to store all logical partition metadata slots.";
+        return false;
+    }
+    for (const auto& block_device : metadata.block_devices) {
+        std::string partition_name = GetBlockDevicePartitionName(block_device);
+        if (block_device.flags & LP_BLOCK_DEVICE_SLOT_SUFFIXED) {
+            if (slot_suffix.empty()) {
+                LERROR << "Block device " << partition_name << " requires a slot suffix,"
+                       << " which could not be derived from the super partition name.";
+                return false;
+            }
+            partition_name += slot_suffix;
+        }
+
+        if ((block_device.first_logical_sector + 1) * LP_SECTOR_SIZE > block_device.size) {
+            LERROR << "Block device " << partition_name << " has invalid first sector "
+                   << block_device.first_logical_sector << " for size " << block_device.size;
+            return false;
+        }
+        BlockDeviceInfo info;
+        if (!opener.GetInfo(partition_name, &info)) {
+            PERROR << partition_name << ": ioctl";
+            return false;
+        }
+        if (info.size != block_device.size) {
+            LERROR << "Block device " << partition_name << " size mismatch (expected"
+                   << block_device.size << ", got " << info.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.
+    uint64_t last_sector = super_device->size / LP_SECTOR_SIZE;
+    for (const auto& extent : metadata.extents) {
+        if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
+            uint64_t physical_sector = extent.target_data;
+            if (physical_sector < super_device->first_logical_sector ||
+                physical_sector + extent.num_sectors > last_sector) {
+                LERROR << "Extent table entry is out of bounds.";
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+// Check that the given region is within metadata bounds.
+static bool ValidateMetadataRegion(const LpMetadata& metadata, uint64_t start, size_t size) {
+    const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
+    if (!super_device) {
+        LERROR << __PRETTY_FUNCTION__ << " could not locate super block device in metadata";
+        return false;
+    }
+    if (start + size >= super_device->first_logical_sector * LP_SECTOR_SIZE) {
+        LERROR << __PRETTY_FUNCTION__ << " write of " << size << " bytes at " << start
+               << " overlaps with logical partition contents";
+        return false;
+    }
+    return true;
+}
+
+static bool WritePrimaryMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
+                                 const std::string& blob,
+                                 const std::function<bool(int, const std::string&)>& writer) {
+    int64_t primary_offset = GetPrimaryMetadataOffset(metadata.geometry, slot_number);
+    if (!ValidateMetadataRegion(metadata, primary_offset, blob.size())) {
+        return false;
+    }
+    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 LpMetadata& metadata, uint32_t slot_number,
+                                const std::string& blob,
+                                const std::function<bool(int, const std::string&)>& writer) {
+    int64_t backup_offset = GetBackupMetadataOffset(metadata.geometry, slot_number);
+    if (!ValidateMetadataRegion(metadata, backup_offset, blob.size())) {
+        return false;
+    }
+    if (SeekFile64(fd, backup_offset, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << backup_offset;
+        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 LpMetadata& metadata, 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 >= metadata.geometry.metadata_slot_count) {
+        LERROR << "Invalid logical partition metadata slot number.";
+        return false;
+    }
+    if (!WritePrimaryMetadata(fd, metadata, slot_number, blob, writer)) {
+        return false;
+    }
+    if (!WriteBackupMetadata(fd, metadata, 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());
+}
+
+#if defined(_WIN32)
+static const int O_SYNC = 0;
+#endif
+
+bool FlashPartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+                         const LpMetadata& metadata) {
+    android::base::unique_fd fd = opener.Open(super_partition, O_RDWR | O_SYNC);
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
+        return false;
+    }
+
+    // This is only used in update_engine and fastbootd, where the super
+    // partition should be specified as a name (or by-name link), and
+    // therefore, we should be able to extract a slot suffix.
+    std::string slot_suffix = GetPartitionSlotSuffix(super_partition);
+
+    // 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(opener, metadata, slot_suffix, &metadata_blob)) {
+        return false;
+    }
+
+    // On retrofit devices, super_partition is system_other and might be set to readonly by
+    // fs_mgr_set_blk_ro(). Unset readonly so that fd can be written to.
+    if (!SetBlockReadonly(fd.get(), false)) {
+        PWARNING << __PRETTY_FUNCTION__ << " BLKROSET 0 failed: " << super_partition;
+    }
+
+    // Write zeroes to the first block.
+    std::string zeroes(LP_PARTITION_RESERVED_BYTES, 0);
+    if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset 0";
+        return false;
+    }
+    if (!android::base::WriteFully(fd, zeroes.data(), zeroes.size())) {
+        PERROR << __PRETTY_FUNCTION__ << " write " << zeroes.size() << " bytes failed";
+        return false;
+    }
+
+    LWARN << "Flashing new logical partition geometry to " << super_partition;
+
+    // Write geometry to the primary and backup locations.
+    std::string blob = SerializeGeometry(metadata.geometry);
+    if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: primary geometry";
+        return false;
+    }
+    if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+        PERROR << __PRETTY_FUNCTION__ << " write " << blob.size() << " bytes failed";
+        return false;
+    }
+    if (SeekFile64(fd, GetBackupGeometryOffset(), SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: backup geometry";
+        return false;
+    }
+    if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+        PERROR << __PRETTY_FUNCTION__ << " backup write " << blob.size() << " bytes failed";
+        return false;
+    }
+
+    bool ok = true;
+    for (size_t i = 0; i < metadata.geometry.metadata_slot_count; i++) {
+        ok &= WriteMetadata(fd, metadata, i, metadata_blob, DefaultWriter);
+    }
+    return ok;
+}
+
+bool FlashPartitionTable(const std::string& super_partition, const LpMetadata& metadata) {
+    return FlashPartitionTable(PartitionOpener(), super_partition, metadata);
+}
+
+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(const IPartitionOpener& opener, const std::string& super_partition,
+                          const LpMetadata& metadata, uint32_t slot_number,
+                          const std::function<bool(int, const std::string&)>& writer) {
+    android::base::unique_fd fd = opener.Open(super_partition, O_RDWR | O_SYNC);
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
+        return false;
+    }
+
+    std::string slot_suffix = SlotSuffixForSlotNumber(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 blob;
+    if (!ValidateAndSerializeMetadata(opener, metadata, slot_suffix, &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(opener, *primary.get(), slot_suffix, &old_blob)) {
+            LERROR << "Error serializing primary metadata to repair corrupted backup";
+            return false;
+        }
+        if (!WriteBackupMetadata(fd, metadata, 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(opener, *backup.get(), slot_suffix, &old_blob)) {
+            LERROR << "Error serializing backup metadata to repair corrupted primary";
+            return false;
+        }
+        if (!WritePrimaryMetadata(fd, metadata, slot_number, old_blob, writer)) {
+            LERROR << "Error writing backup metadata to repair corrupted primary";
+            return false;
+        }
+    }
+
+    // Both copies should now be in sync, so we can continue the update.
+    if (!WriteMetadata(fd, metadata, slot_number, blob, writer)) {
+        return false;
+    }
+
+    LINFO << "Updated logical partition table at slot " << slot_number << " on device "
+          << super_partition;
+    return true;
+}
+
+bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+                          const LpMetadata& metadata, uint32_t slot_number) {
+    return UpdatePartitionTable(opener, super_partition, metadata, slot_number, DefaultWriter);
+}
+
+bool UpdatePartitionTable(const std::string& super_partition, const LpMetadata& metadata,
+                          uint32_t slot_number) {
+    PartitionOpener opener;
+    return UpdatePartitionTable(opener, super_partition, 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..6f1da0f
--- /dev/null
+++ b/fs_mgr/liblp/writer.h
@@ -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.
+ */
+
+#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 UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+                          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..83668e9
--- /dev/null
+++ b/fs_mgr/tests/Android.bp
@@ -0,0 +1,74 @@
+// 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",
+    test_suites: ["device-tests"],
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+    static_libs: [
+        "libfs_mgr",
+        "libfstab",
+    ],
+    srcs: [
+        "file_wait_test.cpp",
+        "fs_mgr_test.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+}
+
+cc_prebuilt_binary {
+    name: "adb-remount-test.sh",
+    srcs: ["adb-remount-test.sh"],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+        windows: {
+            enabled: false,
+        },
+        android: {
+            enabled: false,
+        },
+    },
+    host_supported: true,
+}
+
+sh_test {
+    name: "adb-remount-sh",
+    src: "adb-remount-test.sh",
+    filename: "adb-remount-test.sh",
+    test_suites: ["general-tests"],
+    test_config: "adb-remount-sh.xml",
+}
+
+java_test_host {
+    name: "fs_mgr_vendor_overlay_test",
+
+    srcs:  ["src/**/VendorOverlayHostTest.java"],
+
+    libs: ["tradefed"],
+
+    test_config: "vendor-overlay-test.xml",
+
+    test_suites: ["general-tests"],
+}
diff --git a/fs_mgr/tests/adb-remount-sh.xml b/fs_mgr/tests/adb-remount-sh.xml
new file mode 100644
index 0000000..fa0d63f
--- /dev/null
+++ b/fs_mgr/tests/adb-remount-sh.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<configuration description="Config for adb remount test cases">
+    <option name="test-suite-tag" value="adb-remount" />
+    <!-- This test requires a device, so it's not annotated with a null-device -->
+    <test class="com.android.tradefed.testtype.binary.ExecutableHostTest" >
+        <option name="binary" value="adb-remount-test.sh" />
+        <!-- Increase default timeout as script is quite long -->
+        <option name="per-binary-timeout" value="1h" />
+    </test>
+</configuration>
+
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
new file mode 100755
index 0000000..c2a0f33
--- /dev/null
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -0,0 +1,1564 @@
+#! /bin/bash
+#
+# Divided into four section:
+#
+##  USAGE
+##  Helper Variables
+##  Helper Functions
+##  MAINLINE
+
+##
+##  USAGE
+##
+
+USAGE="USAGE: `basename ${0}` [--help] [--serial <SerialNumber>] [options]
+
+adb remount tests
+
+--help        This help
+--serial      Specify device (must if multiple are present)
+--color       Dress output with highlighting colors
+--print-time  Report the test duration
+
+Conditions:
+ - Must be a userdebug build.
+ - Must be in adb mode.
+ - Also tests overlayfs
+  - Kernel must have overlayfs enabled and patched to support override_creds.
+  - Must have either erofs, squashfs, ext4-dedupe or full partitions.
+  - Minimum expectation system and vender are overlayfs covered partitions.
+"
+
+##
+##  Helper Variables
+##
+
+EMPTY=""
+SPACE=" "
+# A _real_ embedded tab character
+TAB="`echo | tr '\n' '\t'`"
+# A _real_ embedded escape character
+ESCAPE="`echo | tr '\n' '\033'`"
+# A _real_ embedded carriage return character
+CR="`echo | tr '\n' '\r'`"
+GREEN="${ESCAPE}[38;5;40m"
+RED="${ESCAPE}[38;5;196m"
+ORANGE="${ESCAPE}[38;5;255:165:0m"
+BLUE="${ESCAPE}[35m"
+NORMAL="${ESCAPE}[0m"
+TMPDIR=${TMPDIR:-/tmp}
+print_time=false
+start_time=`date +%s`
+ACTIVE_SLOT=
+
+ADB_WAIT=4m
+FASTBOOT_WAIT=2m
+
+##
+##  Helper Functions
+##
+
+[ "USAGE: inFastboot
+
+Returns: true if device is in fastboot mode" ]
+inFastboot() {
+  fastboot devices |
+    if [ -n "${ANDROID_SERIAL}" ]; then
+      grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+    else
+      wc -l | grep '^1$' >/dev/null
+    fi
+}
+
+[ "USAGE: inAdb
+
+Returns: true if device is in adb mode" ]
+inAdb() {
+  adb devices |
+    grep -v -e 'List of devices attached' -e '^$' -e "[${SPACE}${TAB}]recovery\$" |
+    if [ -n "${ANDROID_SERIAL}" ]; then
+      grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+    else
+      wc -l | grep '^1$' >/dev/null
+    fi
+}
+
+[ "USAGE: inRecovery
+
+Returns: true if device is in recovery mode" ]
+inRecovery() {
+  local list="`adb devices |
+              grep -v -e 'List of devices attached' -e '^$'`"
+  if [ -n "${ANDROID_SERIAL}" ]; then
+    echo "${list}" |
+      grep "^${ANDROID_SERIAL}[${SPACE}${TAB}][${SPACE}${TAB}]*recovery\$" >/dev/null
+    return ${?}
+  fi
+  if echo "${list}" | wc -l | grep '^1$' >/dev/null; then
+    echo "${list}" |
+      grep "[${SPACE}${TAB}]recovery\$" >/dev/null
+    return ${?}
+  fi
+  false
+}
+
+[ "USAGE: adb_sh <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
+
+Returns: true if the command succeeded" ]
+adb_sh() {
+  local args=
+  for i in "${@}"; do
+    [ -z "${args}" ] || args="${args} "
+    if [ X"${i}" != X"${i#\'}" ]; then
+      args="${args}${i}"
+    elif [ X"${i}" != X"${i#* }" ]; then
+      args="${args}'${i}'"
+    elif [ X"${i}" != X"${i#*${TAB}}" ]; then
+      args="${args}'${i}'"
+    else
+      args="${args}${i}"
+    fi
+  done
+  adb shell "${args}"
+}
+
+[ "USAGE: adb_date >/dev/stdout
+
+Returns: report device epoch time (suitable for logcat -t)" ]
+adb_date() {
+  adb_sh date +%s.%N </dev/null
+}
+
+[ "USAGE: adb_logcat [arguments] >/dev/stdout
+
+Returns: the logcat output" ]
+adb_logcat() {
+  echo "${RED}[     INFO ]${NORMAL} logcat ${@}" >&2 &&
+  adb logcat "${@}" </dev/null |
+    tr -d '\r' |
+    grep -v 'logd    : logdr: UID=' |
+    sed -e '${/------- beginning of kernel/d}' -e 's/^[0-1][0-9]-[0-3][0-9] //'
+}
+
+[ "USAGE: avc_check >/dev/stderr
+
+Returns: worrisome avc violations" ]
+avc_check() {
+  if ! ${overlayfs_supported:-false}; then
+    return
+  fi
+  local L=`adb_logcat -b all -v brief -d \
+                      -e 'context=u:object_r:unlabeled:s0' 2>/dev/null |
+             sed -n 's/.*avc: //p' |
+             sort -u`
+  if [ -z "${L}" ]; then
+    return
+  fi
+  echo "${ORANGE}[  WARNING ]${NORMAL} unlabeled sepolicy violations:" >&2
+  echo "${L}" |
+    sed 's/^/             /' >&2
+}
+
+[ "USAGE: get_property <prop>
+
+Returns the property value" ]
+get_property() {
+  adb_sh getprop ${1} </dev/null
+}
+
+[ "USAGE: isDebuggable
+
+Returns: true if device is (likely) a debug build" ]
+isDebuggable() {
+  if inAdb && [ 1 != "`get_property ro.debuggable`" ]; then
+    false
+  fi
+}
+
+[ "USAGE: adb_su <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
+
+Returns: true if the command running as root succeeded" ]
+adb_su() {
+  adb_sh su root "${@}"
+}
+
+[ "USAGE: adb_cat <file> >stdout
+
+Returns: content of file to stdout with carriage returns skipped,
+         true of the file exists" ]
+adb_cat() {
+    local OUTPUT="`adb_sh cat ${1} </dev/null 2>&1`"
+    local ret=${?}
+    echo "${OUTPUT}" | tr -d '\r'
+    return ${ret}
+}
+
+[ "USAGE: adb_reboot
+
+Returns: true if the reboot command succeeded" ]
+adb_reboot() {
+  avc_check
+  adb reboot remount-test </dev/null || true
+  sleep 2
+}
+
+[ "USAGE: format_duration [<seconds>|<seconds>s|<minutes>m|<hours>h|<days>d]
+
+human readable output whole seconds, whole minutes or mm:ss" ]
+format_duration() {
+  if [ -z "${1}" ]; then
+    echo unknown
+    return
+  fi
+  local duration="${1}"
+  if [ X"${duration}" != X"${duration%s}" ]; then
+    duration=${duration%s}
+  elif [ X"${duration}" != X"${duration%m}" ]; then
+    duration=`expr ${duration%m} \* 60`
+  elif [ X"${duration}" != X"${duration%h}" ]; then
+    duration=`expr ${duration%h} \* 3600`
+  elif [ X"${duration}" != X"${duration%d}" ]; then
+    duration=`expr ${duration%d} \* 86400`
+  fi
+  local seconds=`expr ${duration} % 60`
+  local minutes=`expr \( ${duration} / 60 \) % 60`
+  local hours=`expr ${duration} / 3600`
+  if [ 0 -eq ${minutes} -a 0 -eq ${hours} ]; then
+    if [ 1 -eq ${duration} ]; then
+      echo 1 second
+      return
+    fi
+    echo ${duration} seconds
+    return
+  elif [ 60 -eq ${duration} ]; then
+    echo 1 minute
+    return
+  elif [ 0 -eq ${seconds} -a 0 -eq ${hours} ]; then
+    echo ${minutes} minutes
+    return
+  fi
+  if [ 0 -eq ${hours} ]; then
+    echo ${minutes}:`expr ${seconds} / 10``expr ${seconds} % 10`
+    return
+  fi
+  echo ${hours}:`expr ${minutes} / 10``expr ${minutes} % 10`:`expr ${seconds} / 10``expr ${seconds} % 10`
+}
+
+[ "USAGE: USB_DEVICE=\`usb_devnum [--next]\`
+
+USB_DEVICE contains cache. Update if system changes.
+
+Returns: the devnum for the USB_SERIAL device" ]
+usb_devnum() {
+  if [ -n "${USB_SERIAL}" ]; then
+    local usb_device=`cat ${USB_SERIAL%/serial}/devnum 2>/dev/null | tr -d ' \t\r\n'`
+    if [ -n "${usb_device}" ]; then
+      USB_DEVICE=dev${usb_device}
+    elif [ -n "${USB_DEVICE}" -a "${1}" ]; then
+      USB_DEVICE=dev`expr ${USB_DEVICE#dev} + 1`
+    fi
+    echo "${USB_DEVICE}"
+  fi
+}
+
+[ "USAGE: adb_wait [timeout]
+
+Returns: waits until the device has returned for adb or optional timeout" ]
+adb_wait() {
+  local start=`date +%s`
+  local duration=
+  local ret
+  if [ -n "${1}" ]; then
+    USB_DEVICE=`usb_devnum --next`
+    duration=`format_duration ${1}`
+    echo -n ". . . waiting ${duration}" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}"
+    timeout --preserve-status --signal=KILL ${1} adb wait-for-device 2>/dev/null
+    ret=${?}
+    echo -n "                                                                             ${CR}"
+  else
+    adb wait-for-device
+    ret=${?}
+  fi
+  USB_DEVICE=`usb_devnum`
+  if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
+    local active_slot=`get_active_slot`
+    if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
+      echo "${ORANGE}[  WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
+    fi
+  fi
+  local end=`date +%s`
+  local diff_time=`expr ${end} - ${start}`
+  local _print_time=${print_time}
+  if [ ${diff_time} -lt 15 ]; then
+    _print_time=false
+  fi
+  diff_time=`format_duration ${diff_time}`
+  if [ "${diff_time}" = "${duration}" ]; then
+    _print_time=false
+  fi
+
+  local reason=
+  if inAdb; then
+    reason=`get_property ro.boot.bootreason`
+  fi
+  case ${reason} in
+    reboot*)
+      reason=
+      ;;
+    ${EMPTY})
+      ;;
+    *)
+      reason=" for boot reason ${reason}"
+      ;;
+  esac
+  if ${_print_time} || [ -n "${reason}" ]; then
+    echo "${BLUE}[     INFO ]${NORMAL} adb wait duration ${diff_time}${reason}"
+  fi >&2
+
+  return ${ret}
+}
+
+[ "USAGE: adb_user > /dev/stdout
+
+Returns: the adb daemon user" ]
+adb_user() {
+  adb_sh echo '${USER}' </dev/null
+}
+
+[ "USAGE: usb_status > stdout 2> stderr
+
+Assumes referenced right after adb_wait or fastboot_wait failued.
+If wait failed, check if device is in adb, recovery or fastboot mode
+and report status strings like  \"(USB stack borken?)\",
+\"(In fastboot mode)\", \"(In recovery mode)\" or \"(in adb mode)\".
+Additional diagnostics may be provided to the stderr output.
+
+Returns: USB status string" ]
+usb_status() {
+  if inFastboot; then
+    echo "(In fastboot mode)"
+  elif inRecovery; then
+    echo "(In recovery mode)"
+  elif inAdb; then
+    echo "(In adb mode `adb_user`)"
+  else
+    echo "(USB stack borken for ${USB_ADDRESS})"
+    USB_DEVICE=`usb_devnum`
+    if [ -n "${USB_DEVICE}" ]; then
+      echo "# lsusb -v -s ${USB_DEVICE#dev}"
+      local D=`lsusb -v -s ${USB_DEVICE#dev} 2>&1`
+      if [ -n "${D}" ]; then
+        echo "${D}"
+      else
+        lsusb -v
+      fi
+    else
+      echo "# lsusb -v (expected device missing)"
+      lsusb -v
+    fi >&2
+  fi
+}
+
+[ "USAGE: fastboot_wait [timeout]
+
+Returns: waits until the device has returned for fastboot or optional timeout" ]
+fastboot_wait() {
+  local ret
+  # fastboot has no wait-for-device, but it does an automatic
+  # wait and requires (even a nonsensical) command to do so.
+  if [ -n "${1}" ]; then
+    USB_DEVICE=`usb_devnum --next`
+    echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}"
+    timeout --preserve-status --signal=KILL ${1} fastboot wait-for-device >/dev/null 2>/dev/null
+    ret=${?}
+    echo -n "                                                                             ${CR}"
+    ( exit ${ret} )
+  else
+    fastboot wait-for-device >/dev/null 2>/dev/null
+  fi ||
+    inFastboot
+  ret=${?}
+  USB_DEVICE=`usb_devnum`
+  if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
+    local active_slot=`get_active_slot`
+    if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
+      echo "${ORANGE}[  WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+    fi >&2
+  fi
+  return ${ret}
+}
+
+[ "USAGE: recovery_wait [timeout]
+
+Returns: waits until the device has returned for recovery or optional timeout" ]
+recovery_wait() {
+  local ret
+  if [ -n "${1}" ]; then
+    USB_DEVICE=`usb_devnum --next`
+    echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}"
+    timeout --preserve-status --signal=KILL ${1} adb wait-for-recovery 2>/dev/null
+    ret=${?}
+    echo -n "                                                                             ${CR}"
+  else
+    adb wait-for-recovery
+    ret=${?}
+  fi
+  USB_DEVICE=`usb_devnum`
+  if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
+    local active_slot=`get_active_slot`
+    if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
+      echo "${ORANGE}[  WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+    fi >&2
+  fi
+  return ${ret}
+}
+
+[ "any_wait [timeout]
+
+Returns: waits until a device has returned or optional timeout" ]
+any_wait() {
+  (
+    adb_wait ${1} &
+    adb_pid=${!}
+    fastboot_wait ${1} &
+    fastboot_pid=${!}
+    recovery_wait ${1} &
+    recovery_pid=${!}
+    wait -n
+    kill "${adb_pid}" "${fastboot_pid}" "${recovery_pid}"
+  ) >/dev/null 2>/dev/null
+  inFastboot || inAdb || inRecovery
+}
+
+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
+      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: adb_root
+
+NB: This can be flakey on devices due to USB state
+
+Returns: true if device in root state" ]
+adb_root() {
+  [ root != "`adb_user`" ] || return 0
+  adb root >/dev/null </dev/null 2>/dev/null
+  sleep 2
+  adb_wait ${ADB_WAIT} &&
+    [ root = "`adb_user`" ]
+}
+
+[ "USAGE: adb_unroot
+
+NB: This can be flakey on devices due to USB state
+
+Returns: true if device in un root state" ]
+adb_unroot() {
+  [ root = "`adb_user`" ] || return 0
+  adb unroot >/dev/null </dev/null 2>/dev/null
+  sleep 2
+  adb_wait ${ADB_WAIT} &&
+    [ root != "`adb_user`" ]
+}
+
+[ "USAGE: fastboot_getvar var expected >/dev/stderr
+
+Returns: true if var output matches expected" ]
+fastboot_getvar() {
+  local O=`fastboot getvar ${1} 2>&1`
+  local ret=${?}
+  O="${O#< waiting for * >?}"
+  O="${O%%?Finished. Total time: *}"
+  if [ 0 -ne ${ret} ]; then
+    echo ${O} >&2
+    false
+    return
+  fi
+  if [ "${O}" != "${O#*FAILED}" ]; then
+    O="${1}: <empty>"
+  fi
+  if [ -n "${2}" -a "${1}: ${2}" != "${O}" ]; then
+    echo "${2} != ${O}"
+    false
+    return
+  fi >&2
+  echo ${O} >&2
+}
+
+[ "USAGE: get_active_slot >/dev/stdout
+
+Returns: with a or b string reporting active slot" ]
+get_active_slot() {
+  if inAdb || inRecovery; then
+    get_property ro.boot.slot_suffix | tr -d _
+  elif inFastboot; then
+    fastboot_getvar current-slot 2>&1 | sed -n 's/current-slot: //p'
+  else
+    false
+  fi
+}
+
+[ "USAGE: restore
+
+Do nothing: should be redefined when necessary.  Called after cleanup.
+
+Returns: reverses configurations" ]
+restore() {
+  true
+}
+
+[ "USAGE: cleanup
+
+Do nothing: should be redefined when necessary
+
+Returns: cleans up any latent resources" ]
+cleanup() {
+  true
+}
+
+[ "USAGE: test_duration >/dev/stderr
+
+Prints the duration of the test
+
+Returns: reports duration" ]
+test_duration() {
+  if ${print_time}; then
+    echo "${BLUE}[     INFO ]${NORMAL} end `date`"
+    [ -n "${start_time}" ] || return
+    end_time=`date +%s`
+    local diff_time=`expr ${end_time} - ${start_time}`
+    echo "${BLUE}[     INFO ]${NORMAL} duration `format_duration ${diff_time}`"
+  fi >&2
+}
+
+[ "USAGE: die [-d|-t <epoch>] [message] >/dev/stderr
+
+If -d, or -t <epoch> argument is supplied, dump logcat.
+
+Returns: exit failure, report status" ]
+die() {
+  if [ X"-d" = X"${1}" ]; then
+    adb_logcat -b all -v nsec -d
+    shift
+  elif [ X"-t" = X"${1}" ]; then
+    if [ -n "${2}" ]; then
+      adb_logcat -b all -v nsec -t ${2}
+    else
+      adb_logcat -b all -v nsec -d
+    fi
+    shift 2
+  fi >&2
+  echo "${RED}[  FAILED  ]${NORMAL} ${@}" >&2
+  cleanup
+  restore
+  test_duration
+  exit 1
+}
+
+[ "USAGE: EXPECT_EQ <lval> <rval> [--warning [message]]
+
+Returns true if (regex) lval matches rval" ]
+EXPECT_EQ() {
+  local lval="${1}"
+  local rval="${2}"
+  shift 2
+  local error=1
+  local prefix="${RED}[    ERROR ]${NORMAL}"
+  if [ X"${1}" = X"--warning" ]; then
+      prefix="${RED}[  WARNING ]${NORMAL}"
+      error=0
+      shift 1
+  fi
+  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 "${prefix} expected \"${lval}\""
+      echo "${prefix} got \"${rval}\"" |
+        sed ': again
+             N
+             s/\(\n\)\([^ ]\)/\1             \2/
+             t again'
+      if [ -n "${*}" ] ; then
+        echo "${prefix} ${*}"
+      fi
+    else
+      echo "${prefix} expected \"${lval}\" got \"${rval}\" ${*}"
+    fi >&2
+    return ${error}
+  fi
+  if [ -n "${*}" ] ; then
+    prefix="${GREEN}[     INFO ]${NORMAL}"
+    if [ X"${lval}" != X"${rval}" ]; then  # we were supplied a regex?
+      if [ `echo ${lval}${rval}${*} | wc -c` -gt 60 -o "${rval}" != "${rval% *}" ]; then
+        echo "${prefix} ok \"${lval}\""
+        echo "       = \"${rval}\"" |
+          sed ': again
+               N
+               s/\(\n\)\([^ ]\)/\1          \2/
+               t again'
+        if [ -n "${*}" ] ; then
+          echo "${prefix} ${*}"
+        fi
+      else
+        echo "${prefix} ok \"${lval}\" = \"${rval}\" ${*}"
+      fi
+    else
+      echo "${prefix} ok \"${lval}\" ${*}"
+    fi >&2
+  fi
+  return 0
+}
+
+[ "USAGE: EXPECT_NE <lval> <rval> [--warning [message]]
+
+Returns true if lval matches rval" ]
+EXPECT_NE() {
+  local lval="${1}"
+  local rval="${2}"
+  shift 2
+  local error=1
+  local prefix="${RED}[    ERROR ]${NORMAL}"
+  if [ X"${1}" = X"--warning" ]; then
+      prefix="${RED}[  WARNING ]${NORMAL}"
+      error=0
+      shift 1
+  fi
+  if [ X"${rval}" = X"${lval}" ]; then
+    echo "${prefix} did not expect \"${lval}\" ${*}" >&2
+    return ${error}
+  fi
+  if [ -n "${*}" ] ; then
+    echo "${prefix} ok \"${lval}\" not \"${rval}\" ${*}" >&2
+  fi
+  return 0
+}
+
+[ "USAGE: check_eq <lval> <rval> [--warning [message]]
+
+Exits if (regex) lval mismatches rval" ]
+check_eq() {
+  local lval="${1}"
+  local rval="${2}"
+  shift 2
+  if [ X"${1}" = X"--warning" ]; then
+      EXPECT_EQ "${lval}" "${rval}" ${*}
+      return
+  fi
+  EXPECT_EQ "${lval}" "${rval}" ||
+    die "${@}"
+}
+
+[ "USAGE: check_ne <lval> <rval> [--warning [message]]
+
+Exits if lval matches rval" ]
+check_ne() {
+  local lval="${1}"
+  local rval="${2}"
+  shift 2
+  if [ X"${1}" = X"--warning" ]; then
+      EXPECT_NE "${lval}" "${rval}" ${*}
+      return
+  fi
+  EXPECT_NE "${lval}" "${rval}" ||
+    die "${@}"
+}
+
+[ "USAGE: skip_administrative_mounts [data] < /proc/mounts
+
+Filters out all administrative (eg: sysfs) mounts uninteresting to the test" ]
+skip_administrative_mounts() {
+  if [ "data" = "${1}" ]; then
+    grep -v " /data "
+  else
+    cat -
+  fi |
+  grep -v \
+    -e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\|bpf\) " \
+    -e "^\(binfmt_misc\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\) " \
+    -e "^\(/data/media\|/dev/block/loop[0-9]*\) " \
+    -e "^rootfs / rootfs rw," \
+    -e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|persist\|metadata\) "
+}
+
+[ "USAGE: skip_unrelated_mounts < /proc/mounts
+
+or output from df
+
+Filters out all apex and vendor override administrative overlay mounts
+uninteresting to the test" ]
+skip_unrelated_mounts() {
+    grep -v "^overlay.* /\(apex\|bionic\|system\|vendor\)/[^ ]" |
+      grep -v "[%] /\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$"
+}
+
+##
+##  MAINLINE
+##
+
+OPTIONS=`getopt --alternative --unquoted \
+                --longoptions help,serial:,colour,color,no-colour,no-color \
+                --longoptions gtest_print_time,print-time \
+                -- "?hs:" ${*}` ||
+  ( echo "${USAGE}" >&2 ; false ) ||
+  die "getopt failure"
+set -- ${OPTIONS}
+
+color=false
+while [ ${#} -gt 0 ]; do
+  case ${1} in
+    -h | --help | -\?)
+      echo "${USAGE}" >&2
+      exit 0
+      ;;
+    -s | --serial)
+      export ANDROID_SERIAL=${2}
+      shift
+      ;;
+    --color | --colour)
+      color=true
+      ;;
+    --no-color | --no-colour)
+      color=false
+      ;;
+    --print-time | --gtest_print_time)
+      print_time=true
+      ;;
+    --)
+      shift
+      break
+      ;;
+    -*)
+      echo "${USAGE}" >&2
+      die "${0}: error unknown option ${1}"
+      ;;
+    *)
+      break
+      ;;
+  esac
+  shift
+done
+if ! ${color}; then
+  GREEN=""
+  RED=""
+  ORANGE=""
+  BLUE=""
+  NORMAL=""
+fi
+
+if ${print_time}; then
+  echo "${BLUE}[     INFO ]${NORMAL}" start `date` >&2
+fi
+
+inFastboot && die "device in fastboot mode"
+inRecovery && die "device in recovery mode"
+if ! inAdb; then
+  echo "${ORANGE}[  WARNING ]${NORMAL} device not in adb mode" >&2
+  adb_wait ${ADB_WAIT}
+fi
+inAdb || die "specified device not in adb mode"
+isDebuggable || die "device not a debug build"
+enforcing=true
+if ! adb_su getenforce </dev/null | grep 'Enforcing' >/dev/null; then
+  echo "${ORANGE}[  WARNING ]${NORMAL} device does not have sepolicy in enforcing mode" >&2
+  enforcing=false
+fi
+
+# Do something.
+
+D=`get_property ro.serialno`
+[ -n "${D}" ] || D=`get_property ro.boot.serialno`
+[ -z "${D}" -o -n "${ANDROID_SERIAL}" ] || ANDROID_SERIAL=${D}
+USB_SERIAL=
+[ -z "${ANDROID_SERIAL}" ] || USB_SERIAL=`find /sys/devices -name serial |
+                                          grep usb |
+                                          xargs grep -l ${ANDROID_SERIAL}`
+USB_ADDRESS=
+if [ -n "${USB_SERIAL}" ]; then
+  USB_ADDRESS=${USB_SERIAL%/serial}
+  USB_ADDRESS=usb${USB_ADDRESS##*/}
+fi
+[ -z "${ANDROID_SERIAL}${USB_ADDRESS}" ] ||
+  USB_DEVICE=`usb_devnum`
+  echo "${BLUE}[     INFO ]${NORMAL}" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} >&2
+BUILD_DESCRIPTION=`get_property ro.build.description`
+[ -z "${BUILD_DESCRIPTION}" ] ||
+  echo "${BLUE}[     INFO ]${NORMAL} ${BUILD_DESCRIPTION}" >&2
+ACTIVE_SLOT=`get_active_slot`
+[ -z "${ACTIVE_SLOT}" ] ||
+  echo "${BLUE}[     INFO ]${NORMAL} active slot is ${ACTIVE_SLOT}" >&2
+
+# Report existing partition sizes
+adb_sh ls -l /dev/block/by-name/ </dev/null 2>/dev/null |
+  sed -n 's@.* \([^ ]*\) -> /dev/block/\([^ ]*\)$@\1 \2@p' |
+  while read name device; do
+    case ${name} in
+      system_[ab] | system | vendor_[ab] | vendor | super | cache)
+        case ${device} in
+          sd*)
+            device=${device%%[0-9]*}/${device}
+            ;;
+        esac
+        size=`adb_su cat /sys/block/${device}/size 2>/dev/null </dev/null` &&
+          size=`expr ${size} / 2` &&
+          echo "${BLUE}[     INFO ]${NORMAL} partition ${name} device ${device} size ${size}K" >&2
+        ;;
+    esac
+  done
+
+# If reboot too soon after fresh flash, could trip device update failure logic
+wait_for_screen
+# Can we test remount -R command?
+overlayfs_supported=true
+if [ "orange" = "`get_property ro.boot.verifiedbootstate`" -a \
+     "2" = "`get_property partition.system.verified`" ]; then
+  restore() {
+    ${overlayfs_supported} || return 0
+    inFastboot &&
+      fastboot reboot &&
+      adb_wait ${ADB_WAIT}
+    inAdb &&
+      adb_root &&
+      adb enable-verity >/dev/null 2>/dev/null &&
+      adb_reboot &&
+      adb_wait ${ADB_WAIT}
+  }
+
+  echo "${GREEN}[ RUN      ]${NORMAL} Testing adb shell su root remount -R command" >&2
+
+  avc_check
+  adb_su remount -R system </dev/null || true
+  sleep 2
+  adb_wait ${ADB_WAIT} ||
+    die "waiting for device after remount -R `usb_status`"
+  if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
+       "2" = "`get_property partition.system.verified`" ]; then
+    die "remount -R command failed"
+  fi
+
+  echo "${GREEN}[       OK ]${NORMAL} adb shell su root remount -R command" >&2
+fi
+
+echo "${GREEN}[ RUN      ]${NORMAL} Testing kernel support for overlayfs" >&2
+
+adb_wait || die "wait for device failed"
+adb_sh ls -d /sys/module/overlay </dev/null >/dev/null 2>/dev/null ||
+  adb_sh grep "nodev${TAB}overlay" /proc/filesystems </dev/null >/dev/null 2>/dev/null &&
+  echo "${GREEN}[       OK ]${NORMAL} overlay module present" >&2 ||
+  (
+    echo "${ORANGE}[  WARNING ]${NORMAL} overlay module not present" >&2 &&
+      false
+  ) ||
+  overlayfs_supported=false
+if ${overlayfs_supported}; then
+  adb_su ls /sys/module/overlay/parameters/override_creds </dev/null >/dev/null 2>/dev/null &&
+    echo "${GREEN}[       OK ]${NORMAL} overlay module supports override_creds" >&2 ||
+    case `adb_sh uname -r </dev/null` in
+      4.[456789].* | 4.[1-9][0-9]* | [56789].*)
+        echo "${ORANGE}[  WARNING ]${NORMAL} overlay module does not support override_creds" >&2 &&
+        overlayfs_supported=false
+        ;;
+      *)
+        echo "${GREEN}[       OK ]${NORMAL} overlay module uses caller's creds" >&2
+        ;;
+    esac
+fi
+
+adb_root ||
+  die "initial setup"
+
+echo "${GREEN}[ RUN      ]${NORMAL} Checking current overlayfs status" >&2
+
+# We can not universally use adb enable-verity to ensure device is
+# in a overlayfs disabled state since it can prevent reboot on
+# devices that remount the physical content rather than overlayfs.
+# So lets do our best to surgically wipe the overlayfs state without
+# having to go through enable-verity transition.
+reboot=false
+OVERLAYFS_BACKING="cache mnt/scratch"
+for d in ${OVERLAYFS_BACKING}; do
+  if adb_sh ls -d /${d}/overlay </dev/null >/dev/null 2>/dev/null; then
+    echo "${ORANGE}[  WARNING ]${NORMAL} /${d}/overlay is setup, surgically wiping" >&2
+    adb_sh rm -rf /${d}/overlay </dev/null ||
+      die "/${d}/overlay wipe"
+    reboot=true
+  fi
+done
+if ${reboot}; then
+  echo "${ORANGE}[  WARNING ]${NORMAL} rebooting before test" >&2
+  adb_reboot &&
+    adb_wait ${ADB_WAIT} ||
+    die "lost device after reboot after wipe `usb_status`"
+  adb_root ||
+    die "lost device after elevation to root after wipe `usb_status`"
+fi
+D=`adb_sh df -k </dev/null` &&
+  H=`echo "${D}" | head -1` &&
+  D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay "` &&
+  echo "${H}" &&
+  echo "${D}" &&
+  echo "${ORANGE}[  WARNING ]${NORMAL} overlays present before setup" >&2 ||
+  echo "${GREEN}[       OK ]${NORMAL} no overlay present before setup" >&2
+overlayfs_needed=true
+D=`adb_sh cat /proc/mounts </dev/null |
+   skip_administrative_mounts data`
+if echo "${D}" | grep /dev/root >/dev/null; then
+  D=`echo / /
+     echo "${D}" | grep -v /dev/root`
+fi
+D=`echo "${D}" | cut -s -d' ' -f1 | sort -u`
+no_dedupe=true
+for d in ${D}; do
+  adb_sh tune2fs -l $d </dev/null 2>&1 |
+    grep "Filesystem features:.*shared_blocks" >/dev/null &&
+  no_dedupe=false
+done
+D=`adb_sh df -k ${D} </dev/null |
+   sed 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@'`
+echo "${D}"
+if [ X"${D}" = X"${D##* 100[%] }" ] && ${no_dedupe} ; then
+  overlayfs_needed=false
+  # if device does not need overlays, then adb enable-verity will brick device
+  restore() {
+    ${overlayfs_supported} || return 0
+    inFastboot &&
+      fastboot reboot &&
+      adb_wait ${ADB_WAIT}
+    inAdb &&
+      adb_wait ${ADB_WAIT}
+  }
+elif ! ${overlayfs_supported}; then
+  die "need overlayfs, but do not have it"
+fi
+
+echo "${GREEN}[ RUN      ]${NORMAL} disable verity" >&2
+
+T=`adb_date`
+H=`adb disable-verity 2>&1`
+err=${?}
+L=
+D="${H%?Now reboot your device for settings to take effect*}"
+if [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ]; then
+  echo "${GREEN}[       OK ]${NORMAL} using overlayfs" >&2
+fi
+if [ ${err} != 0 ]; then
+  echo "${H}"
+  ( [ -n "${L}" ] && echo "${L}" && false ) ||
+  die -t "${T}" "disable-verity"
+fi
+rebooted=false
+if [ X"${D}" != X"${H}" ]; then
+  echo "${H}"
+  if [ X"${D}" != X"${D##*setup failed}" ]; then
+    echo "${ORANGE}[  WARNING ]${NORMAL} overlayfs setup whined" >&2
+  fi
+  D=`adb_sh df -k </dev/null` &&
+    H=`echo "${D}" | head -1` &&
+    D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay " || true` &&
+    [ -z "${D}" ] ||
+    ( echo "${H}" && echo "${D}" && false ) ||
+    die -t ${T} "overlay takeover unexpected at this phase"
+  echo "${GREEN}[     INFO ]${NORMAL} rebooting as requested" >&2
+  L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
+  adb_reboot &&
+    adb_wait ${ADB_WAIT} ||
+    die "lost device after reboot requested `usb_status`"
+  adb_root ||
+    die "lost device after elevation to root `usb_status`"
+  rebooted=true
+  # re-disable verity to see the setup remarks expected
+  T=`adb_date`
+  H=`adb disable-verity 2>&1`
+  err=${?}
+  D="${H%?Now reboot your device for settings to take effect*}"
+  if [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ]; then
+    echo "${GREEN}[       OK ]${NORMAL} using overlayfs" >&2
+  fi
+  if [ ${err} != 0 ]; then
+    T=
+  fi
+fi
+if ${overlayfs_supported} && ${overlayfs_needed} && [ X"${D}" != X"${D##*setup failed}" ]; then
+  echo "${D}"
+  ( [ -n "${L}" ] && echo "${L}" && false ) ||
+  die -t "${T}" "setup for overlay"
+fi
+if [ X"${D}" != X"${D##*Successfully disabled verity}" ]; then
+  echo "${H}"
+  D=`adb_sh df -k </dev/null` &&
+    H=`echo "${D}" | head -1` &&
+    D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay " || true` &&
+    [ -z "${D}" ] ||
+    ( echo "${H}" && echo "${D}" && false ) ||
+    ( [ -n "${L}" ] && echo "${L}" && false ) ||
+    die -t "${T}" "overlay takeover unexpected"
+  [ -n "${L}" ] && echo "${L}"
+  die -t "${T}" "unexpected report of verity being disabled a second time"
+elif ${rebooted}; then
+  echo "${GREEN}[       OK ]${NORMAL} verity already disabled" >&2
+else
+  echo "${ORANGE}[  WARNING ]${NORMAL} verity already disabled" >&2
+fi
+
+echo "${GREEN}[ RUN      ]${NORMAL} remount" >&2
+
+# Feed log with selinux denials as baseline before overlays
+adb_unroot
+adb_sh find /system /vendor </dev/null >/dev/null 2>/dev/null
+adb_root
+
+D=`adb remount 2>&1`
+ret=${?}
+echo "${D}"
+[ ${ret} != 0 ] ||
+  [ X"${D}" = X"${D##*remount failed}" ] ||
+  ( [ -n "${L}" ] && echo "${L}" && false ) ||
+  die -t "${T}" "adb remount failed"
+D=`adb_sh df -k </dev/null` &&
+  H=`echo "${D}" | head -1` &&
+  D=`echo "${D}" | skip_unrelated_mounts | grep "^overlay "` ||
+  ( [ -n "${L}" ] && echo "${L}" && false )
+ret=${?}
+uses_dynamic_scratch=false
+scratch_partition=
+if ${overlayfs_needed}; then
+  if [ ${ret} != 0 ]; then
+    die -t ${T} "overlay takeover failed"
+  fi
+  echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
+   echo "${ORANGE}[  WARNING ]${NORMAL} overlay takeover not complete" >&2
+  scratch_partition=scratch
+  if echo "${D}" | grep " /mnt/scratch" >/dev/null; then
+    echo "${BLUE}[     INFO ]${NORMAL} using ${scratch_partition} dynamic partition for overrides" >&2
+  fi
+  M=`adb_sh cat /proc/mounts </dev/null |
+     sed -n 's@\([^ ]*\) /mnt/scratch \([^ ]*\) .*@\2 on \1@p'`
+  [ -n "${M}" ] &&
+    echo "${BLUE}[     INFO ]${NORMAL} scratch filesystem ${M}"
+  uses_dynamic_scratch=true
+  if [ "${M}" != "${M##*/dev/block/by-name/}" ]; then
+    uses_dynamic_scratch=false
+    scratch_partition="${M##*/dev/block/by-name/}"
+  fi
+  scratch_size=`adb_sh df -k /mnt/scratch </dev/null 2>/dev/null |
+                while read device kblocks used available use mounted on; do
+                  if [ "/mnt/scratch" = "\${mounted}" ]; then
+                    echo \${kblocks}
+                  fi
+                done` &&
+    [ -n "${scratch_size}" ] ||
+    die "scratch size"
+  echo "${BLUE}[     INFO ]${NORMAL} scratch size ${scratch_size}KB" >&2
+  for d in ${OVERLAYFS_BACKING}; do
+    if adb_sh ls -d /${d}/overlay/system/upper </dev/null >/dev/null 2>/dev/null; then
+      echo "${BLUE}[     INFO ]${NORMAL} /${d}/overlay is setup" >&2
+    fi
+  done
+
+  echo "${H}" &&
+    echo "${D}" &&
+    echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
+    die  "overlay takeover after remount"
+  !(adb_sh grep "^overlay " /proc/mounts </dev/null |
+    skip_unrelated_mounts |
+    grep " overlay ro,") ||
+    die "remount overlayfs missed a spot (ro)"
+  D=`adb_sh grep " rw," /proc/mounts </dev/null |
+     skip_administrative_mounts data`
+  if echo "${D}" | grep /dev/root >/dev/null; then
+    D=`echo / /
+       echo "${D}" | grep -v /dev/root`
+  fi
+  D=`echo "${D}" | cut -s -d' ' -f1 | sort -u`
+  bad_rw=false
+  for d in ${D}; do
+    if adb_sh tune2fs -l $d </dev/null 2>&1 |
+       grep "Filesystem features:.*shared_blocks" >/dev/null; then
+      bad_rw=true
+    else
+      d=`adb_sh df -k ${D} </dev/null |
+       sed 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@'`
+      [ X"${d}" = X"${d##* 100[%] }" ] ||
+        bad_rw=true
+    fi
+  done
+  [ -z "${D}" ] ||
+    D=`adb_sh df -k ${D} </dev/null |
+       sed -e 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@' \
+           -e 's/^Filesystem      /Filesystem (rw) /'`
+  [ -z "${D}" ] || echo "${D}"
+  ${bad_rw} && die "remount overlayfs missed a spot (rw)"
+else
+  if [ ${ret} = 0 ]; then
+    die -t ${T} "unexpected overlay takeover"
+  fi
+fi
+
+# Check something.
+
+echo "${GREEN}[ RUN      ]${NORMAL} push content to /system and /vendor" >&2
+
+A="Hello World! $(date)"
+echo "${A}" | adb_sh cat - ">/system/hello"
+echo "${A}" | adb_sh cat - ">/vendor/hello"
+B="`adb_cat /system/hello`" ||
+  die "sytem hello"
+check_eq "${A}" "${B}" /system before reboot
+B="`adb_cat /vendor/hello`" ||
+  die "vendor hello"
+check_eq "${A}" "${B}" /vendor before reboot
+SYSTEM_DEVT=`adb_sh stat --format=%D /system/hello </dev/null`
+VENDOR_DEVT=`adb_sh stat --format=%D /vendor/hello </dev/null`
+SYSTEM_INO=`adb_sh stat --format=%i /system/hello </dev/null`
+VENDOR_INO=`adb_sh stat --format=%i /vendor/hello </dev/null`
+BASE_SYSTEM_DEVT=`adb_sh stat --format=%D /system/bin/stat </dev/null`
+BASE_VENDOR_DEVT=`adb_sh stat --format=%D /vendor/bin/stat </dev/null`
+check_eq "${SYSTEM_DEVT%[0-9a-fA-F][0-9a-fA-F]}" "${VENDOR_DEVT%[0-9a-fA-F][0-9a-fA-F]}" vendor and system devt
+check_ne "${SYSTEM_INO}" "${VENDOR_INO}" vendor and system inode
+if ${overlayfs_needed}; then
+  check_ne "${SYSTEM_DEVT}" "${BASE_SYSTEM_DEVT}" system devt
+  check_ne "${VENDOR_DEVT}" "${BASE_VENDOR_DEVT}" vendor devt
+else
+  check_eq "${SYSTEM_DEVT}" "${BASE_SYSTEM_DEVT}" system devt
+  check_eq "${VENDOR_DEVT}" "${BASE_VENDOR_DEVT}" vendor devt
+fi
+check_ne "${BASE_SYSTEM_DEVT}" "${BASE_VENDOR_DEVT}" --warning system/vendor devt
+[ -n "${SYSTEM_DEVT%[0-9a-fA-F][0-9a-fA-F]}" ] ||
+  die "system devt ${SYSTEM_DEVT} is major 0"
+[ -n "${VENDOR_DEVT%[0-9a-fA-F][0-9a-fA-F]}" ] ||
+  die "vendor devt ${SYSTEM_DEVT} is major 0"
+
+# Download libc.so, append some gargage, push back, and check if the file
+# is updated.
+tempdir="`mktemp -d`"
+cleanup() {
+  rm -rf ${tempdir}
+}
+adb pull /system/lib/bootstrap/libc.so ${tempdir} >/dev/null ||
+  die "pull libc.so from device"
+garbage="`hexdump -n 16 -e '4/4 "%08X" 1 "\n"' /dev/random`"
+echo ${garbage} >> ${tempdir}/libc.so
+adb push ${tempdir}/libc.so /system/lib/bootstrap/libc.so >/dev/null ||
+  die "push libc.so to device"
+adb pull /system/lib/bootstrap/libc.so ${tempdir}/libc.so.fromdevice >/dev/null ||
+  die "pull libc.so from device"
+diff ${tempdir}/libc.so ${tempdir}/libc.so.fromdevice > /dev/null ||
+  die "libc.so differ"
+
+echo "${GREEN}[ RUN      ]${NORMAL} reboot to confirm content persistent" >&2
+
+fixup_from_recovery() {
+  inRecovery || return 1
+  echo "${ORANGE}[    ERROR ]${NORMAL} Device in recovery" >&2
+  adb reboot </dev/null
+  adb_wait ${ADB_WAIT}
+}
+
+adb_reboot &&
+  adb_wait ${ADB_WAIT} ||
+  fixup_from_recovery ||
+  die "reboot after override content added failed `usb_status`"
+
+if ${overlayfs_needed}; then
+  D=`adb_su df -k </dev/null` &&
+    H=`echo "${D}" | head -1` &&
+    D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay "` ||
+    ( echo "${L}" && false ) ||
+    die -d "overlay takeover failed after reboot"
+
+  adb_su sed -n '1,/overlay \/system/p' /proc/mounts </dev/null |
+    skip_administrative_mounts |
+    grep -v ' \(erofs\|squashfs\|ext4\|f2fs\|vfat\) ' &&
+    echo "${ORANGE}[  WARNING ]${NORMAL} overlay takeover after first stage init" >&2 ||
+    echo "${GREEN}[       OK ]${NORMAL} overlay takeover in first stage init" >&2
+fi
+
+if ${enforcing}; then
+  adb_unroot ||
+    die "device not in unroot'd state"
+  B="`adb_cat /vendor/hello 2>&1`"
+  check_eq "cat: /vendor/hello: Permission denied" "${B}" vendor after reboot w/o root
+  echo "${GREEN}[       OK ]${NORMAL} /vendor content correct MAC after reboot" >&2
+  # Feed unprivileged log with selinux denials as a result of overlays
+  wait_for_screen
+  adb_sh find /system /vendor </dev/null >/dev/null 2>/dev/null
+fi
+B="`adb_cat /system/hello`"
+check_eq "${A}" "${B}" /system after reboot
+echo "${GREEN}[       OK ]${NORMAL} /system content remains after reboot" >&2
+# Only root can read vendor if sepolicy permissions are as expected.
+adb_root ||
+  die "adb root"
+B="`adb_cat /vendor/hello`"
+check_eq "${A}" "${B}" vendor after reboot
+echo "${GREEN}[       OK ]${NORMAL} /vendor content remains after reboot" >&2
+
+check_eq "${SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/hello </dev/null`" system devt after reboot
+check_eq "${VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/hello </dev/null`" vendor devt after reboot
+check_eq "${SYSTEM_INO}" "`adb_sh stat --format=%i /system/hello </dev/null`" system inode after reboot
+check_eq "${VENDOR_INO}" "`adb_sh stat --format=%i /vendor/hello </dev/null`" vendor inode after reboot
+check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/bin/stat </dev/null`" base system devt after reboot
+check_eq "${BASE_VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/bin/stat </dev/null`" base system devt after reboot
+check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" devt for su after reboot
+
+# Feed log with selinux denials as a result of overlays
+adb_sh find /system /vendor </dev/null >/dev/null 2>/dev/null
+
+# Check if the updated libc.so is persistent after reboot.
+adb_root &&
+  adb pull /system/lib/bootstrap/libc.so ${tempdir}/libc.so.fromdevice >/dev/null ||
+  die "pull libc.so from device"
+diff ${tempdir}/libc.so ${tempdir}/libc.so.fromdevice > /dev/null || die "libc.so differ"
+rm -rf ${tempdir}
+cleanup() {
+  true
+}
+echo "${GREEN}[       OK ]${NORMAL} /system/lib/bootstrap/libc.so content remains after reboot" >&2
+
+echo "${GREEN}[ RUN      ]${NORMAL} flash vendor, confirm its content disappears" >&2
+
+H=`adb_sh echo '${HOSTNAME}' </dev/null 2>/dev/null`
+is_bootloader_fastboot=false
+# cuttlefish?
+[ X"${H}" != X"${H#vsoc}" ] || is_bootloader_fastboot=true
+is_userspace_fastboot=false
+
+if ! ${is_bootloader_fastboot}; then
+  echo "${ORANGE}[  WARNING ]${NORMAL} does not support fastboot, skipping"
+elif [ -z "${ANDROID_PRODUCT_OUT}" ]; then
+  echo "${ORANGE}[  WARNING ]${NORMAL} build tree not setup, skipping"
+elif [ ! -s "${ANDROID_PRODUCT_OUT}/vendor.img" ]; then
+  echo "${ORANGE}[  WARNING ]${NORMAL} vendor image missing, skipping"
+elif [ "${ANDROID_PRODUCT_OUT}" = "${ANDROID_PRODUCT_OUT%*/${H}}" ]; then
+  echo "${ORANGE}[  WARNING ]${NORMAL} wrong vendor image, skipping"
+elif [ -z "${ANDROID_HOST_OUT}" ]; then
+  echo "${ORANGE}[  WARNING ]${NORMAL} please run lunch, skipping"
+elif ! (
+          adb_cat /vendor/build.prop |
+          cmp -s ${ANDROID_PRODUCT_OUT}/vendor/build.prop
+       ) >/dev/null 2>/dev/null; then
+  echo "${ORANGE}[  WARNING ]${NORMAL} vendor image signature mismatch, skipping"
+else
+  wait_for_screen
+  avc_check
+  adb reboot fastboot </dev/null ||
+    die "fastbootd not supported (wrong adb in path?)"
+  any_wait ${ADB_WAIT} &&
+    inFastboot ||
+    die "reboot into fastboot to flash vendor `usb_status` (bad bootloader?)"
+  fastboot flash vendor ||
+    ( fastboot reboot && false) ||
+    die "fastboot flash vendor"
+  fastboot_getvar is-userspace yes &&
+    is_userspace_fastboot=true
+  if [ -n "${scratch_paritition}" ]; then
+    fastboot_getvar partition-type:${scratch_partition} raw ||
+      ( fastboot reboot && false) ||
+      die "fastboot can not see ${scratch_partition} parameters"
+    if ${uses_dynamic_scratch}; then
+      # check ${scratch_partition} via fastboot
+      fastboot_getvar has-slot:${scratch_partition} no &&
+        fastboot_getvar is-logical:${scratch_partition} yes ||
+        ( fastboot reboot && false) ||
+        die "fastboot can not see ${scratch_partition} parameters"
+    else
+      fastboot_getvar is-logical:${scratch_partition} no ||
+        ( fastboot reboot && false) ||
+        die "fastboot can not see ${scratch_partition} parameters"
+    fi
+    if ! ${uses_dynamic_scratch}; then
+      fastboot reboot-bootloader ||
+        die "Reboot into fastboot"
+    fi
+    if ${uses_dynamic_scratch}; then
+      echo "${BLUE}[     INFO ]${NORMAL} expect fastboot erase ${scratch_partition} to fail" >&2
+      fastboot erase ${scratch_partition} &&
+        ( fastboot reboot || true) &&
+        die "fastboot can erase ${scratch_partition}"
+    fi
+    echo "${BLUE}[     INFO ]${NORMAL} expect fastboot format ${scratch_partition} to fail" >&2
+    fastboot format ${scratch_partition} &&
+      ( fastboot reboot || true) &&
+      die "fastboot can format ${scratch_partition}"
+  fi
+  fastboot reboot ||
+    die "can not reboot out of fastboot"
+  echo "${ORANGE}[  WARNING ]${NORMAL} adb after fastboot"
+  adb_wait ${ADB_WAIT} ||
+    fixup_from_recovery ||
+    die "did not reboot after formatting ${scratch_partition} `usb_status`"
+  if ${overlayfs_needed}; then
+    adb_root &&
+      D=`adb_sh df -k </dev/null` &&
+      H=`echo "${D}" | head -1` &&
+      D=`echo "${D}" | skip_unrelated_mounts | grep "^overlay "` &&
+      echo "${H}" &&
+      echo "${D}" &&
+      echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
+      die  "overlay /system takeover after flash vendor"
+    echo "${D}" | grep "^overlay .* /vendor\$" >/dev/null &&
+      if ${is_userspace_fastboot}; then
+        die  "overlay supposed to be minus /vendor takeover after flash vendor"
+      else
+        echo "${ORANGE}[  WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
+        echo "${ORANGE}[  WARNING ]${NORMAL} overlay supposed to be minus /vendor takeover after flash vendor" >&2
+      fi
+  fi
+  B="`adb_cat /system/hello`"
+  check_eq "${A}" "${B}" system after flash vendor
+  adb_root ||
+    die "adb root"
+  B="`adb_cat /vendor/hello`"
+  if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then
+    check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
+             vendor content after flash vendor
+  else
+    echo "${ORANGE}[  WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
+    check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
+             --warning vendor content after flash vendor
+  fi
+
+  check_eq "${SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/hello </dev/null`" system devt after reboot
+  check_eq "${SYSTEM_INO}" "`adb_sh stat --format=%i /system/hello </dev/null`" system inode after reboot
+  check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/bin/stat </dev/null`" base system devt after reboot
+  check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" devt for su after reboot
+
+fi
+
+wait_for_screen
+echo "${GREEN}[ RUN      ]${NORMAL} remove test content (cleanup)" >&2
+
+T=`adb_date`
+H=`adb remount 2>&1`
+err=${?}
+L=
+D="${H%?Now reboot your device for settings to take effect*}"
+if [ X"${H}" != X"${D}" ]; then
+  echo "${ORANGE}[  WARNING ]${NORMAL} adb remount requires a reboot after partial flash (legacy avb)"
+  L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
+  adb_reboot &&
+    adb_wait ${ADB_WAIT} &&
+    adb_root ||
+    die "failed to reboot"
+  T=`adb_date`
+  H=`adb remount 2>&1`
+  err=${?}
+fi
+echo "${H}"
+[ ${err} = 0 ] &&
+  ( adb_sh rm /vendor/hello </dev/null 2>/dev/null || true ) &&
+  adb_sh rm /system/hello </dev/null ||
+  ( [ -n "${L}" ] && echo "${L}" && false ) ||
+  die -t ${T} "cleanup hello"
+B="`adb_cat /system/hello`"
+check_eq "cat: /system/hello: No such file or directory" "${B}" after rm
+B="`adb_cat /vendor/hello`"
+check_eq "cat: /vendor/hello: No such file or directory" "${B}" after rm
+
+if [ -n "${scratch_partition}" ]; then
+
+  echo "${GREEN}[ RUN      ]${NORMAL} test fastboot flash to ${scratch_partition} recovery" >&2
+
+  avc_check
+  adb reboot fastboot </dev/null ||
+    die "Reboot into fastbootd"
+  img=${TMPDIR}/adb-remount-test-${$}.img
+  cleanup() {
+    rm ${img}
+  }
+  dd if=/dev/zero of=${img} bs=4096 count=16 2>/dev/null &&
+    fastboot_wait ${FASTBOOT_WAIT} ||
+    die "reboot into fastboot `usb_status`"
+  fastboot flash --force ${scratch_partition} ${img}
+  err=${?}
+  cleanup
+  cleanup() {
+    true
+  }
+  fastboot reboot ||
+    die "can not reboot out of fastboot"
+  [ 0 -eq ${err} ] ||
+    die "fastboot flash ${scratch_partition}"
+  adb_wait ${ADB_WAIT} &&
+    adb_root ||
+    die "did not reboot after flashing empty ${scratch_partition} `usb_status`"
+  T=`adb_date`
+  D=`adb disable-verity 2>&1`
+  err=${?}
+  if [ X"${D}" != "${D%?Now reboot your device for settings to take effect*}" ]
+  then
+    echo "${ORANGE}[  WARNING ]${NORMAL} adb disable-verity requires a reboot after partial flash"
+    adb_reboot &&
+      adb_wait ${ADB_WAIT} &&
+      adb_root ||
+      die "failed to reboot"
+    T=`adb_date`
+    D="${D}
+`adb disable-verity 2>&1`"
+    err=${?}
+  fi
+
+  echo "${D}"
+  [ ${err} = 0 ] &&
+    [ X"${D}" = X"${D##*setup failed}" ] &&
+    [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ] &&
+    echo "${GREEN}[       OK ]${NORMAL} ${scratch_partition} recreated" >&2 ||
+    die -t ${T} "setup for overlayfs"
+  D=`adb remount 2>&1`
+  err=${?}
+  echo "${D}"
+  [ ${err} != 0 ] ||
+    [ X"${D}" = X"${D##*remount failed}" ] ||
+    ( echo "${D}" && false ) ||
+    die -t ${T} "remount failed"
+fi
+
+echo "${GREEN}[ RUN      ]${NORMAL} test raw remount commands" >&2
+
+fixup_from_fastboot() {
+  inFastboot || return 1
+  if [ -n "${ACTIVE_SLOT}" ]; then
+    local active_slot=`get_active_slot`
+    if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
+      echo "${ORANGE}[    ERROR ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+    else
+      echo "${ORANGE}[    ERROR ]${NORMAL} Active slot to be set to ${ACTIVE_SLOT}"
+    fi >&2
+    fastboot --set-active=${ACTIVE_SLOT}
+  fi
+  fastboot reboot
+  adb_wait ${ADB_WAIT}
+}
+
+# Prerequisite is a prepped device from above.
+adb_reboot &&
+  adb_wait ${ADB_WAIT} ||
+  fixup_from_fastboot ||
+  die "lost device after reboot to ro state `usb_status`"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
+  die "/vendor is not read-only"
+adb_su mount -o rw,remount /vendor </dev/null ||
+  die "remount command"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
+  die "/vendor is not read-write"
+echo "${GREEN}[       OK ]${NORMAL} mount -o rw,remount command works" >&2
+
+# Prerequisite is a prepped device from above.
+adb_reboot &&
+  adb_wait ${ADB_WAIT} ||
+  fixup_from_fastboot ||
+  die "lost device after reboot to ro state `usb_status`"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
+  die "/vendor is not read-only"
+adb_su remount vendor </dev/null ||
+  die "remount command"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
+  die "/vendor is not read-write"
+adb_sh grep " /system .* rw," /proc/mounts >/dev/null </dev/null &&
+  die "/vendor is not read-only"
+echo "${GREEN}[       OK ]${NORMAL} remount command works from setup" >&2
+
+# Prerequisite is an overlayfs deconstructed device but with verity disabled.
+# This also saves a lot of 'noise' from the command doing a mkfs on backing
+# storage and all the related tuning and adjustment.
+for d in ${OVERLAYFS_BACKING}; do
+  adb_su rm -rf /${d}/overlay </dev/null ||
+    die "/${d}/overlay wipe"
+done
+adb_reboot &&
+  adb_wait ${ADB_WAIT} ||
+  fixup_from_fastboot ||
+  die "lost device after reboot after wipe `usb_status`"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
+  die "/vendor is not read-only"
+adb_su remount vendor </dev/null ||
+  die "remount command"
+adb_su df -k </dev/null | skip_unrelated_mounts
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
+  die "/vendor is not read-write"
+adb_sh grep " \(/system\|/\) .* rw," /proc/mounts >/dev/null </dev/null &&
+  die "/system is not read-only"
+echo "${GREEN}[       OK ]${NORMAL} remount command works from scratch" >&2
+
+if ! restore; then
+  restore() {
+    true
+  }
+  die "failed to restore verity after remount from scratch test"
+fi
+
+err=0
+
+if ${overlayfs_supported}; then
+  echo "${GREEN}[ RUN      ]${NORMAL} test 'adb remount -R'" >&2
+  avc_check
+  adb_root &&
+    adb remount -R &&
+    adb_wait ${ADB_WAIT} ||
+    die "adb remount -R"
+  if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
+       "2" = "`get_property partition.system.verified`" ]; then
+    die "remount -R command failed to disable verity"
+  fi
+
+  echo "${GREEN}[       OK ]${NORMAL} 'adb remount -R' command" >&2
+
+  restore
+  err=${?}
+fi
+
+restore() {
+  true
+}
+
+[ ${err} = 0 ] ||
+  die "failed to restore verity"
+
+echo "${GREEN}[  PASSED  ]${NORMAL} adb remount" >&2
+
+test_duration
diff --git a/fs_mgr/tests/file_wait_test.cpp b/fs_mgr/tests/file_wait_test.cpp
new file mode 100644
index 0000000..cc8b143
--- /dev/null
+++ b/fs_mgr/tests/file_wait_test.cpp
@@ -0,0 +1,91 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 <chrono>
+#include <string>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <fs_mgr/file_wait.h>
+#include <gtest/gtest.h>
+
+using namespace std::literals;
+using android::base::unique_fd;
+using android::fs_mgr::WaitForFile;
+using android::fs_mgr::WaitForFileDeleted;
+
+class FileWaitTest : public ::testing::Test {
+  protected:
+    void SetUp() override {
+        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
+        test_file_ = temp_dir_.path + "/"s + tinfo->name();
+    }
+
+    void TearDown() override { unlink(test_file_.c_str()); }
+
+    TemporaryDir temp_dir_;
+    std::string test_file_;
+};
+
+TEST_F(FileWaitTest, FileExists) {
+    unique_fd fd(open(test_file_.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0700));
+    ASSERT_GE(fd, 0);
+
+    ASSERT_TRUE(WaitForFile(test_file_, 500ms));
+    ASSERT_FALSE(WaitForFileDeleted(test_file_, 500ms));
+}
+
+TEST_F(FileWaitTest, FileDoesNotExist) {
+    ASSERT_FALSE(WaitForFile(test_file_, 500ms));
+    ASSERT_TRUE(WaitForFileDeleted(test_file_, 500ms));
+}
+
+TEST_F(FileWaitTest, CreateAsync) {
+    std::thread thread([this] {
+        std::this_thread::sleep_for(std::chrono::seconds(1));
+        unique_fd fd(open(test_file_.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0700));
+    });
+    EXPECT_TRUE(WaitForFile(test_file_, 3s));
+    thread.join();
+}
+
+TEST_F(FileWaitTest, CreateOtherAsync) {
+    std::thread thread([this] {
+        std::this_thread::sleep_for(std::chrono::seconds(1));
+        unique_fd fd(open(test_file_.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0700));
+    });
+    EXPECT_FALSE(WaitForFile(test_file_ + ".wontexist", 2s));
+    thread.join();
+}
+
+TEST_F(FileWaitTest, DeleteAsync) {
+    // Note: need to close the file, otherwise inotify considers it not deleted.
+    {
+        unique_fd fd(open(test_file_.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0700));
+        ASSERT_GE(fd, 0);
+    }
+
+    std::thread thread([this] {
+        std::this_thread::sleep_for(std::chrono::seconds(1));
+        unlink(test_file_.c_str());
+    });
+    EXPECT_TRUE(WaitForFileDeleted(test_file_, 3s));
+    thread.join();
+}
+
+TEST_F(FileWaitTest, BadPath) {
+    ASSERT_FALSE(WaitForFile("/this/path/does/not/exist", 5ms));
+    EXPECT_EQ(errno, ENOENT);
+}
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
new file mode 100644
index 0000000..6d87594
--- /dev/null
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -0,0 +1,1038 @@
+/*
+ * 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/file.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <fstab/fstab.h>
+#include <gtest/gtest.h>
+
+#include "../fs_mgr_priv_boot_config.h"
+
+using namespace android::fs_mgr;
+
+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) {
+    Fstab fstab;
+    ASSERT_TRUE(ReadFstabFromFile("/proc/mounts", &fstab));
+
+    std::unique_ptr<std::FILE, int (*)(std::FILE*)> mounts(setmntent("/proc/mounts", "re"),
+                                                           endmntent);
+    ASSERT_NE(mounts, nullptr);
+
+    mntent* mentry;
+    size_t i = 0;
+    while ((mentry = getmntent(mounts.get())) != nullptr) {
+        ASSERT_LT(i, fstab.size());
+        auto& entry = fstab[i];
+
+        EXPECT_EQ(mentry->mnt_fsname, entry.blk_device);
+        EXPECT_EQ(mentry->mnt_dir, entry.mount_point);
+        EXPECT_EQ(mentry->mnt_type, entry.fs_type);
+
+        std::set<std::string> mnt_opts;
+        for (auto& s : android::base::Split(mentry->mnt_opts, ",")) {
+            mnt_opts.emplace(s);
+        }
+        std::set<std::string> fs_options;
+        if (!entry.fs_options.empty()) {
+            for (auto& s : android::base::Split(entry.fs_options, ",")) {
+                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 & entry.flags) {
+                fs_options.emplace(mount_flags[f].name);
+            }
+        }
+        if (!(entry.flags & MS_RDONLY)) {
+            fs_options.emplace("rw");
+        }
+        EXPECT_EQ(mnt_opts, fs_options);
+        ++i;
+    }
+    EXPECT_EQ(i, fstab.size());
+}
+
+// TODO(124837435): enable it later when it can pass TreeHugger.
+TEST(fs_mgr, DISABLED_ReadFstabFromFile_MountOptions) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source /            ext4    ro,barrier=1                    wait,slotselect,avb
+source /metadata    ext4    noatime,nosuid,nodev,discard    wait,formattable
+
+source /data        f2fs    noatime,nosuid,nodev,discard,reserve_root=32768,resgid=1065,fsync_mode=nobarrier    latemount,wait,check,fileencryption=ice,keydirectory=/metadata/vold/metadata_encryption,quota,formattable,sysfs_path=/sys/devices/platform/soc/1d84000.ufshc,reservedsize=128M
+
+source /misc        emmc    defaults                        defaults
+
+source /vendor/firmware_mnt    vfat    ro,shortname=lower,uid=1000,gid=1000,dmask=227,fmask=337,context=u:object_r:firmware_file:s0    wait,slotselect
+
+source auto         vfat    defaults                        voldmanaged=usb:auto
+source none         swap    defaults                        zramsize=1073741824,max_comp_streams=8
+source none2        swap    nodiratime,remount,bind         zramsize=1073741824,max_comp_streams=8
+source none3        swap    unbindable,private,slave        zramsize=1073741824,max_comp_streams=8
+source none4        swap    noexec,shared,rec               zramsize=1073741824,max_comp_streams=8
+source none5        swap    rw                              zramsize=1073741824,max_comp_streams=8
+)fs";
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(11U, fstab.size());
+
+    EXPECT_EQ("/", fstab[0].mount_point);
+    EXPECT_EQ(static_cast<unsigned long>(MS_RDONLY), fstab[0].flags);
+    EXPECT_EQ("barrier=1", fstab[0].fs_options);
+
+    EXPECT_EQ("/metadata", fstab[1].mount_point);
+    EXPECT_EQ(static_cast<unsigned long>(MS_NOATIME | MS_NOSUID | MS_NODEV), fstab[1].flags);
+    EXPECT_EQ("discard", fstab[1].fs_options);
+
+    EXPECT_EQ("/data", fstab[2].mount_point);
+    EXPECT_EQ(static_cast<unsigned long>(MS_NOATIME | MS_NOSUID | MS_NODEV), fstab[2].flags);
+    EXPECT_EQ("discard,reserve_root=32768,resgid=1065,fsync_mode=nobarrier", fstab[2].fs_options);
+
+    EXPECT_EQ("/misc", fstab[3].mount_point);
+    EXPECT_EQ(0U, fstab[3].flags);
+    EXPECT_EQ("", fstab[3].fs_options);
+
+    EXPECT_EQ("/vendor/firmware_mnt", fstab[4].mount_point);
+    EXPECT_EQ(static_cast<unsigned long>(MS_RDONLY), fstab[4].flags);
+    EXPECT_EQ(
+            "shortname=lower,uid=1000,gid=1000,dmask=227,fmask=337,"
+            "context=u:object_r:firmware_file:s0",
+            fstab[4].fs_options);
+
+    EXPECT_EQ("auto", fstab[5].mount_point);
+    EXPECT_EQ(0U, fstab[5].flags);
+    EXPECT_EQ("", fstab[5].fs_options);
+
+    EXPECT_EQ("none", fstab[6].mount_point);
+    EXPECT_EQ(0U, fstab[6].flags);
+    EXPECT_EQ("", fstab[6].fs_options);
+
+    EXPECT_EQ("none2", fstab[7].mount_point);
+    EXPECT_EQ(static_cast<unsigned long>(MS_NODIRATIME | MS_REMOUNT | MS_BIND), fstab[7].flags);
+    EXPECT_EQ("", fstab[7].fs_options);
+
+    EXPECT_EQ("none3", fstab[8].mount_point);
+    EXPECT_EQ(static_cast<unsigned long>(MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE), fstab[8].flags);
+    EXPECT_EQ("", fstab[8].fs_options);
+
+    EXPECT_EQ("none4", fstab[9].mount_point);
+    EXPECT_EQ(static_cast<unsigned long>(MS_NOEXEC | MS_SHARED | MS_REC), fstab[9].flags);
+    EXPECT_EQ("", fstab[9].fs_options);
+
+    EXPECT_EQ("none5", fstab[10].mount_point);
+    EXPECT_EQ(0U, fstab[10].flags);  // rw is the same as defaults
+    EXPECT_EQ("", fstab[10].fs_options);
+}
+
+static bool CompareFlags(FstabEntry::FsMgrFlags& lhs, FstabEntry::FsMgrFlags& rhs) {
+    // clang-format off
+    return lhs.wait == rhs.wait &&
+           lhs.check == rhs.check &&
+           lhs.crypt == rhs.crypt &&
+           lhs.nonremovable == rhs.nonremovable &&
+           lhs.vold_managed == rhs.vold_managed &&
+           lhs.recovery_only == rhs.recovery_only &&
+           lhs.verify == rhs.verify &&
+           lhs.force_crypt == rhs.force_crypt &&
+           lhs.no_emulated_sd == rhs.no_emulated_sd &&
+           lhs.no_trim == rhs.no_trim &&
+           lhs.file_encryption == rhs.file_encryption &&
+           lhs.formattable == rhs.formattable &&
+           lhs.slot_select == rhs.slot_select &&
+           lhs.force_fde_or_fbe == rhs.force_fde_or_fbe &&
+           lhs.late_mount == rhs.late_mount &&
+           lhs.no_fail == rhs.no_fail &&
+           lhs.verify_at_boot == rhs.verify_at_boot &&
+           lhs.quota == rhs.quota &&
+           lhs.avb == rhs.avb &&
+           lhs.logical == rhs.logical &&
+           lhs.checkpoint_blk == rhs.checkpoint_blk &&
+           lhs.checkpoint_fs == rhs.checkpoint_fs &&
+           lhs.first_stage_mount == rhs.first_stage_mount &&
+           lhs.slot_select_other == rhs.slot_select_other &&
+           lhs.fs_verity == rhs.fs_verity;
+    // clang-format on
+}
+
+// TODO(124837435): enable it later when it can pass TreeHugger.
+TEST(fs_mgr, DISABLED_ReadFstabFromFile_FsMgrFlags) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      wait,check,nonremovable,recoveryonly,verifyatboot,verify
+source none1       swap   defaults      avb,noemulatedsd,notrim,formattable,slotselect,nofail
+source none2       swap   defaults      first_stage_mount,latemount,quota,logical,slotselect_other
+source none3       swap   defaults      checkpoint=block
+source none4       swap   defaults      checkpoint=fs
+source none5       swap   defaults      defaults
+)fs";
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(6U, fstab.size());
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+    {
+        FstabEntry::FsMgrFlags flags = {};
+        flags.wait = true;
+        flags.check = true;
+        flags.nonremovable = true;
+        flags.recovery_only = true;
+        flags.verify_at_boot = true;
+        flags.verify = true;
+        EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    }
+    entry++;
+
+    EXPECT_EQ("none1", entry->mount_point);
+    {
+        FstabEntry::FsMgrFlags flags = {};
+        flags.avb = true;
+        flags.no_emulated_sd = true;
+        flags.no_trim = true;
+        flags.formattable = true;
+        flags.slot_select = true;
+        flags.no_fail = true;
+        EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    }
+    entry++;
+
+    EXPECT_EQ("none2", entry->mount_point);
+    {
+        FstabEntry::FsMgrFlags flags = {};
+        flags.first_stage_mount = true;
+        flags.late_mount = true;
+        flags.quota = true;
+        flags.logical = true;
+        flags.slot_select_other = true;
+        EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    }
+    entry++;
+
+    EXPECT_EQ("none3", entry->mount_point);
+    {
+        FstabEntry::FsMgrFlags flags = {};
+        flags.checkpoint_blk = true;
+        EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    }
+    entry++;
+
+    EXPECT_EQ("none4", entry->mount_point);
+    {
+        FstabEntry::FsMgrFlags flags = {};
+        flags.checkpoint_fs = true;
+        EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    }
+    entry++;
+
+    EXPECT_EQ("none5", entry->mount_point);
+    {
+        FstabEntry::FsMgrFlags flags = {};
+        EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    }
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_AllBad) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      encryptable,forceencrypt,fileencryption,forcefdeorfbe,keydirectory,length,swapprio,zramsize,max_comp_streams,reservedsize,eraseblk,logicalblk,sysfs_path,zram_loopback_path,zram_loopback_size,zram_backing_dev_path
+
+source none1       swap   defaults      encryptable=,forceencrypt=,fileencryption=,keydirectory=,length=,swapprio=,zramsize=,max_comp_streams=,avb=,reservedsize=,eraseblk=,logicalblk=,sysfs_path=,zram_loopback_path=,zram_loopback_size=,zram_backing_dev_path=
+
+source none2       swap   defaults      forcefdeorfbe=
+
+)fs";
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(3U, fstab.size());
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+    {
+        FstabEntry::FsMgrFlags flags = {};
+        EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    }
+    EXPECT_EQ("", entry->key_loc);
+    EXPECT_EQ("", entry->key_dir);
+    EXPECT_EQ(0, entry->length);
+    EXPECT_EQ("", entry->label);
+    EXPECT_EQ(-1, entry->partnum);
+    EXPECT_EQ(-1, entry->swap_prio);
+    EXPECT_EQ(0, entry->max_comp_streams);
+    EXPECT_EQ(0, entry->zram_size);
+    EXPECT_EQ(0, entry->reserved_size);
+    EXPECT_EQ("", entry->file_contents_mode);
+    EXPECT_EQ("", entry->file_names_mode);
+    EXPECT_EQ(0, entry->erase_blk_size);
+    EXPECT_EQ(0, entry->logical_blk_size);
+    EXPECT_EQ("", entry->sysfs_path);
+    EXPECT_EQ("", entry->zram_loopback_path);
+    EXPECT_EQ(512U * 1024U * 1024U, entry->zram_loopback_size);
+    EXPECT_EQ("", entry->zram_backing_dev_path);
+    entry++;
+
+    EXPECT_EQ("none1", entry->mount_point);
+    {
+        FstabEntry::FsMgrFlags flags = {};
+        flags.crypt = true;
+        flags.force_crypt = true;
+        flags.file_encryption = true;
+        flags.avb = true;
+        EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    }
+    EXPECT_EQ("", entry->key_loc);
+    EXPECT_EQ("", entry->key_dir);
+    EXPECT_EQ(0, entry->length);
+    EXPECT_EQ("", entry->label);
+    EXPECT_EQ(-1, entry->partnum);
+    EXPECT_EQ(-1, entry->swap_prio);
+    EXPECT_EQ(0, entry->max_comp_streams);
+    EXPECT_EQ(0, entry->zram_size);
+    EXPECT_EQ(0, entry->reserved_size);
+    EXPECT_EQ("", entry->file_contents_mode);
+    EXPECT_EQ("", entry->file_names_mode);
+    EXPECT_EQ(0, entry->erase_blk_size);
+    EXPECT_EQ(0, entry->logical_blk_size);
+    EXPECT_EQ("", entry->sysfs_path);
+    EXPECT_EQ("", entry->zram_loopback_path);
+    EXPECT_EQ(512U * 1024U * 1024U, entry->zram_loopback_size);
+    EXPECT_EQ("", entry->zram_backing_dev_path);
+    entry++;
+
+    // forcefdeorfbe sets file_contents_mode and file_names_mode by default, so test it separately.
+    EXPECT_EQ("none2", entry->mount_point);
+    {
+        FstabEntry::FsMgrFlags flags = {};
+        flags.force_fde_or_fbe = true;
+        EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    }
+    EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
+    EXPECT_EQ("aes-256-cts", entry->file_names_mode);
+    EXPECT_EQ("", entry->key_loc);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Encryptable) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      encryptable=/dir/key
+)fs";
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(1U, fstab.size());
+
+    FstabEntry::FsMgrFlags flags = {};
+    flags.crypt = true;
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ("/dir/key", entry->key_loc);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_VoldManaged) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      voldmanaged=:
+source none1       swap   defaults      voldmanaged=sdcard
+source none2       swap   defaults      voldmanaged=sdcard:3
+source none3       swap   defaults      voldmanaged=sdcard:auto
+)fs";
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(4U, fstab.size());
+
+    FstabEntry::FsMgrFlags flags = {};
+    flags.vold_managed = true;
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_TRUE(entry->label.empty());
+    EXPECT_EQ(-1, entry->partnum);
+    entry++;
+
+    EXPECT_EQ("none1", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_TRUE(entry->label.empty());
+    EXPECT_EQ(-1, entry->partnum);
+    entry++;
+
+    EXPECT_EQ("none2", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ("sdcard", entry->label);
+    EXPECT_EQ(3, entry->partnum);
+    entry++;
+
+    EXPECT_EQ("none3", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ("sdcard", entry->label);
+    EXPECT_EQ(-1, entry->partnum);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Length) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      length=blah
+source none1       swap   defaults      length=123456
+)fs";
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(2U, fstab.size());
+
+    FstabEntry::FsMgrFlags flags = {};
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(0, entry->length);
+    entry++;
+
+    EXPECT_EQ("none1", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(123456, entry->length);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Swapprio) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      swapprio=blah
+source none1       swap   defaults      swapprio=123456
+)fs";
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(2U, fstab.size());
+
+    FstabEntry::FsMgrFlags flags = {};
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(-1, entry->swap_prio);
+    entry++;
+
+    EXPECT_EQ("none1", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(123456, entry->swap_prio);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_ZramSize) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      zramsize=blah
+source none1       swap   defaults      zramsize=123456
+source none2       swap   defaults      zramsize=blah%
+source none3       swap   defaults      zramsize=5%
+source none4       swap   defaults      zramsize=105%
+source none5       swap   defaults      zramsize=%
+)fs";
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(6U, fstab.size());
+
+    FstabEntry::FsMgrFlags flags = {};
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(0, entry->zram_size);
+    entry++;
+
+    EXPECT_EQ("none1", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(123456, entry->zram_size);
+    entry++;
+
+    EXPECT_EQ("none2", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(0, entry->zram_size);
+    entry++;
+
+    EXPECT_EQ("none3", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_NE(0, entry->zram_size);
+    entry++;
+
+    EXPECT_EQ("none4", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(0, entry->zram_size);
+    entry++;
+
+    EXPECT_EQ("none5", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(0, entry->zram_size);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_ForceEncrypt) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      forceencrypt=/dir/key
+)fs";
+
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(1U, fstab.size());
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+
+    FstabEntry::FsMgrFlags flags = {};
+    flags.force_crypt = true;
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+
+    EXPECT_EQ("/dir/key", entry->key_loc);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_ForceFdeOrFbe) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      forcefdeorfbe=/dir/key
+)fs";
+
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(1U, fstab.size());
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+
+    FstabEntry::FsMgrFlags flags = {};
+    flags.force_fde_or_fbe = true;
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+
+    EXPECT_EQ("/dir/key", entry->key_loc);
+    EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
+    EXPECT_EQ("aes-256-cts", entry->file_names_mode);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_FileEncryption) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      fileencryption=blah
+source none1       swap   defaults      fileencryption=software
+source none2       swap   defaults      fileencryption=aes-256-xts
+source none3       swap   defaults      fileencryption=adiantum
+source none4       swap   defaults      fileencryption=adiantum:aes-256-heh
+source none5       swap   defaults      fileencryption=ice
+source none6       swap   defaults      fileencryption=ice:blah
+source none7       swap   defaults      fileencryption=ice:aes-256-cts
+source none8       swap   defaults      fileencryption=ice:aes-256-heh
+source none9       swap   defaults      fileencryption=ice:adiantum
+source none10      swap   defaults      fileencryption=ice:adiantum:
+)fs";
+
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(11U, fstab.size());
+
+    FstabEntry::FsMgrFlags flags = {};
+    flags.file_encryption = true;
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ("", entry->file_contents_mode);
+    EXPECT_EQ("", entry->file_names_mode);
+
+    entry++;
+    EXPECT_EQ("none1", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
+    EXPECT_EQ("aes-256-cts", entry->file_names_mode);
+
+    entry++;
+    EXPECT_EQ("none2", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
+    EXPECT_EQ("aes-256-cts", entry->file_names_mode);
+
+    entry++;
+    EXPECT_EQ("none3", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ("adiantum", entry->file_contents_mode);
+    EXPECT_EQ("adiantum", entry->file_names_mode);
+
+    entry++;
+    EXPECT_EQ("none4", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ("adiantum", entry->file_contents_mode);
+    EXPECT_EQ("aes-256-heh", entry->file_names_mode);
+
+    entry++;
+    EXPECT_EQ("none5", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ("ice", entry->file_contents_mode);
+    EXPECT_EQ("aes-256-cts", entry->file_names_mode);
+
+    entry++;
+    EXPECT_EQ("none6", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ("ice", entry->file_contents_mode);
+    EXPECT_EQ("", entry->file_names_mode);
+
+    entry++;
+    EXPECT_EQ("none7", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ("ice", entry->file_contents_mode);
+    EXPECT_EQ("aes-256-cts", entry->file_names_mode);
+
+    entry++;
+    EXPECT_EQ("none8", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ("ice", entry->file_contents_mode);
+    EXPECT_EQ("aes-256-heh", entry->file_names_mode);
+
+    entry++;
+    EXPECT_EQ("none9", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ("ice", entry->file_contents_mode);
+    EXPECT_EQ("adiantum", entry->file_names_mode);
+
+    entry++;
+    EXPECT_EQ("none10", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ("", entry->file_contents_mode);
+    EXPECT_EQ("", entry->file_names_mode);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MaxCompStreams) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      max_comp_streams=blah
+source none1       swap   defaults      max_comp_streams=123456
+)fs";
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(2U, fstab.size());
+
+    FstabEntry::FsMgrFlags flags = {};
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(0, entry->max_comp_streams);
+    entry++;
+
+    EXPECT_EQ("none1", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(123456, entry->max_comp_streams);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_ReservedSize) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      reservedsize=blah
+source none1       swap   defaults      reservedsize=2
+source none2       swap   defaults      reservedsize=1K
+source none3       swap   defaults      reservedsize=2m
+)fs";
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(4U, fstab.size());
+
+    FstabEntry::FsMgrFlags flags = {};
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(0, entry->reserved_size);
+    entry++;
+
+    EXPECT_EQ("none1", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(2, entry->reserved_size);
+    entry++;
+
+    EXPECT_EQ("none2", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(1024, entry->reserved_size);
+    entry++;
+
+    EXPECT_EQ("none3", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(2 * 1024 * 1024, entry->reserved_size);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_EraseBlk) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      eraseblk=blah
+source none1       swap   defaults      eraseblk=4000
+source none2       swap   defaults      eraseblk=5000
+source none3       swap   defaults      eraseblk=8192
+)fs";
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(4U, fstab.size());
+
+    FstabEntry::FsMgrFlags flags = {};
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(0, entry->erase_blk_size);
+    entry++;
+
+    EXPECT_EQ("none1", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(0, entry->erase_blk_size);
+    entry++;
+
+    EXPECT_EQ("none2", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(0, entry->erase_blk_size);
+    entry++;
+
+    EXPECT_EQ("none3", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(8192, entry->erase_blk_size);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Logicalblk) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      logicalblk=blah
+source none1       swap   defaults      logicalblk=4000
+source none2       swap   defaults      logicalblk=5000
+source none3       swap   defaults      logicalblk=8192
+)fs";
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(4U, fstab.size());
+
+    FstabEntry::FsMgrFlags flags = {};
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(0, entry->logical_blk_size);
+    entry++;
+
+    EXPECT_EQ("none1", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(0, entry->logical_blk_size);
+    entry++;
+
+    EXPECT_EQ("none2", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(0, entry->logical_blk_size);
+    entry++;
+
+    EXPECT_EQ("none3", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(8192, entry->logical_blk_size);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Avb) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      avb=vbmeta_partition
+source none1       swap   defaults      avb_keys=/path/to/test.avbpubkey
+)fs";
+
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(2U, fstab.size());
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+
+    FstabEntry::FsMgrFlags flags = {};
+    flags.avb = true;
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+
+    EXPECT_EQ("vbmeta_partition", entry->vbmeta_partition);
+    entry++;
+
+    EXPECT_EQ("none1", entry->mount_point);
+    FstabEntry::FsMgrFlags empty_flags = {};  // no flags should be set for avb_keys.
+    EXPECT_TRUE(CompareFlags(empty_flags, entry->fs_mgr_flags));
+    EXPECT_EQ("/path/to/test.avbpubkey", entry->avb_keys);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_KeyDirectory) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      keydirectory=/dir/key
+)fs";
+
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(1U, fstab.size());
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+
+    FstabEntry::FsMgrFlags flags = {};
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+
+    EXPECT_EQ("/dir/key", entry->key_dir);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_SysfsPath) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      sysfs_path=/sys/device
+)fs";
+
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(1U, fstab.size());
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+
+    FstabEntry::FsMgrFlags flags = {};
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+
+    EXPECT_EQ("/sys/device", entry->sysfs_path);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Zram) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      zram_loopback_path=/dev/path
+
+source none1       swap   defaults      zram_loopback_size=blah
+source none2       swap   defaults      zram_loopback_size=2
+source none3       swap   defaults      zram_loopback_size=1K
+source none4       swap   defaults      zram_loopback_size=2m
+
+source none5       swap   defaults      zram_backing_dev_path=/dev/path2
+
+)fs";
+
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(6U, fstab.size());
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+    EXPECT_EQ("/dev/path", entry->zram_loopback_path);
+    entry++;
+
+    EXPECT_EQ("none1", entry->mount_point);
+    EXPECT_EQ(512U * 1024U * 1024U, entry->zram_loopback_size);
+    entry++;
+
+    EXPECT_EQ("none2", entry->mount_point);
+    EXPECT_EQ(2U, entry->zram_loopback_size);
+    entry++;
+
+    EXPECT_EQ("none3", entry->mount_point);
+    EXPECT_EQ(1024U, entry->zram_loopback_size);
+    entry++;
+
+    EXPECT_EQ("none4", entry->mount_point);
+    EXPECT_EQ(2U * 1024U * 1024U, entry->zram_loopback_size);
+    entry++;
+
+    EXPECT_EQ("none5", entry->mount_point);
+    EXPECT_EQ("/dev/path2", entry->zram_backing_dev_path);
+}
diff --git a/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
new file mode 100644
index 0000000..f08cab2
--- /dev/null
+++ b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 com.android.tests.vendoroverlay;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test the vendor overlay feature. Requires adb remount with OverlayFS.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class VendorOverlayHostTest extends BaseHostJUnit4Test {
+  boolean wasRoot = false;
+
+  @Before
+  public void setup() throws DeviceNotAvailableException {
+    wasRoot = getDevice().isAdbRoot();
+    if (!wasRoot) {
+      Assume.assumeTrue("Test requires root", getDevice().enableAdbRoot());
+    }
+
+    Assume.assumeTrue("Skipping vendor overlay test due to lack of necessary OverlayFS support",
+        testConditionsMet());
+
+    getDevice().remountSystemWritable();
+    // Was OverlayFS used by adb remount? Without it we can't safely re-enable dm-verity.
+    Pattern vendorPattern = Pattern.compile("^overlay .+ /vendor$", Pattern.MULTILINE);
+    Pattern productPattern = Pattern.compile("^overlay .+ /product$", Pattern.MULTILINE);
+    CommandResult result = getDevice().executeShellV2Command("df");
+    Assume.assumeTrue("OverlayFS not used for adb remount on /vendor",
+        vendorPattern.matcher(result.getStdout()).find());
+    Assume.assumeTrue("OverlayFS not used for adb remount on /product",
+        productPattern.matcher(result.getStdout()).find());
+  }
+
+  private boolean cmdSucceeded(CommandResult result) {
+    return result.getStatus() == CommandStatus.SUCCESS;
+  }
+
+  private void assumeMkdirSuccess(String dir) throws DeviceNotAvailableException {
+    CommandResult result = getDevice().executeShellV2Command("mkdir -p " + dir);
+    Assume.assumeTrue("Couldn't create " + dir, cmdSucceeded(result));
+  }
+
+  /**
+   * Tests that files in the appropriate /product/vendor_overlay dir are overlaid onto /vendor.
+   */
+  @Test
+  public void testVendorOverlay() throws DeviceNotAvailableException {
+    String vndkVersion = getDevice().executeShellV2Command("getprop ro.vndk.version").getStdout();
+
+    // Create files and modify policy
+    CommandResult result = getDevice().executeShellV2Command(
+        "echo '/(product|system/product)/vendor_overlay/" + vndkVersion +
+        "/.* u:object_r:vendor_file:s0'" + " >> /system/etc/selinux/plat_file_contexts");
+    Assume.assumeTrue("Couldn't modify plat_file_contexts", cmdSucceeded(result));
+    assumeMkdirSuccess("/vendor/testdir");
+    assumeMkdirSuccess("/vendor/diffcontext");
+    assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/testdir");
+    result = getDevice().executeShellV2Command(
+        "echo overlay > /product/vendor_overlay/'" + vndkVersion + "'/testdir/test");
+    Assume.assumeTrue("Couldn't create text file in testdir", cmdSucceeded(result));
+    assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/noexist/test");
+    assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/diffcontext/test");
+    result = getDevice().executeShellV2Command(
+        "restorecon -r /product/vendor_overlay/'" + vndkVersion + "'/testdir");
+    Assume.assumeTrue("Couldn't write testdir context", cmdSucceeded(result));
+
+    getDevice().reboot();
+
+    // Test that the file was overlaid properly
+    result = getDevice().executeShellV2Command("[ $(cat /vendor/testdir/test) = overlay ]");
+    Assert.assertTrue("test file was not overlaid onto /vendor/", cmdSucceeded(result));
+    result = getDevice().executeShellV2Command("[ ! -d /vendor/noexist/test ]");
+    Assert.assertTrue("noexist dir shouldn't exist on /vendor", cmdSucceeded(result));
+    result = getDevice().executeShellV2Command("[ ! -d /vendor/diffcontext/test ]");
+    Assert.assertTrue("diffcontext dir shouldn't exist on /vendor", cmdSucceeded(result));
+  }
+
+  // Duplicate of fs_mgr_overlayfs_valid() logic
+  // Requires root
+  public boolean testConditionsMet() throws DeviceNotAvailableException {
+    if (cmdSucceeded(getDevice().executeShellV2Command(
+        "[ -e /sys/module/overlay/parameters/override_creds ]"))) {
+      return true;
+    }
+    if (cmdSucceeded(getDevice().executeShellV2Command("[ ! -e /sys/module/overlay ]"))) {
+      return false;
+    }
+    CommandResult result = getDevice().executeShellV2Command("awk '{ print $3 }' /proc/version");
+    Pattern kernelVersionPattern = Pattern.compile("([1-9])[.]([0-9]+).*");
+    Matcher kernelVersionMatcher = kernelVersionPattern.matcher(result.getStdout());
+    kernelVersionMatcher.find();
+    int majorKernelVersion;
+    int minorKernelVersion;
+    try {
+      majorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(1));
+      minorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(2));
+    } catch (Exception e) {
+      return false;
+    }
+    if (majorKernelVersion < 4) {
+      return true;
+    }
+    if (majorKernelVersion > 4) {
+      return false;
+    }
+    if (minorKernelVersion > 6) {
+      return false;
+    }
+    return true;
+  }
+
+  @After
+  public void tearDown() throws DeviceNotAvailableException {
+    if (getDevice().executeAdbCommand("enable-verity").contains("Now reboot your device")) {
+      getDevice().reboot();
+    }
+    if (!wasRoot) {
+      getDevice().disableAdbRoot();
+    }
+  }
+}
+
diff --git a/fs_mgr/tests/vendor-overlay-test.xml b/fs_mgr/tests/vendor-overlay-test.xml
new file mode 100644
index 0000000..0b5c8cc
--- /dev/null
+++ b/fs_mgr/tests/vendor-overlay-test.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<configuration description="Config for vendor overlay test cases">
+    <test class="com.android.tradefed.testtype.HostTest" >
+        <option name="jar" value="fs_mgr_vendor_overlay_test.jar" />
+    </test>
+</configuration>
+
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..7e6ad5b
--- /dev/null
+++ b/fs_mgr/tools/dmctl.cpp
@@ -0,0 +1,455 @@
+/*
+ * 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/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/unique_fd.h>
+#include <libdm/dm.h>
+
+#include <fstream>
+#include <functional>
+#include <iomanip>
+#include <ios>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+using namespace std::literals::string_literals;
+using namespace android::dm;
+using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
+
+static int Usage(void) {
+    std::cerr << "usage: dmctl <command> [command options]" << std::endl;
+    std::cerr << "       dmctl -f file" << 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> [-v]" << std::endl;
+    std::cerr << "  getpath <dm-name>" << std::endl;
+    std::cerr << "  status <dm-name>" << std::endl;
+    std::cerr << "  table <dm-name>" << std::endl;
+    std::cerr << "  help" << std::endl;
+    std::cerr << std::endl;
+    std::cerr << "-f file reads command and all parameters from named file" << 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 if (target_type == "bow") {
+            if (!HasArgs(1)) {
+                std::cerr << "Expected \"bow\" <block_device>" << std::endl;
+                return nullptr;
+            }
+            std::string block_device = NextArg();
+            return std::make_unique<DmTargetBow>(start_sector, num_sectors, block_device);
+        } else if (target_type == "snapshot-origin") {
+            if (!HasArgs(1)) {
+                std::cerr << "Expected \"snapshot-origin\" <block_device>" << std::endl;
+                return nullptr;
+            }
+            std::string block_device = NextArg();
+            return std::make_unique<DmTargetSnapshotOrigin>(start_sector, num_sectors,
+                                                            block_device);
+        } else if (target_type == "snapshot") {
+            if (!HasArgs(4)) {
+                std::cerr
+                        << "Expected \"snapshot\" <block_device> <block_device> <mode> <chunk_size>"
+                        << std::endl;
+                return nullptr;
+            }
+            std::string base_device = NextArg();
+            std::string cow_device = NextArg();
+            std::string mode_str = NextArg();
+            std::string chunk_size_str = NextArg();
+
+            SnapshotStorageMode mode;
+            if (mode_str == "P") {
+                mode = SnapshotStorageMode::Persistent;
+            } else if (mode_str == "N") {
+                mode = SnapshotStorageMode::Transient;
+            } else {
+                std::cerr << "Unrecognized mode: " << mode_str << "\n";
+                return nullptr;
+            }
+
+            uint32_t chunk_size;
+            if (!android::base::ParseUint(chunk_size_str, &chunk_size)) {
+                std::cerr << "Chunk size must be an unsigned integer.\n";
+                return nullptr;
+            }
+            return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
+                                                      cow_device, mode, chunk_size);
+        } else if (target_type == "snapshot-merge") {
+            if (!HasArgs(3)) {
+                std::cerr
+                        << "Expected \"snapshot-merge\" <block_device> <block_device> <chunk_size>"
+                        << std::endl;
+                return nullptr;
+            }
+            std::string base_device = NextArg();
+            std::string cow_device = NextArg();
+            std::string chunk_size_str = NextArg();
+            SnapshotStorageMode mode = SnapshotStorageMode::Merge;
+
+            uint32_t chunk_size;
+            if (!android::base::ParseUint(chunk_size_str, &chunk_size)) {
+                std::cerr << "Chunk size must be an unsigned integer.\n";
+                return nullptr;
+            }
+            return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
+                                                      cow_device, mode, chunk_size);
+        } 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, [[maybe_unused]] int argc,
+                         [[maybe_unused]] char** argv) {
+    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, int argc, char** argv) {
+    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;
+    }
+
+    bool verbose = (argc && (argv[0] == "-v"s));
+    for (const auto& dev : devices) {
+        std::cout << std::left << std::setw(20) << dev.name() << " : " << dev.Major() << ":"
+                  << dev.Minor() << std::endl;
+        if (verbose) {
+            std::vector<DeviceMapper::TargetInfo> table;
+            if (!dm.GetTableInfo(dev.name(), &table)) {
+                std::cerr << "Could not query table status for device \"" << dev.name() << "\"."
+                          << std::endl;
+                return -EINVAL;
+            }
+
+            uint32_t target_num = 1;
+            for (const auto& target : table) {
+                std::cout << "  target#" << target_num << ": ";
+                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;
+                target_num++;
+            }
+        }
+    }
+
+    return 0;
+}
+
+static const std::map<std::string, std::function<int(DeviceMapper&, int, char**)>> 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, argc - 1, argv + 1);
+    }
+
+    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 DumpTable(const std::string& mode, 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 (mode == "status") {
+        if (!dm.GetTableStatus(argv[0], &table)) {
+            std::cerr << "Could not query table status of device \"" << argv[0] << "\"."
+                      << std::endl;
+            return -EINVAL;
+        }
+    } else if (mode == "table") {
+        if (!dm.GetTableInfo(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 int TableCmdHandler(int argc, char** argv) {
+    return DumpTable("table", argc, argv);
+}
+
+static int StatusCmdHandler(int argc, char** argv) {
+    return DumpTable("status", argc, argv);
+}
+
+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},
+        {"status", StatusCmdHandler},
+        // clang-format on
+};
+
+static bool ReadFile(const char* filename, std::vector<std::string>* args,
+                     std::vector<char*>* arg_ptrs) {
+    std::ifstream file(filename);
+    if (!file) return false;
+
+    std::string arg;
+    while (file >> arg) args->push_back(arg);
+
+    for (auto const& i : *args) arg_ptrs->push_back(const_cast<char*>(i.c_str()));
+    return true;
+}
+
+int main(int argc, char** argv) {
+    android::base::InitLogging(argv, &android::base::StderrLogger);
+    if (argc < 2) {
+        return Usage();
+    }
+
+    std::vector<std::string> args;
+    std::vector<char*> arg_ptrs;
+    if (std::string("-f") == argv[1]) {
+        if (argc != 3) {
+            return Usage();
+        }
+
+        args.push_back(argv[0]);
+        if (!ReadFile(argv[2], &args, &arg_ptrs)) {
+            return Usage();
+        }
+
+        argc = arg_ptrs.size();
+        argv = &arg_ptrs[0];
+    }
+
+    for (const auto& cmd : cmdmap) {
+        if (cmd.first == argv[1]) {
+            return cmd.second(argc - 2, argv + 2);
+        }
+    }
+
+    return Usage();
+}
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
new file mode 100644
index 0000000..778e08c
--- /dev/null
+++ b/gatekeeperd/Android.bp
@@ -0,0 +1,84 @@
+//
+// 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.
+//
+
+cc_binary {
+    name: "gatekeeperd",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wunused",
+    ],
+    srcs: [
+        "gatekeeperd.cpp",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "libgatekeeper",
+        "libgsi",
+        "liblog",
+        "libhardware",
+        "libbase",
+        "libutils",
+        "libcrypto",
+        "libkeystore_aidl",
+        "libkeystore_binder",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "android.hardware.gatekeeper@1.0",
+        "libgatekeeper_aidl",
+    ],
+
+    static_libs: ["libscrypt_static"],
+    include_dirs: ["external/scrypt/lib/crypto"],
+    init_rc: ["gatekeeperd.rc"],
+}
+
+filegroup {
+    name: "gatekeeper_aidl",
+    srcs: [
+        "binder/android/service/gatekeeper/IGateKeeperService.aidl",
+    ],
+    path: "binder",
+}
+
+cc_library_shared {
+    name: "libgatekeeper_aidl",
+    srcs: [
+        ":gatekeeper_aidl",
+        "GateKeeperResponse.cpp",
+    ],
+    aidl: {
+        export_aidl_headers: true,
+        include_dirs: [
+            "system/core/gatekeeperd/binder",
+            "frameworks/base/core/java/",
+        ],
+    },
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "liblog",
+        "libutils",
+    ],
+    export_shared_lib_headers: [
+        "libbinder",
+    ],
+}
diff --git a/gatekeeperd/Android.mk b/gatekeeperd/Android.mk
deleted file mode 100644
index 6d5d1ea..0000000
--- a/gatekeeperd/Android.mk
+++ /dev/null
@@ -1,47 +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 := \
-	SoftGateKeeperDevice.cpp \
-	IGateKeeperService.cpp \
-	gatekeeperd.cpp
-
-LOCAL_MODULE := gatekeeperd
-LOCAL_SHARED_LIBRARIES := \
-	libbinder \
-	libgatekeeper \
-	liblog \
-	libhardware \
-	libbase \
-	libutils \
-	libcrypto \
-	libkeystore_aidl \
-	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
-include $(BUILD_EXECUTABLE)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/gatekeeperd/GateKeeperResponse.cpp b/gatekeeperd/GateKeeperResponse.cpp
new file mode 100644
index 0000000..ca0c98f
--- /dev/null
+++ b/gatekeeperd/GateKeeperResponse.cpp
@@ -0,0 +1,83 @@
+/*
+**
+** Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT 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 "gatekeeperd"
+
+#include <gatekeeper/GateKeeperResponse.h>
+
+#include <binder/Parcel.h>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace service {
+namespace gatekeeper {
+
+status_t GateKeeperResponse::readFromParcel(const Parcel* in) {
+    if (in == nullptr) {
+        LOG(ERROR) << "readFromParcel got null in parameter";
+        return BAD_VALUE;
+    }
+    timeout_ = 0;
+    should_reenroll_ = false;
+    payload_ = {};
+    response_code_ = ResponseCode(in->readInt32());
+    if (response_code_ == ResponseCode::OK) {
+        should_reenroll_ = in->readInt32();
+        ssize_t length = in->readInt32();
+        if (length > 0) {
+            length = in->readInt32();
+            const uint8_t* buf = reinterpret_cast<const uint8_t*>(in->readInplace(length));
+            if (buf == nullptr) {
+                LOG(ERROR) << "readInplace returned null buffer for length " << length;
+                return BAD_VALUE;
+            }
+            payload_.resize(length);
+            std::copy(buf, buf + length, payload_.data());
+        }
+    } else if (response_code_ == ResponseCode::RETRY) {
+        timeout_ = in->readInt32();
+    }
+    return NO_ERROR;
+}
+status_t GateKeeperResponse::writeToParcel(Parcel* out) const {
+    if (out == nullptr) {
+        LOG(ERROR) << "writeToParcel got null out parameter";
+        return BAD_VALUE;
+    }
+    out->writeInt32(int32_t(response_code_));
+    if (response_code_ == ResponseCode::OK) {
+        out->writeInt32(should_reenroll_);
+        out->writeInt32(payload_.size());
+        if (payload_.size() != 0) {
+            out->writeInt32(payload_.size());
+            uint8_t* buf = reinterpret_cast<uint8_t*>(out->writeInplace(payload_.size()));
+            if (buf == nullptr) {
+                LOG(ERROR) << "writeInplace returned null buffer for length " << payload_.size();
+                return BAD_VALUE;
+            }
+            std::copy(payload_.begin(), payload_.end(), buf);
+        }
+    } else if (response_code_ == ResponseCode::RETRY) {
+        out->writeInt32(timeout_);
+    }
+    return NO_ERROR;
+}
+
+}  // namespace gatekeeper
+}  // namespace service
+}  // namespace android
diff --git a/gatekeeperd/IGateKeeperService.cpp b/gatekeeperd/IGateKeeperService.cpp
deleted file mode 100644
index 1c339f4..0000000
--- a/gatekeeperd/IGateKeeperService.cpp
+++ /dev/null
@@ -1,173 +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.
-*/
-
-#define LOG_TAG "GateKeeperService"
-#include <utils/Log.h>
-
-#include "IGateKeeperService.h"
-
-namespace android {
-
-const android::String16 IGateKeeperService::descriptor("android.service.gatekeeper.IGateKeeperService");
-const android::String16& IGateKeeperService::getInterfaceDescriptor() const {
-    return IGateKeeperService::descriptor;
-}
-
-status_t BnGateKeeperService::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
-    switch(code) {
-        case ENROLL: {
-            CHECK_INTERFACE(IGateKeeperService, data, reply);
-            uint32_t uid = data.readInt32();
-
-            ssize_t currentPasswordHandleSize = data.readInt32();
-            const uint8_t *currentPasswordHandle =
-                    static_cast<const uint8_t *>(data.readInplace(currentPasswordHandleSize));
-            if (!currentPasswordHandle) currentPasswordHandleSize = 0;
-
-            ssize_t currentPasswordSize = data.readInt32();
-            const uint8_t *currentPassword =
-                    static_cast<const uint8_t *>(data.readInplace(currentPasswordSize));
-            if (!currentPassword) currentPasswordSize = 0;
-
-            ssize_t desiredPasswordSize = data.readInt32();
-            const uint8_t *desiredPassword =
-                    static_cast<const uint8_t *>(data.readInplace(desiredPasswordSize));
-            if (!desiredPassword) desiredPasswordSize = 0;
-
-            uint8_t *out = NULL;
-            uint32_t outSize = 0;
-            int ret = enroll(uid, currentPasswordHandle, currentPasswordHandleSize,
-                    currentPassword, currentPasswordSize, desiredPassword,
-                    desiredPasswordSize, &out, &outSize);
-
-            reply->writeNoException();
-            reply->writeInt32(1);
-            if (ret == 0 && outSize > 0 && out != NULL) {
-                reply->writeInt32(GATEKEEPER_RESPONSE_OK);
-                reply->writeInt32(0);
-                reply->writeInt32(outSize);
-                reply->writeInt32(outSize);
-                void *buf = reply->writeInplace(outSize);
-                memcpy(buf, out, outSize);
-                delete[] out;
-            } else if (ret > 0) {
-                reply->writeInt32(GATEKEEPER_RESPONSE_RETRY);
-                reply->writeInt32(ret);
-            } else {
-                reply->writeInt32(GATEKEEPER_RESPONSE_ERROR);
-            }
-            return NO_ERROR;
-        }
-        case VERIFY: {
-            CHECK_INTERFACE(IGateKeeperService, data, reply);
-            uint32_t uid = data.readInt32();
-            ssize_t currentPasswordHandleSize = data.readInt32();
-            const uint8_t *currentPasswordHandle =
-                    static_cast<const uint8_t *>(data.readInplace(currentPasswordHandleSize));
-            if (!currentPasswordHandle) currentPasswordHandleSize = 0;
-
-            ssize_t currentPasswordSize = data.readInt32();
-            const uint8_t *currentPassword =
-                static_cast<const uint8_t *>(data.readInplace(currentPasswordSize));
-            if (!currentPassword) currentPasswordSize = 0;
-
-            bool request_reenroll = false;
-            int ret = verify(uid, (uint8_t *) currentPasswordHandle,
-                    currentPasswordHandleSize, (uint8_t *) currentPassword, currentPasswordSize,
-                    &request_reenroll);
-
-            reply->writeNoException();
-            reply->writeInt32(1);
-            if (ret == 0) {
-                reply->writeInt32(GATEKEEPER_RESPONSE_OK);
-                reply->writeInt32(request_reenroll ? 1 : 0);
-                reply->writeInt32(0); // no payload returned from this call
-            } else if (ret > 0) {
-                reply->writeInt32(GATEKEEPER_RESPONSE_RETRY);
-                reply->writeInt32(ret);
-            } else {
-                reply->writeInt32(GATEKEEPER_RESPONSE_ERROR);
-            }
-            return NO_ERROR;
-        }
-        case VERIFY_CHALLENGE: {
-            CHECK_INTERFACE(IGateKeeperService, data, reply);
-            uint32_t uid = data.readInt32();
-            uint64_t challenge = data.readInt64();
-            ssize_t currentPasswordHandleSize = data.readInt32();
-            const uint8_t *currentPasswordHandle =
-                    static_cast<const uint8_t *>(data.readInplace(currentPasswordHandleSize));
-            if (!currentPasswordHandle) currentPasswordHandleSize = 0;
-
-            ssize_t currentPasswordSize = data.readInt32();
-            const uint8_t *currentPassword =
-                static_cast<const uint8_t *>(data.readInplace(currentPasswordSize));
-            if (!currentPassword) currentPasswordSize = 0;
-
-
-            uint8_t *out = NULL;
-            uint32_t outSize = 0;
-            bool request_reenroll = false;
-            int ret = verifyChallenge(uid, challenge, (uint8_t *) currentPasswordHandle,
-                    currentPasswordHandleSize, (uint8_t *) currentPassword, currentPasswordSize,
-                    &out, &outSize, &request_reenroll);
-            reply->writeNoException();
-            reply->writeInt32(1);
-            if (ret == 0 && outSize > 0 && out != NULL) {
-                reply->writeInt32(GATEKEEPER_RESPONSE_OK);
-                reply->writeInt32(request_reenroll ? 1 : 0);
-                reply->writeInt32(outSize);
-                reply->writeInt32(outSize);
-                void *buf = reply->writeInplace(outSize);
-                memcpy(buf, out, outSize);
-                delete[] out;
-            } else if (ret > 0) {
-                reply->writeInt32(GATEKEEPER_RESPONSE_RETRY);
-                reply->writeInt32(ret);
-            } else {
-                reply->writeInt32(GATEKEEPER_RESPONSE_ERROR);
-            }
-            return NO_ERROR;
-        }
-        case GET_SECURE_USER_ID: {
-            CHECK_INTERFACE(IGateKeeperService, data, reply);
-            uint32_t uid = data.readInt32();
-            uint64_t sid = getSecureUserId(uid);
-            reply->writeNoException();
-            reply->writeInt64(sid);
-            return NO_ERROR;
-        }
-        case CLEAR_SECURE_USER_ID: {
-            CHECK_INTERFACE(IGateKeeperService, data, reply);
-            uint32_t uid = data.readInt32();
-            clearSecureUserId(uid);
-            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);
-    }
-};
-
-
-}; // namespace android
diff --git a/gatekeeperd/IGateKeeperService.h b/gatekeeperd/IGateKeeperService.h
deleted file mode 100644
index 2816efc..0000000
--- a/gatekeeperd/IGateKeeperService.h
+++ /dev/null
@@ -1,118 +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 IGATEKEEPER_SERVICE_H_
-#define IGATEKEEPER_SERVICE_H_
-
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-namespace android {
-
-/*
- * This must be kept manually in sync with frameworks/base's IGateKeeperService.aidl
- */
-class IGateKeeperService : public IInterface {
-public:
-    enum {
-        ENROLL = IBinder::FIRST_CALL_TRANSACTION + 0,
-        VERIFY = IBinder::FIRST_CALL_TRANSACTION + 1,
-        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 {
-        GATEKEEPER_RESPONSE_OK = 0,
-        GATEKEEPER_RESPONSE_RETRY = 1,
-        GATEKEEPER_RESPONSE_ERROR = -1,
-    };
-
-    // DECLARE_META_INTERFACE - C++ client interface not needed
-    static const android::String16 descriptor;
-    virtual const android::String16& getInterfaceDescriptor() const;
-    IGateKeeperService() {}
-    virtual ~IGateKeeperService() {}
-
-    /**
-     * Enrolls a password with the GateKeeper. Returns 0 on success, negative on failure.
-     * Returns:
-     * - 0 on success
-     * - A timestamp T > 0 if the call has failed due to throttling and should not
-     *   be reattempted until T milliseconds have elapsed
-     * - -1 on failure
-     */
-    virtual int enroll(uint32_t uid,
-            const uint8_t *current_password_handle, uint32_t current_password_handle_length,
-            const uint8_t *current_password, uint32_t current_password_length,
-            const uint8_t *desired_password, uint32_t desired_password_length,
-            uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) = 0;
-
-    /**
-     * Verifies a password previously enrolled with the GateKeeper.
-     * Returns:
-     * - 0 on success
-     * - A timestamp T > 0 if the call has failed due to throttling and should not
-     *   be reattempted until T milliseconds have elapsed
-     * - -1 on failure
-     */
-    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) = 0;
-
-    /**
-     * Verifies a password previously enrolled with the GateKeeper.
-     * Returns:
-     * - 0 on success
-     * - A timestamp T > 0 if the call has failed due to throttling and should not
-     *   be reattempted until T milliseconds have elapsed
-     * - -1 on failure
-     */
-    virtual int verifyChallenge(uint32_t uid, uint64_t challenge,
-            const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
-            const uint8_t *provided_password, uint32_t provided_password_length,
-            uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) = 0;
-    /**
-     * Returns the secure user ID for the provided android user
-     */
-    virtual uint64_t getSecureUserId(uint32_t uid) = 0;
-
-    /**
-     * 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;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnGateKeeperService: public BnInterface<IGateKeeperService> {
-public:
-    virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-            uint32_t flags = 0);
-};
-
-} // namespace android
-
-#endif
-
diff --git a/gatekeeperd/SoftGateKeeper.h b/gatekeeperd/SoftGateKeeper.h
deleted file mode 100644
index 2f4f4d7..0000000
--- a/gatekeeperd/SoftGateKeeper.h
+++ /dev/null
@@ -1,182 +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.
- *
- */
-
-#ifndef SOFT_GATEKEEPER_H_
-#define SOFT_GATEKEEPER_H_
-
-extern "C" {
-#include <openssl/rand.h>
-#include <openssl/sha.h>
-
-#include <crypto_scrypt.h>
-}
-
-#include <android-base/memory.h>
-#include <gatekeeper/gatekeeper.h>
-
-#include <iostream>
-#include <unordered_map>
-#include <memory>
-
-namespace gatekeeper {
-
-struct fast_hash_t {
-    uint64_t salt;
-    uint8_t digest[SHA256_DIGEST_LENGTH];
-};
-
-class SoftGateKeeper : public GateKeeper {
-public:
-    static const uint32_t SIGNATURE_LENGTH_BYTES = 32;
-
-    // scrypt params
-    static const uint64_t N = 16384;
-    static const uint32_t r = 8;
-    static const uint32_t p = 1;
-
-    static const int MAX_UINT_32_CHARS = 11;
-
-    SoftGateKeeper() {
-        key_.reset(new uint8_t[SIGNATURE_LENGTH_BYTES]);
-        memset(key_.get(), 0, SIGNATURE_LENGTH_BYTES);
-    }
-
-    virtual ~SoftGateKeeper() {
-    }
-
-    virtual bool GetAuthTokenKey(const uint8_t **auth_token_key,
-            uint32_t *length) const {
-        if (auth_token_key == NULL || length == NULL) return false;
-        uint8_t *auth_token_key_copy = new uint8_t[SIGNATURE_LENGTH_BYTES];
-        memcpy(auth_token_key_copy, key_.get(), SIGNATURE_LENGTH_BYTES);
-
-        *auth_token_key = auth_token_key_copy;
-        *length = SIGNATURE_LENGTH_BYTES;
-        return true;
-    }
-
-    virtual void GetPasswordKey(const uint8_t **password_key, uint32_t *length) {
-        if (password_key == NULL || length == NULL) return;
-        uint8_t *password_key_copy = new uint8_t[SIGNATURE_LENGTH_BYTES];
-        memcpy(password_key_copy, key_.get(), SIGNATURE_LENGTH_BYTES);
-
-        *password_key = password_key_copy;
-        *length = SIGNATURE_LENGTH_BYTES;
-    }
-
-    virtual void ComputePasswordSignature(uint8_t *signature, uint32_t signature_length,
-            const uint8_t *, uint32_t, const uint8_t *password,
-            uint32_t password_length, salt_t salt) const {
-        if (signature == NULL) return;
-        crypto_scrypt(password, password_length, reinterpret_cast<uint8_t *>(&salt),
-                sizeof(salt), N, r, p, signature, signature_length);
-    }
-
-    virtual void GetRandom(void *random, uint32_t requested_length) const {
-        if (random == NULL) return;
-        RAND_pseudo_bytes((uint8_t *) random, requested_length);
-    }
-
-    virtual void ComputeSignature(uint8_t *signature, uint32_t signature_length,
-            const uint8_t *, uint32_t, const uint8_t *, const uint32_t) const {
-        if (signature == NULL) return;
-        memset(signature, 0, signature_length);
-    }
-
-    virtual uint64_t GetMillisecondsSinceBoot() const {
-        struct timespec time;
-        int res = clock_gettime(CLOCK_BOOTTIME, &time);
-        if (res < 0) return 0;
-        return (time.tv_sec * 1000) + (time.tv_nsec / 1000 / 1000);
-    }
-
-    virtual bool IsHardwareBacked() const {
-        return false;
-    }
-
-    virtual bool GetFailureRecord(uint32_t uid, secure_id_t user_id, failure_record_t *record,
-            bool /* secure */) {
-        failure_record_t *stored = &failure_map_[uid];
-        if (user_id != stored->secure_user_id) {
-            stored->secure_user_id = user_id;
-            stored->last_checked_timestamp = 0;
-            stored->failure_counter = 0;
-        }
-        memcpy(record, stored, sizeof(*record));
-        return true;
-    }
-
-    virtual bool ClearFailureRecord(uint32_t uid, secure_id_t user_id, bool /* secure */) {
-        failure_record_t *stored = &failure_map_[uid];
-        stored->secure_user_id = user_id;
-        stored->last_checked_timestamp = 0;
-        stored->failure_counter = 0;
-        return true;
-    }
-
-    virtual bool WriteFailureRecord(uint32_t uid, failure_record_t *record, bool /* secure */) {
-        failure_map_[uid] = *record;
-        return true;
-    }
-
-    fast_hash_t ComputeFastHash(const SizedBuffer &password, uint64_t salt) {
-        fast_hash_t fast_hash;
-        size_t digest_size = password.length + sizeof(salt);
-        std::unique_ptr<uint8_t[]> digest(new uint8_t[digest_size]);
-        memcpy(digest.get(), &salt, sizeof(salt));
-        memcpy(digest.get() + sizeof(salt), password.buffer.get(), password.length);
-
-        SHA256(digest.get(), digest_size, (uint8_t *) &fast_hash.digest);
-
-        fast_hash.salt = salt;
-        return fast_hash;
-    }
-
-    bool VerifyFast(const fast_hash_t &fast_hash, const SizedBuffer &password) {
-        fast_hash_t computed = ComputeFastHash(password, fast_hash.salt);
-        return memcmp(computed.digest, fast_hash.digest, SHA256_DIGEST_LENGTH) == 0;
-    }
-
-    bool DoVerify(const password_handle_t *expected_handle, const SizedBuffer &password) {
-        uint64_t user_id = android::base::get_unaligned<secure_id_t>(&expected_handle->user_id);
-        FastHashMap::const_iterator it = fast_hash_map_.find(user_id);
-        if (it != fast_hash_map_.end() && VerifyFast(it->second, password)) {
-            return true;
-        } else {
-            if (GateKeeper::DoVerify(expected_handle, password)) {
-                uint64_t salt;
-                GetRandom(&salt, sizeof(salt));
-                fast_hash_map_[user_id] = ComputeFastHash(password, salt);
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-private:
-
-    typedef std::unordered_map<uint32_t, failure_record_t> FailureRecordMap;
-    typedef std::unordered_map<uint64_t, fast_hash_t> FastHashMap;
-
-    std::unique_ptr<uint8_t[]> key_;
-    FailureRecordMap failure_map_;
-    FastHashMap fast_hash_map_;
-};
-}
-
-#endif // SOFT_GATEKEEPER_H_
diff --git a/gatekeeperd/SoftGateKeeperDevice.cpp b/gatekeeperd/SoftGateKeeperDevice.cpp
deleted file mode 100644
index f5e2ce6..0000000
--- a/gatekeeperd/SoftGateKeeperDevice.cpp
+++ /dev/null
@@ -1,110 +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 "SoftGateKeeper.h"
-#include "SoftGateKeeperDevice.h"
-
-namespace android {
-
-int SoftGateKeeperDevice::enroll(uint32_t uid,
-            const uint8_t *current_password_handle, uint32_t current_password_handle_length,
-            const uint8_t *current_password, uint32_t current_password_length,
-            const uint8_t *desired_password, uint32_t desired_password_length,
-            uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) {
-
-    if (enrolled_password_handle == NULL || enrolled_password_handle_length == NULL ||
-            desired_password == NULL || desired_password_length == 0)
-        return -EINVAL;
-
-    // Current password and current password handle go together
-    if (current_password_handle == NULL || current_password_handle_length == 0 ||
-            current_password == NULL || current_password_length == 0) {
-        current_password_handle = NULL;
-        current_password_handle_length = 0;
-        current_password = NULL;
-        current_password_length = 0;
-    }
-
-    SizedBuffer desired_password_buffer(desired_password_length);
-    memcpy(desired_password_buffer.buffer.get(), desired_password, desired_password_length);
-
-    SizedBuffer current_password_handle_buffer(current_password_handle_length);
-    if (current_password_handle) {
-        memcpy(current_password_handle_buffer.buffer.get(), current_password_handle,
-                current_password_handle_length);
-    }
-
-    SizedBuffer current_password_buffer(current_password_length);
-    if (current_password) {
-        memcpy(current_password_buffer.buffer.get(), current_password, current_password_length);
-    }
-
-    EnrollRequest request(uid, &current_password_handle_buffer, &desired_password_buffer,
-            &current_password_buffer);
-    EnrollResponse response;
-
-    impl_->Enroll(request, &response);
-
-    if (response.error == ERROR_RETRY) {
-        return response.retry_timeout;
-    } else if (response.error != ERROR_NONE) {
-        return -EINVAL;
-    }
-
-    *enrolled_password_handle = response.enrolled_password_handle.buffer.release();
-    *enrolled_password_handle_length = response.enrolled_password_handle.length;
-    return 0;
-}
-
-int SoftGateKeeperDevice::verify(uint32_t uid,
-        uint64_t challenge, const uint8_t *enrolled_password_handle,
-        uint32_t enrolled_password_handle_length, const uint8_t *provided_password,
-        uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length,
-        bool *request_reenroll) {
-
-    if (enrolled_password_handle == NULL ||
-            provided_password == NULL) {
-        return -EINVAL;
-    }
-
-    SizedBuffer password_handle_buffer(enrolled_password_handle_length);
-    memcpy(password_handle_buffer.buffer.get(), enrolled_password_handle,
-            enrolled_password_handle_length);
-    SizedBuffer provided_password_buffer(provided_password_length);
-    memcpy(provided_password_buffer.buffer.get(), provided_password, provided_password_length);
-
-    VerifyRequest request(uid, challenge, &password_handle_buffer, &provided_password_buffer);
-    VerifyResponse response;
-
-    impl_->Verify(request, &response);
-
-    if (response.error == ERROR_RETRY) {
-        return response.retry_timeout;
-    } else if (response.error != ERROR_NONE) {
-        return -EINVAL;
-    }
-
-    if (auth_token != NULL && auth_token_length != NULL) {
-       *auth_token = response.auth_token.buffer.release();
-       *auth_token_length = response.auth_token.length;
-    }
-
-    if (request_reenroll != NULL) {
-        *request_reenroll = response.request_reenroll;
-    }
-
-    return 0;
-}
-} // namespace android
diff --git a/gatekeeperd/SoftGateKeeperDevice.h b/gatekeeperd/SoftGateKeeperDevice.h
deleted file mode 100644
index e3dc068..0000000
--- a/gatekeeperd/SoftGateKeeperDevice.h
+++ /dev/null
@@ -1,76 +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.
- */
-
-#ifndef SOFT_GATEKEEPER_DEVICE_H_
-#define SOFT_GATEKEEPER_DEVICE_H_
-
-#include "SoftGateKeeper.h"
-
-#include <memory>
-
-using namespace gatekeeper;
-
-namespace android {
-
-/**
- * Software based GateKeeper implementation
- */
-class SoftGateKeeperDevice {
-public:
-    SoftGateKeeperDevice() {
-        impl_.reset(new SoftGateKeeper());
-    }
-
-   // Wrappers to translate the gatekeeper HAL API to the Kegyuard Messages API.
-
-    /**
-     * Enrolls password_payload, which should be derived from a user selected pin or password,
-     * with the authentication factor private key used only for enrolling authentication
-     * factor data.
-     *
-     * Returns: 0 on success or an error code less than 0 on error.
-     * On error, enrolled_password_handle will not be allocated.
-     */
-    int enroll(uint32_t uid,
-            const uint8_t *current_password_handle, uint32_t current_password_handle_length,
-            const uint8_t *current_password, uint32_t current_password_length,
-            const uint8_t *desired_password, uint32_t desired_password_length,
-            uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length);
-
-    /**
-     * Verifies provided_password matches enrolled_password_handle.
-     *
-     * Implementations of this module may retain the result of this call
-     * to attest to the recency of authentication.
-     *
-     * On success, writes the address of a verification token to auth_token,
-     * usable to attest password verification to other trusted services. Clients
-     * may pass NULL for this value.
-     *
-     * Returns: 0 on success or an error code less than 0 on error
-     * On error, verification token will not be allocated
-     */
-    int verify(uint32_t uid, uint64_t challenge,
-            const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
-            const uint8_t *provided_password, uint32_t provided_password_length,
-            uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll);
-private:
-    std::unique_ptr<SoftGateKeeper> impl_;
-};
-
-} // namespace gatekeeper
-
-#endif //SOFT_GATEKEEPER_DEVICE_H_
diff --git a/gatekeeperd/binder/android/service/gatekeeper/GateKeeperResponse.aidl b/gatekeeperd/binder/android/service/gatekeeper/GateKeeperResponse.aidl
new file mode 100644
index 0000000..097bb54
--- /dev/null
+++ b/gatekeeperd/binder/android/service/gatekeeper/GateKeeperResponse.aidl
@@ -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.
+ */
+
+package android.service.gatekeeper;
+
+/**
+ * Response object for a GateKeeper verification request.
+ * @hide
+ */
+parcelable GateKeeperResponse cpp_header "gatekeeper/GateKeeperResponse.h";
+
diff --git a/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl b/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl
new file mode 100644
index 0000000..57adaba
--- /dev/null
+++ b/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+package android.service.gatekeeper;
+
+import android.service.gatekeeper.GateKeeperResponse;
+
+/**
+ * Interface for communication with GateKeeper, the
+ * secure password storage daemon.
+ *
+ * This must be kept manually in sync with system/core/gatekeeperd
+ * until AIDL can generate both C++ and Java bindings.
+ *
+ * @hide
+ */
+interface IGateKeeperService {
+    /**
+     * Enrolls a password, returning the handle to the enrollment to be stored locally.
+     * @param uid The Android user ID associated to this enrollment
+     * @param currentPasswordHandle The previously enrolled handle, or null if none
+     * @param currentPassword The previously enrolled plaintext password, or null if none.
+     *                        If provided, must verify against the currentPasswordHandle.
+     * @param desiredPassword The new desired password, for which a handle will be returned
+     *                        upon success.
+     * @return an EnrollResponse or null on failure
+     */
+    GateKeeperResponse enroll(int uid, in @nullable byte[] currentPasswordHandle,
+            in @nullable byte[] currentPassword, in byte[] desiredPassword);
+
+    /**
+     * Verifies an enrolled handle against a provided, plaintext blob.
+     * @param uid The Android user ID associated to this enrollment
+     * @param enrolledPasswordHandle The handle against which the provided password will be
+     *                               verified.
+     * @param The plaintext blob to verify against enrolledPassword.
+     * @return a VerifyResponse, or null on failure.
+     */
+    GateKeeperResponse verify(int uid, in byte[] enrolledPasswordHandle, in byte[] providedPassword);
+
+    /**
+     * Verifies an enrolled handle against a provided, plaintext blob.
+     * @param uid The Android user ID associated to this enrollment
+     * @param challenge a challenge to authenticate agaisnt the device credential. If successful
+     *                  authentication occurs, this value will be written to the returned
+     *                  authentication attestation.
+     * @param enrolledPasswordHandle The handle against which the provided password will be
+     *                               verified.
+     * @param The plaintext blob to verify against enrolledPassword.
+     * @return a VerifyResponse with an attestation, or null on failure.
+     */
+    GateKeeperResponse verifyChallenge(int uid, long challenge, in byte[] enrolledPasswordHandle,
+            in byte[] providedPassword);
+
+    /**
+     * Retrieves the secure identifier for the user with the provided Android ID,
+     * or 0 if none is found.
+     * @param uid the Android user id
+     */
+    long getSecureUserId(int uid);
+
+    /**
+     * Clears secure user id associated with the provided Android ID.
+     * Must be called when password is set to NONE.
+     * @param uid the Android user id.
+     */
+    void clearSecureUserId(int uid);
+
+    /**
+     * 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).
+     */
+    void reportDeviceSetupComplete();
+}
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 5781765..1d65b1c 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -16,7 +16,8 @@
 
 #define LOG_TAG "gatekeeperd"
 
-#include "IGateKeeperService.h"
+#include <android/service/gatekeeper/BnGateKeeperService.h>
+#include <gatekeeper/GateKeeperResponse.h>
 
 #include <errno.h>
 #include <fcntl.h>
@@ -25,7 +26,9 @@
 #include <unistd.h>
 #include <memory>
 
-#include <android/security/IKeystoreService.h>
+#include <android/security/keystore/IKeystoreService.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/PermissionCache.h>
@@ -34,12 +37,11 @@
 #include <hardware/hw_auth_token.h>
 #include <keystore/keystore.h> // For error code
 #include <keystore/keystore_return_types.h>
+#include <libgsi/libgsi.h>
 #include <log/log.h>
 #include <utils/Log.h>
 #include <utils/String16.h>
 
-#include "SoftGateKeeperDevice.h"
-
 #include <hidl/HidlSupport.h>
 #include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
 
@@ -49,6 +51,11 @@
 using android::hardware::gatekeeper::V1_0::GatekeeperResponse;
 using android::hardware::Return;
 
+using ::android::binder::Status;
+using ::android::service::gatekeeper::BnGateKeeperService;
+using GKResponse = ::android::service::gatekeeper::GateKeeperResponse;
+using GKResponseCode = ::android::service::gatekeeper::ResponseCode;
+
 namespace android {
 
 static const String16 KEYGUARD_PERMISSION("android.permission.ACCESS_KEYGUARD_SECURE_STORAGE");
@@ -59,10 +66,10 @@
     GateKeeperProxy() {
         clear_state_if_needed_done = false;
         hw_device = IGatekeeper::getService();
+        is_running_gsi = android::base::GetBoolProperty(android::gsi::kGsiBootedProp, false);
 
-        if (hw_device == nullptr) {
-            ALOGW("falling back to software GateKeeper");
-            soft_device.reset(new SoftGateKeeperDevice());
+        if (!hw_device) {
+            LOG(ERROR) << "Could not find Gatekeeper device, which makes me very sad.";
         }
     }
 
@@ -86,9 +93,9 @@
             return;
         }
 
-        if (mark_cold_boot()) {
+        if (mark_cold_boot() && !is_running_gsi) {
             ALOGI("cold boot: clearing state");
-            if (hw_device != nullptr) {
+            if (hw_device) {
                 hw_device->deleteAllUsers([](const GatekeeperResponse &){});
             }
         }
@@ -138,16 +145,28 @@
         }
     }
 
-    virtual int enroll(uint32_t uid,
-            const uint8_t *current_password_handle, uint32_t current_password_handle_length,
-            const uint8_t *current_password, uint32_t current_password_length,
-            const uint8_t *desired_password, uint32_t desired_password_length,
-            uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) {
+    // This should only be called on uids being passed to the GateKeeper HAL. It ensures that
+    // secure storage shared across a GSI image and a host image will not overlap.
+    uint32_t adjust_uid(uint32_t uid) {
+        static constexpr uint32_t kGsiOffset = 1000000;
+        CHECK(uid < kGsiOffset);
+        CHECK(hw_device != nullptr);
+        if (is_running_gsi) {
+            return uid + kGsiOffset;
+        }
+        return uid;
+    }
+
+#define GK_ERROR *gkResponse = GKResponse::error(), Status::ok()
+
+    Status enroll(int32_t uid, const std::unique_ptr<std::vector<uint8_t>>& currentPasswordHandle,
+                  const std::unique_ptr<std::vector<uint8_t>>& currentPassword,
+                  const std::vector<uint8_t>& desiredPassword, GKResponse* gkResponse) override {
         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)) {
-            return PERMISSION_DENIED;
+            return GK_ERROR;
         }
 
         // Make sure to clear any state from before factory reset as soon as a credential is
@@ -155,220 +174,189 @@
         clear_state_if_needed();
 
         // need a desired password to enroll
-        if (desired_password_length == 0) return -EINVAL;
+        if (desiredPassword.size() == 0) return GK_ERROR;
 
-        int ret;
-        if (hw_device != nullptr) {
-            const gatekeeper::password_handle_t *handle =
-                    reinterpret_cast<const gatekeeper::password_handle_t *>(current_password_handle);
+        if (!hw_device) {
+            LOG(ERROR) << "has no HAL to talk to";
+            return GK_ERROR;
+        }
 
-            if (handle != NULL && handle->version != 0 && !handle->hardware_backed) {
-                // handle is being re-enrolled from a software version. HAL probably won't accept
-                // the handle as valid, so we nullify it and enroll from scratch
-                current_password_handle = NULL;
-                current_password_handle_length = 0;
-                current_password = NULL;
-                current_password_length = 0;
+        android::hardware::hidl_vec<uint8_t> curPwdHandle;
+        android::hardware::hidl_vec<uint8_t> curPwd;
+
+        if (currentPasswordHandle && currentPassword) {
+            if (currentPasswordHandle->size() != sizeof(gatekeeper::password_handle_t)) {
+                LOG(INFO) << "Password handle has wrong length";
+                return GK_ERROR;
             }
+            curPwdHandle.setToExternal(const_cast<uint8_t*>(currentPasswordHandle->data()),
+                                       currentPasswordHandle->size());
+            curPwd.setToExternal(const_cast<uint8_t*>(currentPassword->data()),
+                                 currentPassword->size());
+        }
 
-            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);
+        android::hardware::hidl_vec<uint8_t> newPwd;
+        newPwd.setToExternal(const_cast<uint8_t*>(desiredPassword.data()), desiredPassword.size());
 
-            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);
+        uint32_t hw_uid = adjust_uid(uid);
+        Return<void> hwRes = hw_device->enroll(
+                hw_uid, curPwdHandle, curPwd, newPwd, [&gkResponse](const GatekeeperResponse& rsp) {
+                    if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
+                        *gkResponse = GKResponse::ok({rsp.data.begin(), rsp.data.end()});
+                    } else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT &&
+                               rsp.timeout > 0) {
+                        *gkResponse = GKResponse::retry(rsp.timeout);
+                    } else {
+                        *gkResponse = GKResponse::error();
                     }
-                    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;
+                });
+        if (!hwRes.isOk()) {
+            LOG(ERROR) << "enroll transaction failed";
+            return GK_ERROR;
+        }
+
+        if (gkResponse->response_code() == GKResponseCode::OK && !gkResponse->should_reenroll()) {
+            if (gkResponse->payload().size() != sizeof(gatekeeper::password_handle_t)) {
+                LOG(ERROR) << "HAL returned password handle of invalid length "
+                           << gkResponse->payload().size();
+                return GK_ERROR;
             }
-        } else {
-            ret = soft_device->enroll(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);
-        }
 
-        if (ret == GATEKEEPER_RESPONSE_OK && (*enrolled_password_handle == nullptr ||
-            *enrolled_password_handle_length != sizeof(password_handle_t))) {
-            ret = GATEKEEPER_RESPONSE_ERROR;
-            ALOGE("HAL: password_handle=%p size_of_handle=%" PRIu32 "\n",
-                  *enrolled_password_handle, *enrolled_password_handle_length);
-        }
-
-        if (ret == GATEKEEPER_RESPONSE_OK) {
-            gatekeeper::password_handle_t *handle =
-                    reinterpret_cast<gatekeeper::password_handle_t *>(*enrolled_password_handle);
+            const gatekeeper::password_handle_t* handle =
+                    reinterpret_cast<const gatekeeper::password_handle_t*>(
+                            gkResponse->payload().data());
             store_sid(uid, handle->user_id);
-            bool rr;
 
+            GKResponse verifyResponse;
             // immediately verify this password so we don't ask the user to enter it again
             // if they just created it.
-            verify(uid, *enrolled_password_handle, sizeof(password_handle_t), desired_password,
-                    desired_password_length, &rr);
+            auto status = verify(uid, gkResponse->payload(), desiredPassword, &verifyResponse);
+            if (!status.isOk() || verifyResponse.response_code() != GKResponseCode::OK) {
+                LOG(ERROR) << "Failed to verify password after enrolling";
+            }
         }
 
-        return ret;
+        return Status::ok();
     }
 
-    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;
-        uint32_t auth_token_length;
-        return verifyChallenge(uid, 0, enrolled_password_handle, enrolled_password_handle_length,
-                provided_password, provided_password_length,
-                &auth_token, &auth_token_length, request_reenroll);
+    Status verify(int32_t uid, const ::std::vector<uint8_t>& enrolledPasswordHandle,
+                  const ::std::vector<uint8_t>& providedPassword, GKResponse* gkResponse) override {
+        return verifyChallenge(uid, 0 /* challenge */, enrolledPasswordHandle, providedPassword,
+                               gkResponse);
     }
 
-    virtual int verifyChallenge(uint32_t uid, uint64_t challenge,
-            const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
-            const uint8_t *provided_password, uint32_t provided_password_length,
-            uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) {
+    Status verifyChallenge(int32_t uid, int64_t challenge,
+                           const std::vector<uint8_t>& enrolledPasswordHandle,
+                           const std::vector<uint8_t>& providedPassword,
+                           GKResponse* gkResponse) override {
         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)) {
-            return PERMISSION_DENIED;
+            return GK_ERROR;
         }
 
         // can't verify if we're missing either param
-        if ((enrolled_password_handle_length | provided_password_length) == 0)
-            return -EINVAL;
+        if (enrolledPasswordHandle.size() == 0 || providedPassword.size() == 0) return GK_ERROR;
 
-        int ret;
-        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) {
-                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 (!hw_device) return GK_ERROR;
+
+        if (enrolledPasswordHandle.size() != sizeof(gatekeeper::password_handle_t)) {
+            LOG(INFO) << "Password handle has wrong length";
+            return GK_ERROR;
+        }
+        const gatekeeper::password_handle_t* handle =
+                reinterpret_cast<const gatekeeper::password_handle_t*>(
+                        enrolledPasswordHandle.data());
+
+        uint32_t hw_uid = adjust_uid(uid);
+        android::hardware::hidl_vec<uint8_t> curPwdHandle;
+        curPwdHandle.setToExternal(const_cast<uint8_t*>(enrolledPasswordHandle.data()),
+                                   enrolledPasswordHandle.size());
+        android::hardware::hidl_vec<uint8_t> enteredPwd;
+        enteredPwd.setToExternal(const_cast<uint8_t*>(providedPassword.data()),
+                                 providedPassword.size());
+
+        Return<void> hwRes = hw_device->verify(
+                hw_uid, challenge, curPwdHandle, enteredPwd,
+                [&gkResponse](const GatekeeperResponse& rsp) {
+                    if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
+                        *gkResponse = GKResponse::ok(
+                                {rsp.data.begin(), rsp.data.end()},
+                                rsp.code == GatekeeperStatusCode::STATUS_REENROLL /* reenroll */);
+                    } else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT) {
+                        *gkResponse = GKResponse::retry(rsp.timeout);
+                    } else {
+                        *gkResponse = GKResponse::error();
                     }
                 });
-                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;
-                ret = soft_dev.verify(uid, challenge,
-                    enrolled_password_handle, enrolled_password_handle_length,
-                    provided_password, provided_password_length, auth_token, auth_token_length,
-                    request_reenroll);
 
-                if (ret == 0) {
-                    // success! re-enroll with HAL
-                    *request_reenroll = true;
+        if (!hwRes.isOk()) {
+            LOG(ERROR) << "verify transaction failed";
+            return GK_ERROR;
+        }
+
+        if (gkResponse->response_code() == GKResponseCode::OK) {
+            if (gkResponse->payload().size() != 0) {
+                sp<IServiceManager> sm = defaultServiceManager();
+                sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
+                sp<security::keystore::IKeystoreService> service =
+                        interface_cast<security::keystore::IKeystoreService>(binder);
+
+                if (service) {
+                    int result = 0;
+                    auto binder_result = service->addAuthToken(gkResponse->payload(), &result);
+                    if (!binder_result.isOk() ||
+                        !keystore::KeyStoreServiceReturnCode(result).isOk()) {
+                        LOG(ERROR) << "Failure sending auth token to KeyStore: " << result;
+                    }
+                } else {
+                    LOG(ERROR) << "Cannot deliver auth token. Unable to communicate with Keystore.";
                 }
             }
-        } else {
-            ret = soft_device->verify(uid, challenge,
-                enrolled_password_handle, enrolled_password_handle_length,
-                provided_password, provided_password_length, auth_token, auth_token_length,
-                request_reenroll);
+
+            maybe_store_sid(uid, handle->user_id);
         }
 
-        if (ret == 0 && *auth_token != NULL && *auth_token_length > 0) {
-            // TODO: cache service?
-            sp<IServiceManager> sm = defaultServiceManager();
-            sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
-            sp<security::IKeystoreService> service =
-                interface_cast<security::IKeystoreService>(binder);
-            if (service != NULL) {
-                std::vector<uint8_t> auth_token_vector(*auth_token,
-                                                       (*auth_token) + *auth_token_length);
-                int result = 0;
-                auto binder_result = service->addAuthToken(auth_token_vector, &result);
-                if (!binder_result.isOk() || !keystore::KeyStoreServiceReturnCode(result).isOk()) {
-                    ALOGE("Failure sending auth token to KeyStore: %" PRId32, result);
-                }
-            } else {
-                ALOGE("Unable to communicate with KeyStore");
-            }
-        }
-
-        if (ret == 0) {
-            maybe_store_sid(uid, reinterpret_cast<const gatekeeper::password_handle_t *>(
-                        enrolled_password_handle)->user_id);
-        }
-
-        return ret;
+        return Status::ok();
     }
 
-    virtual uint64_t getSecureUserId(uint32_t uid) { return read_sid(uid); }
+    Status getSecureUserId(int32_t uid, int64_t* sid) override {
+        *sid = read_sid(uid);
+        return Status::ok();
+    }
 
-    virtual void clearSecureUserId(uint32_t uid) {
+    Status clearSecureUserId(int32_t uid) override {
         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;
+            return Status::ok();
         }
         clear_sid(uid);
 
-        if (hw_device != nullptr) {
-            hw_device->deleteUser(uid, [] (const GatekeeperResponse &){});
+        if (hw_device) {
+            uint32_t hw_uid = adjust_uid(uid);
+            hw_device->deleteUser(hw_uid, [] (const GatekeeperResponse &){});
         }
+        return Status::ok();
     }
 
-    virtual void reportDeviceSetupComplete() {
+    Status reportDeviceSetupComplete() override {
         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;
+            return Status::ok();
         }
 
         clear_state_if_needed();
+        return Status::ok();
     }
 
-    virtual status_t dump(int fd, const Vector<String16> &) {
+    status_t dump(int fd, const Vector<String16>&) override {
         IPCThreadState* ipc = IPCThreadState::self();
         const int pid = ipc->getCallingPid();
         const int uid = ipc->getCallingUid();
@@ -384,14 +372,14 @@
             write(fd, result, strlen(result) + 1);
         }
 
-        return NO_ERROR;
+        return OK;
     }
 
 private:
     sp<IGatekeeper> hw_device;
-    std::unique_ptr<SoftGateKeeperDevice> soft_device;
 
     bool clear_state_if_needed_done;
+    bool is_running_gsi;
 };
 }// namespace android
 
diff --git a/gatekeeperd/include/gatekeeper/GateKeeperResponse.h b/gatekeeperd/include/gatekeeper/GateKeeperResponse.h
new file mode 100644
index 0000000..99fff02
--- /dev/null
+++ b/gatekeeperd/include/gatekeeper/GateKeeperResponse.h
@@ -0,0 +1,85 @@
+/*
+**
+** Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT 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 GATEKEEPERD_INCLUDE_GATEKEEPER_GATEKEEPERRESPONSE_H_
+#define GATEKEEPERD_INCLUDE_GATEKEEPER_GATEKEEPERRESPONSE_H_
+
+#include <binder/Parcelable.h>
+
+namespace android {
+namespace service {
+namespace gatekeeper {
+
+enum class ResponseCode : int32_t {
+    ERROR = -1,
+    OK = 0,
+    RETRY = 1,
+};
+
+class GateKeeperResponse : public ::android::Parcelable {
+    GateKeeperResponse(ResponseCode response_code, int32_t timeout = 0,
+                       std::vector<uint8_t> payload = {}, bool should_reenroll = false)
+        : response_code_(response_code),
+          timeout_(timeout),
+          payload_(std::move(payload)),
+          should_reenroll_(should_reenroll) {}
+
+  public:
+    GateKeeperResponse() = default;
+    GateKeeperResponse(GateKeeperResponse&&) = default;
+    GateKeeperResponse(const GateKeeperResponse&) = default;
+    GateKeeperResponse& operator=(GateKeeperResponse&&) = default;
+
+    static GateKeeperResponse error() { return GateKeeperResponse(ResponseCode::ERROR); }
+    static GateKeeperResponse retry(int32_t timeout) {
+        return GateKeeperResponse(ResponseCode::RETRY, timeout);
+    }
+    static GateKeeperResponse ok(std::vector<uint8_t> payload, bool reenroll = false) {
+        return GateKeeperResponse(ResponseCode::OK, 0, std::move(payload), reenroll);
+    }
+
+    status_t readFromParcel(const Parcel* in) override;
+    status_t writeToParcel(Parcel* out) const override;
+
+    const std::vector<uint8_t>& payload() const { return payload_; }
+
+    void payload(std::vector<uint8_t> payload) { payload_ = payload; }
+
+    ResponseCode response_code() const { return response_code_; }
+
+    void response_code(ResponseCode response_code) { response_code_ = response_code; }
+
+    bool should_reenroll() const { return should_reenroll_; }
+
+    void should_reenroll(bool should_reenroll) { should_reenroll_ = should_reenroll; }
+
+    int32_t timeout() const { return timeout_; }
+
+    void timeout(int32_t timeout) { timeout_ = timeout; }
+
+  private:
+    ResponseCode response_code_;
+    int32_t timeout_;
+    std::vector<uint8_t> payload_;
+    bool should_reenroll_;
+};
+
+}  // namespace gatekeeper
+}  // namespace service
+}  // namespace android
+
+#endif  // GATEKEEPERD_INCLUDE_GATEKEEPER_GATEKEEPERRESPONSE_H_
diff --git a/gatekeeperd/tests/Android.mk b/gatekeeperd/tests/Android.mk
deleted file mode 100644
index c38c64b..0000000
--- a/gatekeeperd/tests/Android.mk
+++ /dev/null
@@ -1,29 +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_MODULE := gatekeeperd-unit-tests
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-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
-LOCAL_SRC_FILES := \
-	gatekeeper_test.cpp
-include $(BUILD_NATIVE_TEST)
-
diff --git a/gatekeeperd/tests/gatekeeper_test.cpp b/gatekeeperd/tests/gatekeeper_test.cpp
deleted file mode 100644
index 100375f..0000000
--- a/gatekeeperd/tests/gatekeeper_test.cpp
+++ /dev/null
@@ -1,204 +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 <arpa/inet.h>
-#include <iostream>
-
-#include <gtest/gtest.h>
-#include <hardware/hw_auth_token.h>
-
-#include "../SoftGateKeeper.h"
-
-using ::gatekeeper::SizedBuffer;
-using ::testing::Test;
-using ::gatekeeper::EnrollRequest;
-using ::gatekeeper::EnrollResponse;
-using ::gatekeeper::VerifyRequest;
-using ::gatekeeper::VerifyResponse;
-using ::gatekeeper::SoftGateKeeper;
-using ::gatekeeper::secure_id_t;
-
-static void do_enroll(SoftGateKeeper &gatekeeper, EnrollResponse *response) {
-    SizedBuffer password;
-
-    password.buffer.reset(new uint8_t[16]);
-    password.length = 16;
-    memset(password.buffer.get(), 0, 16);
-    EnrollRequest request(0, NULL, &password, NULL);
-
-    gatekeeper.Enroll(request, response);
-}
-
-TEST(GateKeeperTest, EnrollSuccess) {
-    SoftGateKeeper gatekeeper;
-    EnrollResponse response;
-    do_enroll(gatekeeper, &response);
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
-}
-
-TEST(GateKeeperTest, EnrollBogusData) {
-    SoftGateKeeper gatekeeper;
-    SizedBuffer password;
-    EnrollResponse response;
-
-    EnrollRequest request(0, NULL, &password, NULL);
-
-    gatekeeper.Enroll(request, &response);
-
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_INVALID, response.error);
-}
-
-TEST(GateKeeperTest, VerifySuccess) {
-    SoftGateKeeper gatekeeper;
-    SizedBuffer provided_password;
-    EnrollResponse enroll_response;
-
-    provided_password.buffer.reset(new uint8_t[16]);
-    provided_password.length = 16;
-    memset(provided_password.buffer.get(), 0, 16);
-
-    do_enroll(gatekeeper, &enroll_response);
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
-    VerifyRequest request(0, 1, &enroll_response.enrolled_password_handle,
-            &provided_password);
-    VerifyResponse response;
-
-    gatekeeper.Verify(request, &response);
-
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
-
-    hw_auth_token_t *auth_token =
-        reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get());
-
-    ASSERT_EQ((uint32_t) HW_AUTH_PASSWORD, ntohl(auth_token->authenticator_type));
-    ASSERT_EQ((uint64_t) 1, auth_token->challenge);
-    ASSERT_NE(~((uint32_t) 0), auth_token->timestamp);
-    ASSERT_NE((uint64_t) 0, auth_token->user_id);
-    ASSERT_NE((uint64_t) 0, auth_token->authenticator_id);
-}
-
-TEST(GateKeeperTest, TrustedReEnroll) {
-    SoftGateKeeper gatekeeper;
-    SizedBuffer provided_password;
-    EnrollResponse enroll_response;
-    SizedBuffer password_handle;
-
-    // do_enroll enrolls an all 0 password
-    provided_password.buffer.reset(new uint8_t[16]);
-    provided_password.length = 16;
-    memset(provided_password.buffer.get(), 0, 16);
-    do_enroll(gatekeeper, &enroll_response);
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
-
-    // keep a copy of the handle
-    password_handle.buffer.reset(new uint8_t[enroll_response.enrolled_password_handle.length]);
-    password_handle.length = enroll_response.enrolled_password_handle.length;
-    memcpy(password_handle.buffer.get(), enroll_response.enrolled_password_handle.buffer.get(),
-            password_handle.length);
-
-    // verify first password
-    VerifyRequest request(0, 0, &enroll_response.enrolled_password_handle,
-            &provided_password);
-    VerifyResponse response;
-    gatekeeper.Verify(request, &response);
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
-    hw_auth_token_t *auth_token =
-        reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get());
-
-    secure_id_t secure_id = auth_token->user_id;
-
-    // enroll new password
-    provided_password.buffer.reset(new uint8_t[16]);
-    provided_password.length = 16;
-    memset(provided_password.buffer.get(), 0, 16);
-    SizedBuffer password;
-    password.buffer.reset(new uint8_t[16]);
-    memset(password.buffer.get(), 1, 16);
-    password.length = 16;
-    EnrollRequest enroll_request(0, &password_handle, &password, &provided_password);
-    gatekeeper.Enroll(enroll_request, &enroll_response);
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
-
-    // verify new password
-    password.buffer.reset(new uint8_t[16]);
-    memset(password.buffer.get(), 1, 16);
-    password.length = 16;
-    VerifyRequest new_request(0, 0, &enroll_response.enrolled_password_handle,
-            &password);
-    gatekeeper.Verify(new_request, &response);
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
-    ASSERT_EQ(secure_id,
-        reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get())->user_id);
-}
-
-
-TEST(GateKeeperTest, UntrustedReEnroll) {
-    SoftGateKeeper gatekeeper;
-    SizedBuffer provided_password;
-    EnrollResponse enroll_response;
-
-    // do_enroll enrolls an all 0 password
-    provided_password.buffer.reset(new uint8_t[16]);
-    provided_password.length = 16;
-    memset(provided_password.buffer.get(), 0, 16);
-    do_enroll(gatekeeper, &enroll_response);
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
-
-    // verify first password
-    VerifyRequest request(0, 0, &enroll_response.enrolled_password_handle,
-            &provided_password);
-    VerifyResponse response;
-    gatekeeper.Verify(request, &response);
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
-    hw_auth_token_t *auth_token =
-        reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get());
-
-    secure_id_t secure_id = auth_token->user_id;
-
-    // enroll new password
-    SizedBuffer password;
-    password.buffer.reset(new uint8_t[16]);
-    memset(password.buffer.get(), 1, 16);
-    password.length = 16;
-    EnrollRequest enroll_request(0, NULL, &password, NULL);
-    gatekeeper.Enroll(enroll_request, &enroll_response);
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
-
-    // verify new password
-    password.buffer.reset(new uint8_t[16]);
-    memset(password.buffer.get(), 1, 16);
-    password.length = 16;
-    VerifyRequest new_request(0, 0, &enroll_response.enrolled_password_handle,
-            &password);
-    gatekeeper.Verify(new_request, &response);
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
-    ASSERT_NE(secure_id,
-        reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get())->user_id);
-}
-
-
-TEST(GateKeeperTest, VerifyBogusData) {
-    SoftGateKeeper gatekeeper;
-    SizedBuffer provided_password;
-    SizedBuffer password_handle;
-    VerifyResponse response;
-
-    VerifyRequest request(0, 0, &provided_password, &password_handle);
-
-    gatekeeper.Verify(request, &response);
-
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_INVALID, response.error);
-}
diff --git a/healthd/Android.bp b/healthd/Android.bp
index cefe09d..2cf6be9 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -1,6 +1,7 @@
 cc_library_headers {
     name: "libhealthd_headers",
     vendor_available: true,
+    recovery_available: true,
     export_include_dirs: ["include"],
     header_libs: ["libbatteryservice_headers"],
     export_header_lib_headers: ["libbatteryservice_headers"],
@@ -11,6 +12,7 @@
     srcs: ["BatteryMonitor.cpp"],
     cflags: ["-Wall", "-Werror"],
     vendor_available: true,
+    recovery_available: true,
     export_include_dirs: ["include"],
     shared_libs: [
         "libutils",
@@ -22,12 +24,6 @@
 
 cc_defaults {
     name: "android.hardware.health@2.0-service_defaults",
-    init_rc: ["android.hardware.health@2.0-service.rc"],
-    vendor: true,
-    relative_install_path: "hw",
-    srcs: [
-        "HealthServiceDefault.cpp",
-    ],
 
     cflags: [
         "-Wall",
@@ -57,48 +53,60 @@
 cc_binary {
     name: "android.hardware.health@2.0-service",
     defaults: ["android.hardware.health@2.0-service_defaults"],
-}
 
-cc_binary {
-    name: "android.hardware.health@2.0-service.override",
-    defaults: ["android.hardware.health@2.0-service_defaults"],
+    vendor: true,
+    relative_install_path: "hw",
+    init_rc: ["android.hardware.health@2.0-service.rc"],
+    srcs: [
+        "HealthServiceDefault.cpp",
+    ],
 
     overrides: [
         "healthd",
-    ],
+    ]
 }
 
 cc_binary {
     name: "healthd",
+    defaults: ["android.hardware.health@2.0-service_defaults"],
+
     init_rc: ["healthd.rc"],
     srcs: [
         "HealthServiceHealthd.cpp",
     ],
     local_include_dirs: ["include"],
 
+    shared_libs: [
+        "android.hardware.health@1.0",
+    ],
+
+    vintf_fragments: [
+        "manifest_healthd.xml"
+    ],
+}
+
+cc_library_static {
+    name: "libhealthd_charger_nops",
+
+    srcs: [
+        "healthd_mode_charger_nops.cpp",
+    ],
+
     cflags: [
         "-Wall",
         "-Werror",
     ],
 
+    header_libs: [
+        "libhealthd_headers",
+    ],
+
     static_libs: [
         "android.hardware.health@2.0-impl",
-        "android.hardware.health@1.0-convert",
-        "libhealthservice",
-        "libbatterymonitor",
-        "libhealthstoragedefault",
     ],
 
     shared_libs: [
-        "libbase",
-        "libcutils",
-        "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
-        "liblog",
-        "libutils",
-        "android.hardware.health@1.0",
         "android.hardware.health@2.0",
+        "libutils",
     ],
-
 }
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 818488d..d18f15a 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -2,14 +2,14 @@
 
 LOCAL_PATH := $(call my-dir)
 
+### libhealthd_draw ###
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := libhealthd_draw
 
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
-LOCAL_STATIC_LIBRARIES := \
-	libminui \
-	libbase
+LOCAL_STATIC_LIBRARIES := libminui
+LOCAL_SHARED_LIBRARIES := libbase
 LOCAL_SRC_FILES := healthd_draw.cpp
 
 ifneq ($(TARGET_HEALTHD_DRAW_SPLIT_SCREEN),)
@@ -24,8 +24,11 @@
 LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_OFFSET=0
 endif
 
+LOCAL_HEADER_LIBRARIES := libbatteryservice_headers
+
 include $(BUILD_STATIC_LIBRARY)
 
+### libhealthd_charger ###
 include $(CLEAR_VARS)
 
 LOCAL_CFLAGS := -Werror
@@ -41,30 +44,28 @@
     AnimationParser.cpp
 
 LOCAL_MODULE := libhealthd_charger
-LOCAL_C_INCLUDES := bootable/recovery $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 LOCAL_EXPORT_C_INCLUDE_DIRS := \
     $(LOCAL_PATH) \
     $(LOCAL_PATH)/include
 
 LOCAL_STATIC_LIBRARIES := \
-    android.hardware.health@2.0 \
     android.hardware.health@2.0-impl \
-    android.hardware.health@1.0 \
     android.hardware.health@1.0-convert \
     libhealthstoragedefault \
+    libhealthd_draw \
     libminui \
-    libpng \
-    libz \
-    libutils \
+
+LOCAL_SHARED_LIBRARIES := \
+    android.hardware.health@2.0 \
     libbase \
     libcutils \
-    libhealthd_draw \
     liblog \
-    libm \
-    libc \
+    libpng \
+    libutils \
 
 ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
-LOCAL_STATIC_LIBRARIES += libsuspend
+LOCAL_SHARED_LIBRARIES += libsuspend
 endif
 
 include $(BUILD_STATIC_LIBRARY)
@@ -74,18 +75,11 @@
 ifeq ($(strip $(BOARD_CHARGER_NO_UI)),true)
 LOCAL_CHARGER_NO_UI := true
 endif
-ifdef BRILLO
-LOCAL_CHARGER_NO_UI := true
-endif
 
 LOCAL_SRC_FILES := \
     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
@@ -95,61 +89,105 @@
 
 CHARGER_STATIC_LIBRARIES := \
     android.hardware.health@2.0-impl \
-    android.hardware.health@2.0 \
-    android.hardware.health@1.0 \
     android.hardware.health@1.0-convert \
+    libbinderthreadstate \
     libhidltransport \
     libhidlbase \
     libhwbinder_noltopgo \
     libhealthstoragedefault \
     libvndksupport \
     libhealthd_charger \
+    libhealthd_charger_nops \
     libhealthd_draw \
     libbatterymonitor \
-    libbase \
-    libutils \
-    libcutils \
-    liblog \
-    libm \
-    libc \
 
-LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
+CHARGER_SHARED_LIBRARIES := \
+    android.hardware.health@2.0 \
+    libbase \
+    libcutils \
+    libjsoncpp \
+    libprocessgroup \
+    liblog \
+    libutils \
 
 ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
-LOCAL_STATIC_LIBRARIES += \
-    libminui \
-    libpng \
-    libz \
-
+CHARGER_STATIC_LIBRARIES += libminui
+CHARGER_SHARED_LIBRARIES += libpng
 endif
 
 ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
-LOCAL_STATIC_LIBRARIES += libsuspend
+CHARGER_SHARED_LIBRARIES += libsuspend
 endif
 
+LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
+LOCAL_SHARED_LIBRARIES := $(CHARGER_SHARED_LIBRARIES)
+
 LOCAL_HAL_STATIC_LIBRARIES := libhealthd
 
-# Symlink /charger to /sbin/charger
+# Symlink /charger to /system/bin/charger
 LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT) \
-    && ln -sf /sbin/charger $(TARGET_ROOT_OUT)/charger
+    && ln -sf /system/bin/charger $(TARGET_ROOT_OUT)/charger
 
 include $(BUILD_EXECUTABLE)
 
+### charger.recovery ###
 include $(CLEAR_VARS)
-LOCAL_MODULE := charger_test
-LOCAL_MODULE_TAGS := optional
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror -DCHARGER_TEST -DCHARGER_NO_UI
-LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
+
 LOCAL_SRC_FILES := \
     charger.cpp \
+
+LOCAL_MODULE := charger.recovery
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
+LOCAL_MODULE_STEM := charger
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Wall -Werror
+LOCAL_CFLAGS += -DCHARGER_NO_UI
+
+# charger.recovery doesn't link against libhealthd_{charger,draw} or libminui, since it doesn't need
+# any UI support.
+LOCAL_STATIC_LIBRARIES := \
+    android.hardware.health@2.0-impl \
+    android.hardware.health@1.0-convert \
+    libbinderthreadstate \
+    libhidltransport \
+    libhidlbase \
+    libhwbinder_noltopgo \
+    libhealthstoragedefault \
+    libvndksupport \
+    libhealthd_charger_nops \
+    libbatterymonitor \
+
+# These shared libs will be installed to recovery image because of the dependency in `recovery`
+# module.
+LOCAL_SHARED_LIBRARIES := \
+    android.hardware.health@2.0 \
+    libbase \
+    libcutils \
+    liblog \
+    libutils \
+
+# The use of LOCAL_HAL_STATIC_LIBRARIES prevents from building this module with Android.bp.
+LOCAL_HAL_STATIC_LIBRARIES := libhealthd
+
+include $(BUILD_EXECUTABLE)
+
+### charger_test ###
+include $(CLEAR_VARS)
+LOCAL_MODULE := charger_test
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Wall -Werror -DCHARGER_NO_UI
+LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
+LOCAL_SHARED_LIBRARIES := $(CHARGER_SHARED_LIBRARIES)
+LOCAL_SRC_FILES := \
     charger_test.cpp \
 
 include $(BUILD_EXECUTABLE)
 
 CHARGER_STATIC_LIBRARIES :=
+CHARGER_SHARED_LIBRARIES :=
 
+### charger_res_images ###
 ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
 define _add-charger-image
 include $$(CLEAR_VARS)
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 80c5afe..06c8176 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -26,6 +26,8 @@
 #include <stdlib.h>
 #include <sys/types.h>
 #include <unistd.h>
+
+#include <algorithm>
 #include <memory>
 
 #include <android-base/file.h>
@@ -240,10 +242,9 @@
     if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
         props.batteryTechnology = String8(buf.c_str());
 
-    unsigned int i;
     double MaxPower = 0;
 
-    for (i = 0; i < mChargerNames.size(); i++) {
+    for (size_t i = 0; i < mChargerNames.size(); i++) {
         String8 path;
         path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
                           mChargerNames[i].string());
@@ -357,7 +358,7 @@
         if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
             val->valueInt64 =
                 getIntField(mHealthdConfig->batteryChargeCounterPath);
-            ret = NO_ERROR;
+            ret = OK;
         } else {
             ret = NAME_NOT_FOUND;
         }
@@ -367,7 +368,7 @@
         if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
             val->valueInt64 =
                 getIntField(mHealthdConfig->batteryCurrentNowPath);
-            ret = NO_ERROR;
+            ret = OK;
         } else {
             ret = NAME_NOT_FOUND;
         }
@@ -377,7 +378,7 @@
         if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
             val->valueInt64 =
                 getIntField(mHealthdConfig->batteryCurrentAvgPath);
-            ret = NO_ERROR;
+            ret = OK;
         } else {
             ret = NAME_NOT_FOUND;
         }
@@ -387,7 +388,7 @@
         if (!mHealthdConfig->batteryCapacityPath.isEmpty()) {
             val->valueInt64 =
                 getIntField(mHealthdConfig->batteryCapacityPath);
-            ret = NO_ERROR;
+            ret = OK;
         } else {
             ret = NAME_NOT_FOUND;
         }
@@ -403,7 +404,7 @@
 
     case BATTERY_PROP_BATTERY_STATUS:
         val->valueInt64 = getChargeStatus();
-        ret = NO_ERROR;
+        ret = OK;
         break;
 
     default:
@@ -477,10 +478,16 @@
 
         while ((entry = readdir(dir.get()))) {
             const char* name = entry->d_name;
+            std::vector<String8>::iterator itIgnoreName;
 
             if (!strcmp(name, ".") || !strcmp(name, ".."))
                 continue;
 
+            itIgnoreName = find(hc->ignorePowerSupplyNames.begin(),
+                                hc->ignorePowerSupplyNames.end(), String8(name));
+            if (itIgnoreName != hc->ignorePowerSupplyNames.end())
+                continue;
+
             // Look for "type" file in each subdirectory
             path.clear();
             path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);
diff --git a/healthd/OWNERS b/healthd/OWNERS
index 2375f7c..d3f8758 100644
--- a/healthd/OWNERS
+++ b/healthd/OWNERS
@@ -1 +1,2 @@
-toddpoynor@google.com
+elsk@google.com
+hridya@google.com
diff --git a/healthd/android.hardware.health@2.0-service.rc b/healthd/android.hardware.health@2.0-service.rc
index dca0ccc..6960c5d 100644
--- a/healthd/android.hardware.health@2.0-service.rc
+++ b/healthd/android.hardware.health@2.0-service.rc
@@ -2,4 +2,5 @@
     class hal
     user system
     group system
+    capabilities WAKE_ALARM
     file /dev/kmsg w
diff --git a/healthd/animation.h b/healthd/animation.h
index 562b689..9476c91 100644
--- a/healthd/animation.h
+++ b/healthd/animation.h
@@ -20,7 +20,7 @@
 #include <inttypes.h>
 #include <string>
 
-struct GRSurface;
+class GRSurface;
 struct GRFont;
 
 namespace android {
@@ -48,6 +48,25 @@
         GRFont* font;
     };
 
+    // When libminui loads PNG images:
+    // - When treating paths as relative paths, it adds ".png" suffix.
+    // - When treating paths as absolute paths, it doesn't add the suffix. Hence, the suffix
+    //   is added here.
+    void set_resource_root(const std::string& root) {
+        if (!animation_file.empty()) {
+            animation_file = root + animation_file + ".png";
+        }
+        if (!fail_file.empty()) {
+            fail_file = root + fail_file + ".png";
+        }
+        if (!text_clock.font_file.empty()) {
+            text_clock.font_file = root + text_clock.font_file + ".png";
+        }
+        if (!text_percent.font_file.empty()) {
+            text_percent.font_file = root + text_percent.font_file + ".png";
+        }
+    }
+
     std::string animation_file;
     std::string fail_file;
 
diff --git a/healthd/charger.cpp b/healthd/charger.cpp
index 43e7fd5..085cceb 100644
--- a/healthd/charger.cpp
+++ b/healthd/charger.cpp
@@ -14,98 +14,13 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "charger"
-#define KLOG_LEVEL 6
+#include "healthd_mode_charger.h"
+#include "healthd_mode_charger_nops.h"
 
-#include <health2/Health.h>
-#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) {
-    using android::hardware::health::V2_0::implementation::Health;
-    Health::initInstance(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_charger_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();
-}
-
-#ifndef CHARGER_TEST
 int main(int argc, char** argv) {
+#ifdef CHARGER_NO_UI
+    return healthd_charger_nops(argc, argv);
+#else
     return healthd_charger_main(argc, argv);
-}
 #endif
+}
diff --git a/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
index ea3d991..3da8bda 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -21,77 +21,97 @@
 #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) {
-  gr_init();
-  gr_font_size(gr_sys_font(), &char_width_, &char_height_);
+    int ret = gr_init();
 
-  screen_width_ = gr_fb_width() / (kSplitScreen ? 2 : 1);
-  screen_height_ = gr_fb_height();
+    if (ret < 0) {
+        LOGE("gr_init failed\n");
+        graphics_available = false;
+        return;
+    }
 
-  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);
-  }
+    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) {
-  clear_screen();
+    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();
+    /* try to display *something* */
+    if (batt_anim->cur_status == BATTERY_STATUS_UNKNOWN || 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) { gr_fb_blank(blank); }
+void HealthdDraw::blank_screen(bool blank) {
+    if (!graphics_available) return;
+    gr_fb_blank(blank);
+}
 
 void HealthdDraw::clear_screen(void) {
-  gr_color(0, 0, 0, 255);
-  gr_clear();
+    if (!graphics_available) return;
+    gr_color(0, 0, 0, 255);
+    gr_clear();
 }
 
 int HealthdDraw::draw_surface_centered(GRSurface* surface) {
-  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;
+    if (!graphics_available) return 0;
 
-  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;
+    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;
+    return y + h;
 }
 
 int HealthdDraw::draw_text(const GRFont* font, int x, int y, const char* str) {
-  int str_len_px = gr_measure(font, 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 */);
+    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_;
+    return y + char_height_;
 }
 
 void HealthdDraw::determine_xy(const animation::text_field& field,
@@ -119,77 +139,80 @@
 }
 
 void HealthdDraw::draw_clock(const animation* anim) {
-  static constexpr char CLOCK_FORMAT[] = "%H:%M";
-  static constexpr int CLOCK_LENGTH = 6;
+    static constexpr char CLOCK_FORMAT[] = "%H:%M";
+    static constexpr int CLOCK_LENGTH = 6;
 
-  const animation::text_field& field = anim->text_clock;
+    const animation::text_field& field = anim->text_clock;
 
-  if (field.font == nullptr || field.font->char_width == 0 ||
-      field.font->char_height == 0)
-    return;
+    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);
+    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;
-  }
+    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);
+    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);
+    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) {
-  int cur_level = anim->cur_level;
-  if (anim->cur_status == BATTERY_STATUS_FULL) {
-    cur_level = 100;
-  }
+    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;
+    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;
-  }
+    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);
+    std::string str = base::StringPrintf("%d%%", cur_level);
 
-  int x, y;
-  determine_xy(field, str.size(), &x, &y);
+    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());
+    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) {
-  const animation::frame& frame = anim->frames[anim->cur_frame];
+    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);
+    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);
+      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 {
-    gr_color(0xa4, 0xc6, 0x39, 255);
-    y = draw_text(gr_sys_font(), -1, -1, "Charging!");
-    draw_text(gr_sys_font(), -1, y + 25, "?\?/100");
+      LOGW("Charging, level unknown\n");
   }
 }
diff --git a/healthd/healthd_draw.h b/healthd/healthd_draw.h
index 6a6ba76..7c847bd 100644
--- a/healthd/healthd_draw.h
+++ b/healthd/healthd_draw.h
@@ -70,6 +70,12 @@
   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_charger.cpp b/healthd/healthd_mode_charger.cpp
index 56a9f86..edf34f7 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -36,6 +36,7 @@
 #include <linux/netlink.h>
 #include <sys/socket.h>
 
+#include <cutils/android_get_control_file.h>
 #include <cutils/klog.h>
 #include <cutils/misc.h>
 #include <cutils/properties.h>
@@ -54,6 +55,9 @@
 
 using namespace android;
 
+// main healthd loop
+extern int healthd_main(void);
+
 char* locale;
 
 #ifndef max
@@ -72,6 +76,9 @@
 #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 UNPLUGGED_DISPLAY_TIME (3 * MSEC_PER_SEC)
+#define MAX_BATT_LEVEL_WAIT_TIME (3 * MSEC_PER_SEC)
+#define UNPLUGGED_SHUTDOWN_TIME_PROP "ro.product.charger.unplugged_shutdown_time"
 
 #define LAST_KMSG_MAX_SZ (32 * 1024)
 
@@ -79,8 +86,13 @@
 #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";
+// Resources in /product/etc/res overrides resources in /res.
+// If the device is using the Generic System Image (GSI), resources may exist in
+// both paths.
+static constexpr const char* product_animation_desc_path =
+        "/product/etc/res/values/charger/animation.txt";
+static constexpr const char* product_animation_root = "/product/etc/res/images/";
+static constexpr const char* animation_desc_path = "/res/values/charger/animation.txt";
 
 struct key_state {
     bool pending;
@@ -91,9 +103,11 @@
 struct charger {
     bool have_battery_state;
     bool charger_connected;
+    bool screen_blanked;
     int64_t next_screen_transition;
     int64_t next_key_check;
     int64_t next_pwr_check;
+    int64_t wait_batt_level_timestamp;
 
     key_state keys[KEY_MAX + 1];
 
@@ -196,10 +210,9 @@
 #define MAX_KLOG_WRITE_BUF_SZ 256
 
 static void dump_last_kmsg(void) {
-    char* buf;
+    std::string buf;
     char* ptr;
-    unsigned sz = 0;
-    int len;
+    size_t len;
 
     LOGW("\n");
     LOGW("*************** LAST KMSG ***************\n");
@@ -211,21 +224,25 @@
         "/proc/last_kmsg",
         // clang-format on
     };
-    for (size_t i = 0; i < arraysize(kmsg); ++i) {
-        buf = (char*)load_file(kmsg[i], &sz);
-        if (buf && sz) break;
+    for (size_t i = 0; i < arraysize(kmsg) && buf.empty(); ++i) {
+        auto fd = android_get_control_file(kmsg[i]);
+        if (fd >= 0) {
+            android::base::ReadFdToString(fd, &buf);
+        } else {
+            android::base::ReadFileToString(kmsg[i], &buf);
+        }
     }
 
-    if (!buf || !sz) {
+    if (buf.empty()) {
         LOGW("last_kmsg not found. Cold reset?\n");
         goto out;
     }
 
-    len = min(sz, LAST_KMSG_MAX_SZ);
-    ptr = buf + (sz - len);
+    len = min(buf.size(), LAST_KMSG_MAX_SZ);
+    ptr = &buf[buf.size() - len];
 
     while (len > 0) {
-        int cnt = min(len, MAX_KLOG_WRITE_BUF_SZ);
+        size_t cnt = min(len, MAX_KLOG_WRITE_BUF_SZ);
         char yoink;
         char* nl;
 
@@ -241,8 +258,6 @@
         ptr += cnt;
     }
 
-    free(buf);
-
 out:
     LOGW("\n");
     LOGW("************* END LAST KMSG *************\n");
@@ -278,6 +293,21 @@
 
     if (!batt_anim->run || now < charger->next_screen_transition) return;
 
+    // If battery level is not ready, keep checking in the defined time
+    if (batt_prop == nullptr ||
+        (batt_prop->batteryLevel == 0 && batt_prop->batteryStatus == BATTERY_STATUS_UNKNOWN)) {
+        if (charger->wait_batt_level_timestamp == 0) {
+            // Set max delay time and skip drawing screen
+            charger->wait_batt_level_timestamp = now + MAX_BATT_LEVEL_WAIT_TIME;
+            LOGV("[%" PRId64 "] wait for battery capacity ready\n", now);
+            return;
+        } else if (now <= charger->wait_batt_level_timestamp) {
+            // Do nothing, keep waiting
+            return;
+        }
+        // If timeout and battery level is still not ready, draw unknown battery
+    }
+
     if (healthd_draw == nullptr) {
         if (healthd_config && healthd_config->screen_on) {
             if (!healthd_config->screen_on(batt_prop)) {
@@ -293,6 +323,7 @@
 
 #ifndef CHARGER_DISABLE_INIT_BLANK
         healthd_draw->blank_screen(true);
+        charger->screen_blanked = true;
 #endif
     }
 
@@ -301,6 +332,7 @@
         reset_animation(batt_anim);
         charger->next_screen_transition = -1;
         healthd_draw->blank_screen(true);
+        charger->screen_blanked = true;
         LOGV("[%" PRId64 "] animation done\n", now);
         if (charger->charger_connected) request_suspend(true);
         return;
@@ -308,8 +340,10 @@
 
     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);
+    if (charger->screen_blanked) {
+        healthd_draw->blank_screen(false);
+        charger->screen_blanked = false;
+    }
 
     /* animation starting, set up the animation */
     if (batt_anim->cur_frame == 0) {
@@ -327,9 +361,15 @@
                     }
                 }
 
-                // repeat the first frame first_frame_repeats times
-                disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time *
-                            batt_anim->first_frame_repeats;
+                if (charger->charger_connected) {
+                    // repeat the first frame first_frame_repeats times
+                    disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time *
+                                batt_anim->first_frame_repeats;
+                } else {
+                    disp_time = UNPLUGGED_DISPLAY_TIME / batt_anim->num_cycles;
+                }
+
+                LOGV("cur_frame=%d disp_time=%d\n", batt_anim->cur_frame, disp_time);
             }
         }
     }
@@ -348,7 +388,7 @@
     }
 
     /* schedule next screen transition */
-    charger->next_screen_transition = now + disp_time;
+    charger->next_screen_transition = curr_time_ms() + disp_time;
 
     /* advance frame cntr to the next valid frame only if we are charging
      * if necessary, advance cycle cntr, and reset frame cntr
@@ -458,6 +498,7 @@
             /* if the power key got released, force screen state cycle */
             if (key->pending) {
                 kick_animation(charger->batt_anim);
+                request_suspend(false);
             }
         }
     }
@@ -473,18 +514,27 @@
 }
 
 static void handle_power_supply_state(charger* charger, int64_t now) {
+    int timer_shutdown = UNPLUGGED_SHUTDOWN_TIME;
     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.
-         */
-        kick_animation(charger->batt_anim);
         request_suspend(false);
         if (charger->next_pwr_check == -1) {
-            charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME;
+            /* Last cycle would have stopped at the extreme top of battery-icon
+             * Need to show the correct level corresponding to capacity.
+             *
+             * Reset next_screen_transition to update screen immediately.
+             * Reset & kick animation to show complete animation cycles
+             * when charger disconnected.
+             */
+            timer_shutdown =
+                    property_get_int32(UNPLUGGED_SHUTDOWN_TIME_PROP, UNPLUGGED_SHUTDOWN_TIME);
+            charger->next_screen_transition = now - 1;
+            reset_animation(charger->batt_anim);
+            kick_animation(charger->batt_anim);
+            charger->next_pwr_check = now + timer_shutdown;
             LOGW("[%" PRId64 "] device unplugged: shutting down in %" PRId64 " (@ %" PRId64 ")\n",
-                 now, (int64_t)UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);
+                 now, (int64_t)timer_shutdown, charger->next_pwr_check);
         } else if (now >= charger->next_pwr_check) {
             LOGW("[%" PRId64 "] shutting down\n", now);
             reboot(RB_POWER_OFF);
@@ -494,8 +544,15 @@
     } else {
         /* online supply present, reset shutdown timer if set */
         if (charger->next_pwr_check != -1) {
-            LOGW("[%" PRId64 "] device plugged in: shutdown cancelled\n", now);
+            /* Reset next_screen_transition to update screen immediately.
+             * Reset & kick animation to show complete animation cycles
+             * when charger connected again.
+             */
+            request_suspend(false);
+            charger->next_screen_transition = now - 1;
+            reset_animation(charger->batt_anim);
             kick_animation(charger->batt_anim);
+            LOGW("[%" PRId64 "] device plugged in: shutdown cancelled\n", now);
         }
         charger->next_pwr_check = -1;
     }
@@ -523,6 +580,7 @@
     if (!charger->have_battery_state) {
         charger->have_battery_state = true;
         charger->next_screen_transition = curr_time_ms() - 1;
+        request_suspend(false);
         reset_animation(charger->batt_anim);
         kick_animation(charger->batt_anim);
     }
@@ -573,7 +631,10 @@
     bool parse_success;
 
     std::string content;
-    if (base::ReadFileToString(animation_desc_path, &content)) {
+    if (base::ReadFileToString(product_animation_desc_path, &content)) {
+        parse_success = parse_animation_desc(content, &battery_animation);
+        battery_animation.set_resource_root(product_animation_root);
+    } else if (base::ReadFileToString(animation_desc_path, &content)) {
         parse_success = parse_animation_desc(content, &battery_animation);
     } else {
         LOGW("Could not open animation description at %s\n", animation_desc_path);
@@ -669,6 +730,7 @@
     charger->next_screen_transition = -1;
     charger->next_key_check = -1;
     charger->next_pwr_check = -1;
+    charger->wait_batt_level_timestamp = 0;
 
     // Initialize Health implementation (which initializes the internal BatteryMonitor).
     Health::initInstance(config);
@@ -676,3 +738,33 @@
     healthd_config = config;
     charger->boot_min_cap = config->boot_min_cap;
 }
+
+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,
+};
+
+int healthd_charger_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':
+                // -r is now a noop
+                break;
+            case '?':
+            default:
+                LOGE("Unrecognized charger option: %c\n", optopt);
+                exit(1);
+        }
+    }
+
+    return healthd_main();
+}
diff --git a/healthd/healthd_mode_charger.h b/healthd/healthd_mode_charger.h
new file mode 100644
index 0000000..2f0c9f2
--- /dev/null
+++ b/healthd/healthd_mode_charger.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
+
+int healthd_charger_main(int argc, char** argv);
diff --git a/healthd/healthd_mode_charger_nops.cpp b/healthd/healthd_mode_charger_nops.cpp
new file mode 100644
index 0000000..bcc04d5
--- /dev/null
+++ b/healthd/healthd_mode_charger_nops.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_mode_charger_nops.h"
+
+#include <health2/Health.h>
+#include <healthd/healthd.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+using namespace android;
+
+// main healthd loop
+extern int healthd_main(void);
+
+// 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,
+};
+
+static void healthd_mode_nop_init(struct healthd_config* config) {
+    using android::hardware::health::V2_0::implementation::Health;
+    Health::initInstance(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_charger_nops(int /* argc */, char** /* argv */) {
+    healthd_mode_ops = &healthd_nops;
+    return healthd_main();
+}
diff --git a/healthd/healthd_mode_charger_nops.h b/healthd/healthd_mode_charger_nops.h
new file mode 100644
index 0000000..a37b247
--- /dev/null
+++ b/healthd/healthd_mode_charger_nops.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
+
+int healthd_charger_nops(int argc, char** argv);
diff --git a/healthd/include/healthd/healthd.h b/healthd/include/healthd/healthd.h
index c01e8d7..a900071 100644
--- a/healthd/include/healthd/healthd.h
+++ b/healthd/include/healthd/healthd.h
@@ -22,6 +22,8 @@
 #include <utils/Errors.h>
 #include <utils/String8.h>
 
+#include <vector>
+
 // periodic_chores_interval_fast, periodic_chores_interval_slow: intervals at
 // which healthd wakes up to poll health state and perform periodic chores,
 // in units of seconds:
@@ -71,6 +73,7 @@
     int (*energyCounter)(int64_t *);
     int boot_min_cap;
     bool (*screen_on)(android::BatteryProperties *props);
+    std::vector<android::String8> ignorePowerSupplyNames;
 };
 
 enum EventWakeup {
diff --git a/healthd/manifest_healthd.xml b/healthd/manifest_healthd.xml
new file mode 100644
index 0000000..097a7d8
--- /dev/null
+++ b/healthd/manifest_healthd.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="framework">
+    <hal>
+        <name>android.hardware.health</name>
+        <transport>hwbinder</transport>
+        <version>2.0</version>
+        <interface>
+            <name>IHealth</name>
+            <instance>backup</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/init/Android.bp b/init/Android.bp
index 63c8382..86dcb4c 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -26,6 +26,7 @@
         "-Wextra",
         "-Wno-unused-parameter",
         "-Werror",
+        "-DALLOW_FIRST_STAGE_CONSOLE=0",
         "-DALLOW_LOCAL_PROP_OVERRIDE=0",
         "-DALLOW_PERMISSIVE_SELINUX=0",
         "-DREBOOT_BOOTLOADER_ON_PANIC=0",
@@ -36,6 +37,8 @@
     product_variables: {
         debuggable: {
             cppflags: [
+                "-UALLOW_FIRST_STAGE_CONSOLE",
+                "-DALLOW_FIRST_STAGE_CONSOLE=1",
                 "-UALLOW_LOCAL_PROP_OVERRIDE",
                 "-DALLOW_LOCAL_PROP_OVERRIDE=1",
                 "-UALLOW_PERMISSIVE_SELINUX",
@@ -59,38 +62,44 @@
         },
     },
     static_libs: [
-        "libbootloader_message",
-        "libfs_mgr",
-        "libfec",
-        "libfec_rs",
-        "libhidl-gen-utils",
-        "libsquashfs_utils",
-        "liblogwrap",
-        "libext4_utils",
-        "libcutils",
-        "libbase",
-        "libc",
         "libseccomp_policy",
-        "libselinux",
-        "liblog",
-        "libcrypto_utils",
-        "libcrypto",
-        "libc++_static",
-        "libdl",
-        "libsparse",
-        "libz",
-        "libprocessgroup",
         "libavb",
-        "libkeyutils",
+        "libc++fs",
+        "libcgrouprc_format",
+        "libmodprobe",
         "libprotobuf-cpp-lite",
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
     ],
+    shared_libs: [
+        "libbacktrace",
+        "libbase",
+        "libbinder",
+        "libbootloader_message",
+        "libcutils",
+        "libcrypto",
+        "libdl",
+        "libext4_utils",
+        "libfs_mgr",
+        "libfscrypt",
+        "libgsi",
+        "libhidl-gen-utils",
+        "libkeyutils",
+        "liblog",
+        "liblogwrap",
+        "liblp",
+        "libprocessgroup",
+        "libprocessgroup_setup",
+        "libselinux",
+        "libutils",
+    ],
+    bootstrap: true,
 }
 
 cc_library_static {
     name: "libinit",
-    defaults: ["init_defaults"],
+    recovery_available: true,
+    defaults: ["init_defaults", "selinux_policy_version"],
     srcs: [
         "action.cpp",
         "action_manager.cpp",
@@ -101,64 +110,77 @@
         "capabilities.cpp",
         "descriptors.cpp",
         "devices.cpp",
+        "epoll.cpp",
         "firmware_handler.cpp",
+        "first_stage_init.cpp",
+        "first_stage_mount.cpp",
         "import_parser.cpp",
         "init.cpp",
-        "init_first_stage.cpp",
         "keychords.cpp",
-        "log.cpp",
+        "modalias_handler.cpp",
+        "mount_handler.cpp",
+        "mount_namespace.cpp",
         "parser.cpp",
         "persistent_properties.cpp",
         "persistent_properties.proto",
         "property_service.cpp",
         "property_type.cpp",
         "reboot.cpp",
+        "reboot_utils.cpp",
         "security.cpp",
+        "selabel.cpp",
         "selinux.cpp",
         "service.cpp",
+        "service_list.cpp",
+        "service_parser.cpp",
+        "service_utils.cpp",
         "sigchld_handler.cpp",
         "subcontext.cpp",
         "subcontext.proto",
+        "switch_root.cpp",
         "rlimit_parser.cpp",
         "tokenizer.cpp",
         "uevent_listener.cpp",
         "ueventd.cpp",
         "ueventd_parser.cpp",
         "util.cpp",
-        "watchdogd.cpp",
     ],
-    whole_static_libs: ["libcap"],
+    whole_static_libs: ["libcap", "com.android.sysprop.apex"],
     header_libs: ["bootimg_headers"],
     proto: {
         type: "lite",
         export_proto_headers: true,
     },
+
+    target: {
+        recovery: {
+            cflags: ["-DRECOVERY"],
+            exclude_shared_libs: ["libbinder", "libutils"],
+        },
+    },
 }
 
-/*
-This is not yet ready, see the below TODOs for what is missing
-
 cc_binary {
-    // TODO: Missing,
-    //LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
-    //LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
-
-    name: "init",
+    name: "init_second_stage",
+    recovery_available: true,
+    stem: "init",
     defaults: ["init_defaults"],
+    static_libs: ["libinit"],
     required: [
         "e2fsdroid",
         "mke2fs",
         "sload_f2fs",
         "make_f2fs",
     ],
-    static_executable: true,
     srcs: ["main.cpp"],
-    symlinks: [
-        "sbin/ueventd",
-        "sbin/watchdogd",
-    ],
+    symlinks: ["ueventd"],
+    target: {
+        recovery: {
+            cflags: ["-DRECOVERY"],
+            exclude_shared_libs: ["libbinder", "libutils"],
+        },
+    },
 }
-*/
 
 // Tests
 // ------------------------------------------------------------------------------
@@ -166,26 +188,28 @@
 cc_test {
     name: "init_tests",
     defaults: ["init_defaults"],
-    static_executable: true,
+    compile_multilib: "first",
     srcs: [
         "devices_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",
     ],
     static_libs: ["libinit"],
+    test_suites: ["device-tests"],
 }
 
 cc_benchmark {
     name: "init_benchmarks",
-    static_executable: true,
     defaults: ["init_defaults"],
     srcs: [
         "subcontext_benchmark.cpp",
@@ -231,13 +255,18 @@
         "action_parser.cpp",
         "capabilities.cpp",
         "descriptors.cpp",
+        "epoll.cpp",
+        "keychords.cpp",
         "import_parser.cpp",
-        "host_init_parser.cpp",
-        "host_init_stubs.cpp",
+        "host_import_parser.cpp",
+        "host_init_verifier.cpp",
         "parser.cpp",
         "rlimit_parser.cpp",
         "tokenizer.cpp",
         "service.cpp",
+        "service_list.cpp",
+        "service_parser.cpp",
+        "service_utils.cpp",
         "subcontext.cpp",
         "subcontext.proto",
         "util.cpp",
@@ -245,7 +274,10 @@
     proto: {
         type: "lite",
     },
-    generated_headers: ["generated_stub_builtin_function_map"],
+    generated_headers: [
+        "generated_stub_builtin_function_map",
+        "generated_android_ids"
+    ],
     target: {
         android: {
             enabled: false,
diff --git a/init/Android.mk b/init/Android.mk
index c4a6a50..9017772 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -2,10 +2,13 @@
 
 LOCAL_PATH:= $(call my-dir)
 
+-include system/sepolicy/policy_version.mk
+
 # --
 
 ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
 init_options += \
+    -DALLOW_FIRST_STAGE_CONSOLE=1 \
     -DALLOW_LOCAL_PROP_OVERRIDE=1 \
     -DALLOW_PERMISSIVE_SELINUX=1 \
     -DREBOOT_BOOTLOADER_ON_PANIC=1 \
@@ -13,6 +16,7 @@
     -DDUMP_ON_UMOUNT_FAILURE=1
 else
 init_options += \
+    -DALLOW_FIRST_STAGE_CONSOLE=0 \
     -DALLOW_LOCAL_PROP_OVERRIDE=0 \
     -DALLOW_PERMISSIVE_SELINUX=0 \
     -DREBOOT_BOOTLOADER_ON_PANIC=0 \
@@ -28,66 +32,108 @@
     -DSHUTDOWN_ZERO_TIMEOUT=0
 endif
 
-init_options += -DLOG_UEVENTS=0
+init_options += -DLOG_UEVENTS=0 \
+    -DSEPOLICY_VERSION=$(POLICYVERS)
 
 init_cflags += \
     $(init_options) \
     -Wall -Wextra \
     -Wno-unused-parameter \
     -Werror \
-    -std=gnu++1z \
 
 # --
 
+# Do not build this even with mmma if we're system-as-root, otherwise it will overwrite the symlink.
+ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
 include $(CLEAR_VARS)
 LOCAL_CPPFLAGS := $(init_cflags)
-LOCAL_SRC_FILES := main.cpp
+LOCAL_SRC_FILES := \
+    devices.cpp \
+    first_stage_init.cpp \
+    first_stage_main.cpp \
+    first_stage_mount.cpp \
+    mount_namespace.cpp \
+    reboot_utils.cpp \
+    selabel.cpp \
+    selinux.cpp \
+    switch_root.cpp \
+    uevent_listener.cpp \
+    util.cpp \
 
-LOCAL_MODULE:= init
+LOCAL_MODULE := init_first_stage
+LOCAL_MODULE_STEM := init
 
 LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
-LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
+
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)
+LOCAL_UNSTRIPPED_PATH := $(TARGET_RAMDISK_OUT_UNSTRIPPED)
+
+# Install adb_debug.prop into debug ramdisk.
+# This allows adb root on a user build, when debug ramdisk is used.
+LOCAL_REQUIRED_MODULES := \
+   adb_debug.prop \
+
+# Set up the same mount points on the ramdisk that system-as-root contains.
+LOCAL_POST_INSTALL_CMD := mkdir -p \
+    $(TARGET_RAMDISK_OUT)/apex \
+    $(TARGET_RAMDISK_OUT)/debug_ramdisk \
+    $(TARGET_RAMDISK_OUT)/dev \
+    $(TARGET_RAMDISK_OUT)/mnt \
+    $(TARGET_RAMDISK_OUT)/proc \
+    $(TARGET_RAMDISK_OUT)/sys \
 
 LOCAL_STATIC_LIBRARIES := \
-    libinit \
-    libbootloader_message \
+    libc++fs \
+    libfs_avb \
     libfs_mgr \
     libfec \
     libfec_rs \
-    libhidl-gen-utils \
     libsquashfs_utils \
     liblogwrap \
     libext4_utils \
-    libcutils \
-    libbase \
-    libc \
+    libfscrypt \
     libseccomp_policy \
-    libselinux \
-    liblog \
     libcrypto_utils \
-    libcrypto \
-    libc++_static \
-    libdl \
     libsparse \
-    libz \
-    libprocessgroup \
     libavb \
     libkeyutils \
-    libprotobuf-cpp-lite \
-    libpropertyinfoserializer \
-    libpropertyinfoparser \
-
-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
+    liblp \
+    libcutils \
+    libbase \
+    liblog \
+    libcrypto \
+    libdl \
+    libz \
+    libselinux \
+    libcap \
+    libgsi \
+    libcom.android.sysprop.apex \
+    liblzma \
+    libdexfile_support \
+    libunwindstack \
+    libbacktrace \
+    libmodprobe \
 
 LOCAL_SANITIZE := signed-integer-overflow
+# First stage init is weird: it may start without stdout/stderr, and no /proc.
+LOCAL_NOSANITIZE := hwaddress
 include $(BUILD_EXECUTABLE)
+endif
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := init_system
+LOCAL_REQUIRED_MODULES := \
+   init_second_stage \
+
+include $(BUILD_PHONY_PACKAGE)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := init_vendor
+ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
+LOCAL_REQUIRED_MODULES := \
+   init_first_stage \
+
+endif
+include $(BUILD_PHONY_PACKAGE)
diff --git a/init/README.md b/init/README.md
index 9099427..8179bff 100644
--- a/init/README.md
+++ b/init/README.md
@@ -35,29 +35,19 @@
 at the beginning of its execution.  It is responsible for the initial
 set up of the system.
 
-Devices that mount /system, /vendor through the first stage mount mechanism
-load all of the files contained within the
+Init loads all of the files contained within the
 /{system,vendor,odm}/etc/init/ directories immediately after loading
 the primary /init.rc.  This is explained in more details in the
 Imports section of this file.
 
-Legacy devices without the first stage mount mechanism do the following:
-1. /init.rc imports /init.${ro.hardware}.rc which is the primary
-   vendor supplied .rc file.
-2. During the mount\_all command, the init executable loads all of the
-   files contained within the /{system,vendor,odm}/etc/init/ directories.
-   These directories are intended for all Actions and Services used after
-   file system mounting.
-
-One may specify paths in the mount\_all command line to have it import
-.rc files at the specified paths instead of the default ones listed above.
-This is primarily for supporting factory mode and other non-standard boot
-modes.  The three default paths should be used for the normal boot process.
+Legacy devices without the first stage mount mechanism previously were
+able to import init scripts during mount_all, however that is deprecated
+and not allowed for devices launching after Q.
 
 The intention of these directories is:
 
    1. /system/etc/init/ is for core system items such as
-      SurfaceFlinger, MediaService, and logcatd.
+      SurfaceFlinger, MediaService, and logd.
    2. /vendor/etc/init/ is for SoC vendor items such as actions or
       daemons needed for core SoC functionality.
    3. /odm/etc/init/ is for device manufacturer items such as
@@ -72,7 +62,7 @@
 init .rc file should additionally contain any actions associated with
 its service.
 
-An example is the logcatd.rc and Android.mk files located in the
+An example is the userdebug logcatd.rc and Android.mk files located in the
 system/core/logcat directory.  The LOCAL\_INIT\_RC macro in the
 Android.mk file places logcatd.rc in /system/etc/init/ during the
 build process.  Init loads logcatd.rc during the mount\_all command and
@@ -88,14 +78,6 @@
 conflict resolution when multiple services are added to the system, as
 each one will go into a separate file.
 
-There are two options "early" and "late" in mount\_all command
-which can be set after optional paths. With "--early" set, the
-init executable will skip mounting entries with "latemount" flag
-and triggering fs encryption state event. With "--late" set,
-init executable will only mount entries with "latemount" flag but skip
-importing rc files. By default, no option is set, and mount\_all will
-process all entries in the given fstab.
-
 Actions
 -------
 Actions are named sequences of commands.  Actions have a trigger which
@@ -161,6 +143,27 @@
 Options are modifiers to services.  They affect how and when init
 runs the service.
 
+`capabilities [ <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.
+  If no capabilities are provided, then all capabilities are removed from this service, even if it
+  runs as root.
+
+`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,22 +173,11 @@
 
 `critical`
 > This is a device-critical service. If it exits more than four times in
-  four minutes, the device will reboot into recovery mode.
+  four minutes or before boot completes, the device will reboot into bootloader.
 
 `disabled`
 > This service will not automatically start with its class.
-  It must be explicitly started by name.
-
-`setenv <name> <value>`
-> Set the environment variable _name_ to _value_ in the launched process.
-
-`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
-  group default to 0.  'seclabel' is the SELinux security context for the
-  socket.  It defaults to the service security context, as specified by
-  seclabel or computed based on the service executable file security context.
-  For native executables see libcutils android\_get\_control\_socket().
+  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
@@ -196,6 +188,137 @@
   "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. For instance, this is used to allow
+  hwservicemanager to lazily start services. When multiple interfaces are served, this tag should
+  be used multiple times.
+  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.
+
+> This option may take a property instead of a list of keycodes. In this case, only one option is
+  provided: the property name in the typical property expansion format. The property must contain
+  a comma separated list of keycode values or the text 'none' to indicate that
+  this service does not respond to keycodes.
+
+> For example, `keycodes ${some.property.name:-none}` where some.property.name expands
+  to "123,124,125". Since keycodes are handled very early in init,
+  only PRODUCT_DEFAULT_PROPERTY_OVERRIDES properties can be used.
+
+`memcg.limit_in_bytes <value>` and `memcg.limit_percent <value>`
+> Sets the child's memory.limit_in_bytes to the minimum of `limit_in_bytes`
+  bytes and `limit_percent` which is interpreted as a percentage of the size
+  of the device's physical memory (only if memcg is mounted).
+  Values must be equal or greater than 0.
+
+`memcg.limit_property <value>`
+> Sets the child's memory.limit_in_bytes to the value of the specified property
+  (only if memcg is mounted). This property will override the values specified
+  via `memcg.limit_in_bytes` and `memcg.limit_percent`.
+
+`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().
+
+`restart_period <seconds>`
+> If a non-oneshot service exits, it will be restarted at its start time plus
+  this period. It defaults to 5s to rate limit crashing services.
+  This can be increased for services that are meant to run periodically. For
+  example, it may be set to 3600 to indicate that the service should run every hour
+  or 86400 to indicate that the service should run every day.
+
+`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
+  group default to 0.  'seclabel' is the SELinux security context for the
+  socket.  It defaults to the service security context, as specified by
+  seclabel or computed based on the service executable file security context.
+  For native executables see libcutils android\_get\_control\_socket().
+
+`timeout_period <seconds>`
+> Provide a timeout after which point the service will be killed. The oneshot keyword is respected
+  here, so oneshot services do not automatically restart, however all other services will.
+  This is particularly useful for creating a periodic service combined with the restart_period
+  option described above.
+
+`updatable`
+> Mark that the service can be overridden (via the 'override' option) later in
+  the boot sequence by APEXes. When a service with updatable option is started
+  before APEXes are all activated, the execution is delayed until the activation
+  is finished. A service that is not marked as updatable cannot be overridden by
+  APEXes.
+
 `user <username>`
 > Change to 'username' before exec'ing this service.
   Currently defaults to root.  (??? probably should default to nobody)
@@ -212,88 +335,12 @@
   As of Android O, processes can also request capabilities directly in their .rc
   files. See the "capabilities" option below.
 
-`group <groupname> [ <groupname>\* ]`
-> Change to 'groupname' before exec'ing this service.  Additional
-  groupnames beyond the (required) first one are used to set the
-  supplemental groups of the process (via setgroups()).
-  Currently defaults to root.  (??? probably should default to nobody)
-
-`capabilities <capability> [ <capability>\* ]`
-> Set capabilities when exec'ing this service. 'capability' should be a Linux
-  capability without the "CAP\_" prefix, like "NET\_ADMIN" or "SETPCAP". See
-  http://man7.org/linux/man-pages/man7/capabilities.7.html for a list of Linux
-  capabilities.
-
-`setrlimit <resource> <cur> <max>`
-> This applies the given rlimit to the service. rlimits are inherited by child
-  processes, so this effectively applies the given rlimit to the process tree
-  started by this service.
-  It is parsed similarly to the setrlimit command specified below.
-
-`seclabel <seclabel>`
-> Change to 'seclabel' before exec'ing this service.
-  Primarily for use by services run from the rootfs, e.g. ueventd, adbd.
-  Services on the system partition can instead use policy-defined transitions
-  based on their file security context.
-  If not specified and no transition is defined in policy, defaults to the init context.
-
-`oneshot`
-> Do not restart the service when it exits.
-
-`class <name> [ <name>\* ]`
-> Specify class names for the service.  All services in a
-  named class may be started or stopped together.  A service
-  is in the class "default" if one is not specified via the
-  class option. Additional classnames beyond the (required) first
-  one are used to group services.
-`animation class`
-> 'animation' class should include all services necessary for both
-  boot animation and shutdown animation. As these services can be
-  launched very early during bootup and can run until the last stage
-  of shutdown, access to /data partition is not guaranteed. These
-  services can check files under /data but it should not keep files opened
-  and should work when /data is not available.
-
-`onrestart`
-> Execute a Command (see below) when service restarts.
-
 `writepid <file> [ <file>\* ]`
 > Write the child's pid to the given files when it forks. Meant for
   cgroup/cpuset usage. If no files under /dev/cpuset/ are specified, but the
   system property 'ro.cpuset.default' is set to a non-empty cpuset name (e.g.
   '/foreground'), then the pid is written to file /dev/cpuset/_cpuset\_name_/tasks.
 
-`priority <priority>`
-> Scheduling priority of the service process. This value has to be in range
-  -20 to 19. Default priority is 0. Priority is set via setpriority().
-
-`namespace <pid|mnt>`
-> Enter a new PID or mount namespace when forking the service.
-
-`oom_score_adjust <value>`
-> Sets the child's /proc/self/oom\_score\_adj to the specified value,
-  which must range from -1000 to 1000.
-
-`memcg.swappiness <value>`
-> Sets the child's memory.swappiness to the specified value (only if memcg is mounted),
-  which must be equal or greater than 0.
-
-`memcg.soft_limit_in_bytes <value>`
-> Sets the child's memory.soft_limit_in_bytes to the specified value (only if memcg is mounted),
-  which must be equal or greater than 0.
-
-`memcg.limit_in_bytes <value>`
-> Sets the child's memory.limit_in_bytes to the specified value (only if memcg is mounted),
-  which must be equal or greater than 0.
-
-`shutdown <shutdown_behavior>`
-> Set shutdown behavior of the service process. When this is not specified,
-  the service is killed during shutdown process by using SIGTERM and SIGKILL.
-  The service with shutdown_behavior of "critical" is not killed during shutdown
-  until shutdown times out. When shutdown times out, even services tagged with
-  "shutdown critical" will be killed. When the service tagged with "shutdown critical"
-  is not running when shut down starts, it will be started.
-
 
 Triggers
 --------
@@ -347,6 +394,11 @@
   not already running.  See the start entry for more information on
   starting services.
 
+`class_start_post_data <serviceclass>`
+> Like `class_start`, but only considers services that were started
+  after /data was mounted, and that were running at the time
+ `class_reset_post_data` was called. Only used for FDE devices.
+
 `class_stop <serviceclass>`
 > Stop and disable all services of the specified class if they are
   currently running.
@@ -356,6 +408,10 @@
   currently running, without disabling them. They can be restarted
   later using `class_start`.
 
+`class_reset_post_data <serviceclass>`
+> Like `class_reset`, but only considers services that were started
+  after /data was mounted. Only used for FDE devices.
+
 `class_restart <serviceclass>`
 > Restarts all services of the specified class.
 
@@ -415,16 +471,23 @@
   -f: force installation of the module even if the version of the running kernel
   and the version of the kernel for which the module was compiled do not match.
 
-`load_all_props`
-> Loads properties from /system, /vendor, et cetera.
-  This is included in the default init.rc.
+`load_system_props`
+> (This action is deprecated and no-op.)
 
 `load_persist_props`
 > Loads persistent properties when /data has been decrypted.
   This is included in the default init.rc.
 
 `loglevel <level>`
-> Sets the kernel log level to level. Properties are expanded within _level_.
+> Sets init's log level to the integer level, from 7 (all logging) to 0
+  (fatal logging only). The numeric values correspond to the kernel log
+  levels, but this command does not affect the kernel log level. Use the
+  `write` command to write to `/proc/sys/kernel/printk` to change that.
+  Properties are expanded within _level_.
+
+`mark_post_data`
+> Used to mark the point right after /data is mounted. Used to implement the
+  `class_reset_post_data` and `class_start_post_data` commands.
 
 `mkdir <path> [mode] [owner] [group]`
 > Create a directory at _path_, optionally with the given mode, owner, and
@@ -433,16 +496,22 @@
   will be updated if the directory exists already.
 
 `mount_all <fstab> [ <path> ]\* [--<option>]`
-> Calls fs\_mgr\_mount\_all on the given fs\_mgr-format fstab and imports .rc files
-  at the specified paths (e.g., on the partitions just mounted) with optional
+> Calls fs\_mgr\_mount\_all on the given fs\_mgr-format fstab with optional
   options "early" and "late".
-  Refer to the section of "Init .rc Files" for detail.
+  With "--early" set, the init executable will skip mounting entries with
+  "latemount" flag and triggering fs encryption state event. With "--late" set,
+  init executable will only mount entries with "latemount" flag. By default,
+  no option is set, and mount\_all will process all entries in the given fstab.
 
 `mount <type> <device> <dir> [ <flag>\* ] [<options>]`
 > Attempt to mount the named device at the directory _dir_
   _flag_s include "ro", "rw", "remount", "noatime", ...
   _options_ include "barrier=1", "noauto\_da\_alloc", "discard", ... as
-  a comma separated string, eg: barrier=1,noauto\_da\_alloc
+  a comma separated string, e.g. barrier=1,noauto\_da\_alloc
+
+`parse_apex_configs`
+> Parses config file(s) from the mounted APEXes. Intended to be used only once
+  when apexd notifies the mount event by setting apexd.status to ready.
 
 `restart <service>`
 > Stops and restarts a running service, does nothing if the service is currently
@@ -480,6 +549,7 @@
   _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.
+  _cur_ and _max_ can be 'unlimited' or '-1' to indicate an infinite rlimit.
 
 `start <service>`
 > Start a service running if it is not already running.
@@ -503,7 +573,7 @@
 `symlink <target> <path>`
 > Create a symbolic link at _path_ with the value _target_
 
-`sysclktz <mins_west_of_gmt>`
+`sysclktz <minutes_west_of_gmt>`
 > Set the system clock base (0 if system clock ticks in GMT)
 
 `trigger <event>`
@@ -513,9 +583,6 @@
 `umount <path>`
 > Unmount the filesystem mounted at that path.
 
-`verity_load_state`
-> Internal implementation detail used to load dm-verity state.
-
 `verity_update_state <mount-point>`
 > Internal implementation detail used to update dm-verity state and
   set the partition._mount-point_.verified properties used by adb remount
@@ -555,8 +622,9 @@
       `ro.boot.init_rc` during initial boot.
    2. When it imports /{system,vendor,odm}/etc/init/ for first stage mount
       devices immediately after importing /init.rc.
-   3. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
-      paths during mount_all.
+   3. (Deprecated) When it imports /{system,vendor,odm}/etc/init/ or .rc files
+      at specified paths during mount_all, not allowed for devices launching
+      after Q.
 
 The order that files are imported is a bit complex for legacy reasons
 and to keep backwards compatibility.  It is not strictly guaranteed.
@@ -566,7 +634,7 @@
 earlier executed trigger, or 2) place it in an Action with the same
 trigger within the same file at an earlier line.
 
-Nonetheless, the defacto order for first stage mount devices is:
+Nonetheless, the de facto order for first stage mount devices is:
 1. /init.rc is parsed then recursively each of its imports are
    parsed.
 2. The contents of /system/etc/init/ are alphabetized and parsed
@@ -591,12 +659,19 @@
 
 Properties
 ----------
-Init provides information about the services that it is responsible
-for via the below properties.
+Init provides state information with the following properties.
 
 `init.svc.<name>`
 > State of a named service ("stopped", "stopping", "running", "restarting")
 
+`dev.mnt.blk.<mount_point>`
+> Block device base name associated with a *mount_point*.
+  The *mount_point* has / replaced by . and if referencing the root mount point
+  "/", it will use "/root", specifically `dev.mnt.blk.root`.
+  Meant for references to `/sys/device/block/${dev.mnt.blk.<mount_point>}/` and
+  `/sys/fs/ext4/${dev.mnt.blk.<mount_point>}/` to tune the block device
+  characteristics in a device agnostic manner.
+
 
 Boot timing
 -----------
@@ -606,8 +681,11 @@
 > Time after boot in ns (via the CLOCK\_BOOTTIME clock) at which the first
   stage of init started.
 
+`ro.boottime.init.first_stage`
+> How long in ns it took to run first stage.
+
 `ro.boottime.init.selinux`
-> How long it took the first stage to initialize SELinux.
+> How long in ns it took to run SELinux stage.
 
 `ro.boottime.init.cold_boot_wait`
 > How long init waited for ueventd's coldboot phase to end.
@@ -649,7 +727,7 @@
 A handy script named compare-bootcharts.py can be used to compare the
 start/end time of selected processes. The aforementioned grab-bootchart.sh
 will leave a bootchart tarball named bootchart.tgz at /tmp/android-bootchart.
-If two such barballs are preserved on the host machine under different
+If two such tarballs are preserved on the host machine under different
 directories, the script can list the timestamps differences. For example:
 
 Usage: system/core/init/compare-bootcharts.py _base-bootchart-dir_ _exp-bootchart-dir_
@@ -690,23 +768,97 @@
 
 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 controlled 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.
+
+Early Init Boot Sequence
+------------------------
+The early init boot sequence is broken up into three stages: first stage init, SELinux setup, and
+second stage init.
+
+First stage init is responsible for setting up the bare minimum requirements to load the rest of the
+system. Specifically this includes mounting /dev, /proc, mounting 'early mount' partitions (which
+needs to include all partitions that contain system code, for example system and vendor), and moving
+the system.img mount to / for devices with a ramdisk.
+
+Note that in Android Q, system.img always contains TARGET_ROOT_OUT and always is mounted at / by the
+time first stage init finishes. Android Q will also require dynamic partitions and therefore will
+require using a ramdisk to boot Android. The recovery ramdisk can be used to boot to Android instead
+of a dedicated ramdisk as well.
+
+First stage init has three variations depending on the device configuration:
+1) For system-as-root devices, first stage init is part of /system/bin/init and a symlink at /init
+points to /system/bin/init for backwards compatibility. These devices do not need to do anything to
+mount system.img, since it is by definition already mounted as the rootfs by the kernel.
+
+2) For devices with a ramdisk, first stage init is a static executable located at /init. These
+devices mount system.img as /system then perform a switch root operation to move the mount at
+/system to /. The contents of the ramdisk are freed after mounting has completed.
+
+3) For devices that use recovery as a ramdisk, first stage init it contained within the shared init
+located at /init within the recovery ramdisk. These devices first switch root to
+/first_stage_ramdisk to remove the recovery components from the environment, then proceed the same
+as 2). Note that the decision to boot normally into Android instead of booting
+into recovery mode is made if androidboot.force_normal_boot=1 is present in the
+kernel commandline.
+
+Once first stage init finishes it execs /system/bin/init with the "selinux_setup" argument. This
+phase is where SELinux is optionally compiled and loaded onto the system. selinux.cpp contains more
+information on the specifics of this process.
+
+Lastly once that phase finishes, it execs /system/bin/init again with the "second_stage"
+argument. At this point the main phase of init runs and continues the boot process via the init.rc
+scripts.
diff --git a/init/action.cpp b/init/action.cpp
index f782b51..a169591 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -18,24 +18,18 @@
 
 #include <android-base/chrono_utils.h>
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/strings.h>
 
 #include "util.h"
 
-#if defined(__ANDROID__)
-#include <android-base/properties.h>
-#else
-#include "host_init_stubs.h"
-#endif
-
 using android::base::Join;
 
 namespace android {
 namespace init {
 
-Result<Success> RunBuiltinFunction(const BuiltinFunction& function,
-                                   const std::vector<std::string>& args,
-                                   const std::string& context) {
+Result<void> RunBuiltinFunction(const BuiltinFunction& function,
+                                const std::vector<std::string>& args, const std::string& context) {
     auto builtin_arguments = BuiltinArguments(context);
 
     builtin_arguments.args.resize(args.size());
@@ -49,11 +43,14 @@
     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) {}
+Command::Command(BuiltinFunction f, bool execute_in_subcontext, std::vector<std::string>&& args,
+                 int line)
+    : func_(std::move(f)),
+      execute_in_subcontext_(execute_in_subcontext),
+      args_(std::move(args)),
+      line_(line) {}
 
-Result<Success> Command::InvokeFunc(Subcontext* subcontext) const {
+Result<void> Command::InvokeFunc(Subcontext* subcontext) const {
     if (subcontext) {
         if (execute_in_subcontext_) {
             return subcontext->Execute(args_);
@@ -85,7 +82,7 @@
 
 const KeywordFunctionMap* Action::function_map_ = nullptr;
 
-Result<Success> Action::AddCommand(const std::vector<std::string>& args, int line) {
+Result<void> Action::AddCommand(std::vector<std::string>&& args, int line) {
     if (!function_map_) {
         return Error() << "no function map available";
     }
@@ -93,12 +90,12 @@
     auto function = function_map_->FindFunction(args);
     if (!function) return Error() << function.error();
 
-    commands_.emplace_back(function->second, function->first, args, line);
-    return Success();
+    commands_.emplace_back(function->second, function->first, std::move(args), line);
+    return {};
 }
 
-void Action::AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line) {
-    commands_.emplace_back(f, false, args, line);
+void Action::AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line) {
+    commands_.emplace_back(f, false, std::move(args), line);
 }
 
 std::size_t Action::NumCommands() const {
@@ -129,7 +126,7 @@
     // 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) {
+        result.error().code() == ENOENT) {
         report_failure = false;
     }
 
@@ -141,7 +138,7 @@
 
         LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
                   << ":" << command.line() << ") took " << duration.count() << "ms and "
-                  << (result ? "succeeded" : "failed: " + result.error_string());
+                  << (result ? "succeeded" : "failed: " + result.error().message());
     }
 }
 
diff --git a/init/action.h b/init/action.h
index 4f063cc..13b250a 100644
--- a/init/action.h
+++ b/init/action.h
@@ -31,15 +31,15 @@
 namespace android {
 namespace init {
 
-Result<Success> RunBuiltinFunction(const BuiltinFunction& function,
-                                   const std::vector<std::string>& args, const std::string& context);
+Result<void> RunBuiltinFunction(const BuiltinFunction& function,
+                                const std::vector<std::string>& args, const std::string& context);
 
 class Command {
   public:
-    Command(BuiltinFunction f, bool execute_in_subcontext, const std::vector<std::string>& args,
+    Command(BuiltinFunction f, bool execute_in_subcontext, std::vector<std::string>&& args,
             int line);
 
-    Result<Success> InvokeFunc(Subcontext* subcontext) const;
+    Result<void> InvokeFunc(Subcontext* subcontext) const;
     std::string BuildCommandString() const;
 
     int line() const { return line_; }
@@ -61,8 +61,8 @@
            const std::string& event_trigger,
            const std::map<std::string, std::string>& property_triggers);
 
-    Result<Success> AddCommand(const std::vector<std::string>& args, int line);
-    void AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line);
+    Result<void> AddCommand(std::vector<std::string>&& args, int line);
+    void AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line);
     std::size_t NumCommands() const;
     void ExecuteOneCommand(std::size_t command) const;
     void ExecuteAllCommands() const;
diff --git a/init/action_manager.cpp b/init/action_manager.cpp
index 22977bb..985b8ad 100644
--- a/init/action_manager.cpp
+++ b/init/action_manager.cpp
@@ -47,9 +47,7 @@
 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);
+    action->AddCommand(func, {name}, 0);
 
     event_queue_.emplace(action.get());
     actions_.emplace_back(std::move(action));
@@ -90,7 +88,8 @@
         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));
+            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser),
+                           actions_.end());
         }
     }
 }
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index a2c9671..ff20e43 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -16,12 +16,11 @@
 
 #include "action_parser.h"
 
+#include <android-base/properties.h>
 #include <android-base/strings.h>
 
-#include "stable_properties.h"
-
 #if defined(__ANDROID__)
-#include <android-base/properties.h>
+#include "property_service.h"
 #else
 #include "host_init_stubs.h"
 #endif
@@ -41,19 +40,23 @@
         return true;
     }
 
-    if (kExportedActionableProperties.count(prop_name) == 1) {
-        return true;
-    }
+    static constexpr const char* kPartnerPrefixes[] = {
+            "init.svc.vendor.", "ro.vendor.",    "persist.vendor.",
+            "vendor.",          "init.svc.odm.", "ro.odm.",
+            "persist.odm.",     "odm.",          "ro.boot.",
+    };
+
     for (const auto& prefix : kPartnerPrefixes) {
         if (android::base::StartsWith(prop_name, prefix)) {
             return true;
         }
     }
-    return false;
+
+    return CanReadProperty(subcontext->context(), prop_name);
 }
 
-Result<Success> ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext,
-                                     std::map<std::string, std::string>* property_triggers) {
+Result<void> 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('=');
@@ -65,18 +68,18 @@
     prop_name.erase(equal_pos);
 
     if (!IsActionableProperty(subcontext, prop_name)) {
-        return Error() << "unexported property tigger found: " << 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();
+    return {};
 }
 
-Result<Success> ParseTriggers(const std::vector<std::string>& args, Subcontext* subcontext,
-                              std::string* event_trigger,
-                              std::map<std::string, std::string>* property_triggers) {
+Result<void> 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()) {
@@ -105,13 +108,13 @@
         }
     }
 
-    return Success();
+    return {};
 }
 
 }  // namespace
 
-Result<Success> ActionParser::ParseSection(std::vector<std::string>&& args,
-                                           const std::string& filename, int line) {
+Result<void> 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";
@@ -139,19 +142,19 @@
                                            property_triggers);
 
     action_ = std::move(action);
-    return Success();
+    return {};
 }
 
-Result<Success> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
-    return action_ ? action_->AddCommand(std::move(args), line) : Success();
+Result<void> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+    return action_ ? action_->AddCommand(std::move(args), line) : Result<void>{};
 }
 
-Result<Success> ActionParser::EndSection() {
+Result<void> ActionParser::EndSection() {
     if (action_ && action_->NumCommands() > 0) {
         action_manager_->AddAction(std::move(action_));
     }
 
-    return Success();
+    return {};
 }
 
 }  // namespace init
diff --git a/init/action_parser.h b/init/action_parser.h
index b7f7074..2fe9983 100644
--- a/init/action_parser.h
+++ b/init/action_parser.h
@@ -32,10 +32,10 @@
   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;
+    Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                              int line) override;
+    Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
+    Result<void> EndSection() override;
 
   private:
     ActionManager* action_manager_;
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 379b4fa..b7db9b6 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,20 +165,20 @@
   LOG(INFO) << "Bootcharting finished";
 }
 
-static Result<Success> do_bootchart_start() {
+static Result<void> 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();
+        return {};
     }
 
     g_bootcharting_thread = new std::thread(bootchart_thread_main);
-    return Success();
+    return {};
 }
 
-static Result<Success> do_bootchart_stop() {
-    if (!g_bootcharting_thread) return Success();
+static Result<void> do_bootchart_stop() {
+    if (!g_bootcharting_thread) return {};
 
     // Tell the worker thread it's time to quit.
     {
@@ -188,10 +190,10 @@
     g_bootcharting_thread->join();
     delete g_bootcharting_thread;
     g_bootcharting_thread = nullptr;
-    return Success();
+    return {};
 }
 
-Result<Success> do_bootchart(const BuiltinArguments& args) {
+Result<void> do_bootchart(const BuiltinArguments& args) {
     if (args[1] == "start") return do_bootchart_start();
     return do_bootchart_stop();
 }
diff --git a/init/bootchart.h b/init/bootchart.h
index 05474ca..6f19aad 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -26,7 +26,7 @@
 namespace android {
 namespace init {
 
-Result<Success> do_bootchart(const BuiltinArguments& args);
+Result<void> do_bootchart(const BuiltinArguments& args);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/boringssl_self_test.cpp b/init/boringssl_self_test.cpp
index 850f1aa..759eb43 100644
--- a/init/boringssl_self_test.cpp
+++ b/init/boringssl_self_test.cpp
@@ -25,7 +25,7 @@
 namespace android {
 namespace init {
 
-Result<Success> StartBoringSslSelfTest(const BuiltinArguments&) {
+Result<void> StartBoringSslSelfTest(const BuiltinArguments&) {
     pid_t id = fork();
 
     if (id == 0) {
@@ -36,8 +36,8 @@
             // to boot. Rebooting to bootloader to wait for
             // further action from the user.
 
-            int result =
-                android_reboot(ANDROID_RB_RESTART2, 0, "bootloader,boringssl-self-check-failed");
+            int result = android_reboot(ANDROID_RB_RESTART2, 0,
+                                        "bootloader,boringssl-self-check-failed");
             if (result != 0) {
                 LOG(ERROR) << "Failed to reboot into bootloader";
             }
@@ -49,7 +49,7 @@
         PLOG(FATAL) << "Failed to fork for BoringSSL self test";
     }
 
-    return Success();
+    return {};
 }
 
 }  // namespace init
diff --git a/init/boringssl_self_test.h b/init/boringssl_self_test.h
index b21fc78..9e717d0 100644
--- a/init/boringssl_self_test.h
+++ b/init/boringssl_self_test.h
@@ -22,7 +22,7 @@
 namespace android {
 namespace init {
 
-Result<Success> StartBoringSslSelfTest(const BuiltinArguments&);
+Result<void> StartBoringSslSelfTest(const BuiltinArguments&);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 8bd92cc..ba2c7ac 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -16,10 +16,12 @@
 
 #include "builtins.h"
 
+#include <android/api-level.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <fts.h>
+#include <glob.h>
 #include <linux/loop.h>
 #include <linux/module.h>
 #include <mntent.h>
@@ -50,9 +52,10 @@
 #include <android-base/unique_fd.h>
 #include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
-#include <ext4_utils/ext4_crypt.h>
-#include <ext4_utils/ext4_crypt_init_extensions.h>
 #include <fs_mgr.h>
+#include <fscrypt/fscrypt.h>
+#include <fscrypt/fscrypt_init_extensions.h>
+#include <libgsi/libgsi.h>
 #include <selinux/android.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
@@ -61,34 +64,42 @@
 #include "action_manager.h"
 #include "bootchart.h"
 #include "init.h"
+#include "mount_namespace.h"
 #include "parser.h"
 #include "property_service.h"
 #include "reboot.h"
 #include "rlimit_parser.h"
+#include "selabel.h"
 #include "selinux.h"
 #include "service.h"
+#include "service_list.h"
 #include "subcontext.h"
 #include "util.h"
 
 using namespace std::literals::string_literals;
 
+using android::base::Basename;
 using android::base::unique_fd;
+using android::fs_mgr::Fstab;
+using android::fs_mgr::ReadFstabFromFile;
 
 #define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
 
 namespace android {
 namespace init {
 
+std::vector<std::string> late_import_paths;
+
 static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s;
 
-static Result<Success> reboot_into_recovery(const std::vector<std::string>& options) {
+static Result<void> reboot_into_recovery(const std::vector<std::string>& options) {
     LOG(ERROR) << "Rebooting into recovery";
     std::string err;
     if (!write_bootloader_message(options, &err)) {
         return Error() << "Failed to set bootloader message: " << err;
     }
     property_set("sys.powerctl", "reboot,recovery");
-    return Success();
+    return {};
 }
 
 template <typename F>
@@ -98,7 +109,10 @@
     }
 }
 
-static Result<Success> do_class_start(const BuiltinArguments& args) {
+static Result<void> do_class_start(const BuiltinArguments& args) {
+    // Do not start a class if it has a property persist.dont_start_class.CLASS set to 1.
+    if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
+        return {};
     // Starting a class does not start services which are explicitly disabled.
     // They must  be started individually.
     for (const auto& service : ServiceList::GetInstance()) {
@@ -109,32 +123,58 @@
             }
         }
     }
-    return Success();
+    return {};
 }
 
-static Result<Success> do_class_stop(const BuiltinArguments& args) {
+static Result<void> do_class_start_post_data(const BuiltinArguments& args) {
+    if (args.context != kInitContext) {
+        return Error() << "command 'class_start_post_data' only available in init context";
+    }
+    for (const auto& service : ServiceList::GetInstance()) {
+        if (service->classnames().count(args[1])) {
+            if (auto result = service->StartIfPostData(); !result) {
+                LOG(ERROR) << "Could not start service '" << service->name()
+                           << "' as part of class '" << args[1] << "': " << result.error();
+            }
+        }
+    }
+    return {};
+}
+
+static Result<void> do_class_stop(const BuiltinArguments& args) {
     ForEachServiceInClass(args[1], &Service::Stop);
-    return Success();
+    return {};
 }
 
-static Result<Success> do_class_reset(const BuiltinArguments& args) {
+static Result<void> do_class_reset(const BuiltinArguments& args) {
     ForEachServiceInClass(args[1], &Service::Reset);
-    return Success();
+    return {};
 }
 
-static Result<Success> do_class_restart(const BuiltinArguments& args) {
+static Result<void> do_class_reset_post_data(const BuiltinArguments& args) {
+    if (args.context != kInitContext) {
+        return Error() << "command 'class_reset_post_data' only available in init context";
+    }
+    ForEachServiceInClass(args[1], &Service::ResetIfPostData);
+    return {};
+}
+
+static Result<void> do_class_restart(const BuiltinArguments& args) {
+    // Do not restart a class if it has a property persist.dont_start_class.CLASS set to 1.
+    if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
+        return {};
     ForEachServiceInClass(args[1], &Service::Restart);
-    return Success();
+    return {};
 }
 
-static Result<Success> do_domainname(const BuiltinArguments& args) {
+static Result<void> 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 Success();
+    return {};
 }
 
-static Result<Success> do_enable(const BuiltinArguments& args) {
+static Result<void> do_enable(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "Could not find service";
 
@@ -142,10 +182,10 @@
         return Error() << "Could not enable service: " << result.error();
     }
 
-    return Success();
+    return {};
 }
 
-static Result<Success> do_exec(const BuiltinArguments& args) {
+static Result<void> do_exec(const BuiltinArguments& args) {
     auto service = Service::MakeTemporaryOneshotService(args.args);
     if (!service) {
         return Error() << "Could not create exec service";
@@ -155,10 +195,10 @@
     }
 
     ServiceList::GetInstance().AddService(std::move(service));
-    return Success();
+    return {};
 }
 
-static Result<Success> do_exec_background(const BuiltinArguments& args) {
+static Result<void> do_exec_background(const BuiltinArguments& args) {
     auto service = Service::MakeTemporaryOneshotService(args.args);
     if (!service) {
         return Error() << "Could not create exec background service";
@@ -168,10 +208,10 @@
     }
 
     ServiceList::GetInstance().AddService(std::move(service));
-    return Success();
+    return {};
 }
 
-static Result<Success> do_exec_start(const BuiltinArguments& args) {
+static Result<void> do_exec_start(const BuiltinArguments& args) {
     Service* service = ServiceList::GetInstance().FindService(args[1]);
     if (!service) {
         return Error() << "Service not found";
@@ -181,29 +221,29 @@
         return Error() << "Could not start exec service: " << result.error();
     }
 
-    return Success();
+    return {};
 }
 
-static Result<Success> do_export(const BuiltinArguments& args) {
+static Result<void> do_export(const BuiltinArguments& args) {
     if (setenv(args[1].c_str(), args[2].c_str(), 1) == -1) {
         return ErrnoError() << "setenv() failed";
     }
-    return Success();
+    return {};
 }
 
-static Result<Success> do_hostname(const BuiltinArguments& args) {
+static Result<void> 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();
+    return {};
 }
 
-static Result<Success> do_ifup(const BuiltinArguments& args) {
+static Result<void> 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)));
+    unique_fd s(TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)));
     if (s < 0) return ErrnoError() << "opening socket failed";
 
     if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
@@ -216,10 +256,10 @@
         return ErrnoError() << "ioctl(..., SIOCSIFFLAGS, ...) failed";
     }
 
-    return Success();
+    return {};
 }
 
-static Result<Success> do_insmod(const BuiltinArguments& args) {
+static Result<void> do_insmod(const BuiltinArguments& args) {
     int flags = 0;
     auto it = args.begin() + 1;
 
@@ -237,11 +277,34 @@
     int rc = syscall(__NR_finit_module, fd.get(), options.c_str(), flags);
     if (rc == -1) return ErrnoError() << "finit_module for \"" << filename << "\" failed";
 
-    return Success();
+    return {};
+}
+
+static Result<void> 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 {};
+}
+
+static Result<void> 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 {};
+}
+
+static Result<void> 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 {};
 }
 
 // mkdir <path> [mode] [owner] [group]
-static Result<Success> do_mkdir(const BuiltinArguments& args) {
+static Result<void> do_mkdir(const BuiltinArguments& args) {
     mode_t mode = 0755;
     if (args.size() >= 3) {
         mode = std::strtoul(args[2].c_str(), 0, 8);
@@ -284,21 +347,21 @@
         }
     }
 
-    if (e4crypt_is_native()) {
-        if (e4crypt_set_directory_policy(args[1].c_str())) {
+    if (fscrypt_is_native()) {
+        if (fscrypt_set_directory_policy(args[1].c_str())) {
             return reboot_into_recovery(
                 {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + args[1]});
         }
     }
-    return Success();
+    return {};
 }
 
 /* umount <path> */
-static Result<Success> do_umount(const BuiltinArguments& args) {
+static Result<void> do_umount(const BuiltinArguments& args) {
     if (umount(args[1].c_str()) < 0) {
         return ErrnoError() << "umount() failed";
     }
-    return Success();
+    return {};
 }
 
 static struct {
@@ -326,7 +389,7 @@
 #define DATA_MNT_POINT "/data"
 
 /* mount <type> <device> <path> <flags ...> <options> */
-static Result<Success> do_mount(const BuiltinArguments& args) {
+static Result<void> do_mount(const BuiltinArguments& args) {
     const char* options = nullptr;
     unsigned flags = 0;
     bool wait = false;
@@ -373,7 +436,7 @@
                         ioctl(loop, LOOP_CLR_FD, 0);
                         return ErrnoError() << "mount() failed";
                     }
-                    return Success();
+                    return {};
                 }
             }
         }
@@ -388,7 +451,7 @@
 
     }
 
-    return Success();
+    return {};
 }
 
 /* Imports .rc files from the specified paths. Default ones are applied if none is given.
@@ -416,51 +479,6 @@
     if (false) DumpState();
 }
 
-/* mount_fstab
- *
- *  Call fs_mgr_mount_all() to mount the given fstab
- */
-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
-     * process if anything goes wrong (crash or memory leak), and wait for
-     * the child to finish in the parent.
-     */
-    pid_t pid = fork();
-    if (pid > 0) {
-        /* Parent.  Wait for the child to return */
-        int status;
-        int wp_ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
-        if (wp_ret == -1) {
-            // Unexpected error code. We will continue anyway.
-            PLOG(WARNING) << "waitpid failed";
-        }
-
-        if (WIFEXITED(status)) {
-            return WEXITSTATUS(status);
-        } else {
-            return Error() << "child aborted";
-        }
-    } else if (pid == 0) {
-        /* child, call fs_mgr_mount_all() */
-
-        // So we can always see what fs_mgr_mount_all() does.
-        // Only needed if someone explicitly changes the default log level in their init.rc.
-        android::base::ScopedLogSeverity info(android::base::INFO);
-
-        struct fstab* fstab = fs_mgr_read_fstab(fstabfile);
-        int child_ret = fs_mgr_mount_all(fstab, mount_mode);
-        fs_mgr_free_fstab(fstab);
-        if (child_ret == -1) {
-            PLOG(ERROR) << "fs_mgr_mount_all returned an error";
-        }
-        _exit(child_ret);
-    } else {
-        return Error() << "fork() failed";
-    }
-}
-
 /* Queue event based on fs_mgr return code.
  *
  * code: return code of fs_mgr_mount_all
@@ -470,32 +488,35 @@
  *
  * return code is processed based on input code
  */
-static Result<Success> queue_fs_event(int code) {
+static Result<void> queue_fs_event(int code) {
     if (code == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
         ActionManager::GetInstance().QueueEventTrigger("encrypt");
-        return Success();
+        return {};
     } 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();
+        return {};
     } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
         property_set("ro.crypto.state", "unencrypted");
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
-        return Success();
+        return {};
     } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
         property_set("ro.crypto.state", "unsupported");
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
-        return Success();
+        return {};
     } else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
         /* Setup a wipe via recovery, and reboot into recovery */
+        if (android::gsi::IsGsiRunning()) {
+            return Error() << "cannot wipe within GSI";
+        }
         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" };
         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 Error() << "e4crypt_install_keyring() failed";
+        if (fscrypt_install_keyring()) {
+            return Error() << "fscrypt_install_keyring() failed";
         }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "file");
@@ -503,10 +524,10 @@
         // 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();
+        return {};
     } else if (code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED) {
-        if (e4crypt_install_keyring()) {
-            return Error() << "e4crypt_install_keyring() failed";
+        if (fscrypt_install_keyring()) {
+            return Error() << "fscrypt_install_keyring() failed";
         }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "file");
@@ -514,10 +535,10 @@
         // 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();
+        return {};
     } else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
-        if (e4crypt_install_keyring()) {
-            return Error() << "e4crypt_install_keyring() failed";
+        if (fscrypt_install_keyring()) {
+            return Error() << "fscrypt_install_keyring() failed";
         }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "file");
@@ -525,7 +546,7 @@
         // 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();
+        return {};
     } else if (code > 0) {
         Error() << "fs_mgr_mount_all() returned unexpected error " << code;
     }
@@ -539,12 +560,12 @@
  * This function might request a reboot, in which case it will
  * not return.
  */
-static Result<Success> do_mount_all(const BuiltinArguments& args) {
+static Result<void> do_mount_all(const BuiltinArguments& args) {
     std::size_t na = 0;
     bool import_rc = true;
     bool queue_event = true;
     int mount_mode = MOUNT_MODE_DEFAULT;
-    const char* fstabfile = args[1].c_str();
+    const auto& fstab_file = args[1];
     std::size_t path_arg_end = args.size();
     const char* prop_post_fix = "default";
 
@@ -564,13 +585,20 @@
 
     std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix;
     android::base::Timer t;
-    auto mount_fstab_return_code = mount_fstab(fstabfile, mount_mode);
+
+    Fstab fstab;
+    if (!ReadFstabFromFile(fstab_file, &fstab)) {
+        return Error() << "Could not read fstab";
+    }
+
+    auto mount_fstab_return_code =
+            CallFunctionAndHandleProperties(fs_mgr_mount_all, &fstab, mount_mode);
     if (!mount_fstab_return_code) {
-        return Error() << "mount_fstab() failed " << mount_fstab_return_code.error();
+        return Error() << "Could not call fs_mgr_mount_all(): " << mount_fstab_return_code.error();
     }
     property_set(prop_name, std::to_string(t.duration().count()));
 
-    if (import_rc) {
+    if (import_rc && SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
         /* Paths of .rc files are specified at the 2nd argument and beyond */
         import_late(args.args, 2, path_arg_end);
     }
@@ -584,62 +612,86 @@
         }
     }
 
-    return Success();
+    return {};
 }
 
-static Result<Success> do_swapon_all(const BuiltinArguments& args) {
-    struct fstab *fstab;
-    int ret;
+/* umount_all <fstab> */
+static Result<void> do_umount_all(const BuiltinArguments& args) {
+    Fstab fstab;
+    if (!ReadFstabFromFile(args[1], &fstab)) {
+        return Error() << "Could not read fstab";
+    }
 
-    fstab = fs_mgr_read_fstab(args[1].c_str());
-    ret = fs_mgr_swapon_all(fstab);
-    fs_mgr_free_fstab(fstab);
+    auto result = CallFunctionAndHandleProperties(fs_mgr_umount_all, &fstab);
+    if (!result) {
+        return Error() << "Could not call fs_mgr_mount_all() " << result.error();
+    }
 
-    if (ret != 0) return Error() << "fs_mgr_swapon_all() failed";
-    return Success();
+    if (*result != 0) {
+        return Error() << "fs_mgr_mount_all() failed: " << *result;
+    }
+    return {};
 }
 
-static Result<Success> do_setprop(const BuiltinArguments& args) {
+static Result<void> do_swapon_all(const BuiltinArguments& args) {
+    Fstab fstab;
+    if (!ReadFstabFromFile(args[1], &fstab)) {
+        return Error() << "Could not read fstab '" << args[1] << "'";
+    }
+
+    auto result = CallFunctionAndHandleProperties(fs_mgr_swapon_all, fstab);
+    if (!result) {
+        return Error() << "Could not call fs_mgr_swapon_all() " << result.error();
+    }
+
+    if (*result == 0) {
+        return Error() << "fs_mgr_swapon_all() failed.";
+    }
+
+    return {};
+}
+
+static Result<void> do_setprop(const BuiltinArguments& args) {
     property_set(args[1], args[2]);
-    return Success();
+    return {};
 }
 
-static Result<Success> do_setrlimit(const BuiltinArguments& args) {
+static Result<void> 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";
     }
-    return Success();
+    return {};
 }
 
-static Result<Success> do_start(const BuiltinArguments& args) {
+static Result<void> 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();
     }
-    return Success();
+    return {};
 }
 
-static Result<Success> do_stop(const BuiltinArguments& args) {
+static Result<void> do_stop(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "service " << args[1] << " not found";
     svc->Stop();
-    return Success();
+    return {};
 }
 
-static Result<Success> do_restart(const BuiltinArguments& args) {
+static Result<void> do_restart(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "service " << args[1] << " not found";
     svc->Restart();
-    return Success();
+    return {};
 }
 
-static Result<Success> do_trigger(const BuiltinArguments& args) {
+static Result<void> do_trigger(const BuiltinArguments& args) {
     ActionManager::GetInstance().QueueEventTrigger(args[1]);
-    return Success();
+    return {};
 }
 
 static int MakeSymlink(const std::string& target, const std::string& linkpath) {
@@ -660,33 +712,33 @@
     return rc;
 }
 
-static Result<Success> do_symlink(const BuiltinArguments& args) {
+static Result<void> 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 {};
         }
         return ErrnoError() << "symlink() failed";
     }
-    return Success();
+    return {};
 }
 
-static Result<Success> do_rm(const BuiltinArguments& args) {
+static Result<void> do_rm(const BuiltinArguments& args) {
     if (unlink(args[1].c_str()) < 0) {
         return ErrnoError() << "unlink() failed";
     }
-    return Success();
+    return {};
 }
 
-static Result<Success> do_rmdir(const BuiltinArguments& args) {
+static Result<void> do_rmdir(const BuiltinArguments& args) {
     if (rmdir(args[1].c_str()) < 0) {
         return ErrnoError() << "rmdir() failed";
     }
-    return Success();
+    return {};
 }
 
-static Result<Success> do_sysclktz(const BuiltinArguments& args) {
+static Result<void> 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";
@@ -695,42 +747,44 @@
     if (settimeofday(nullptr, &tz) == -1) {
         return ErrnoError() << "settimeofday() failed";
     }
-    return Success();
+    return {};
 }
 
-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");
+static Result<void> do_verity_update_state(const BuiltinArguments& args) {
+    int mode;
+    if (!fs_mgr_load_verity_state(&mode)) {
+        return Error() << "fs_mgr_load_verity_state() failed";
     }
-    if (!loaded) return Error() << "Could not load verity state";
 
-    return Success();
-}
-
-static void verity_update_property(fstab_rec *fstab, const char *mount_point,
-                                   int mode, int status) {
-    property_set("partition."s + mount_point + ".verified", std::to_string(mode));
-}
-
-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";
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        return Error() << "Failed to read default fstab";
     }
-    return Success();
+
+    for (const auto& entry : fstab) {
+        if (!fs_mgr_is_verity_enabled(entry)) {
+            continue;
+        }
+
+        // To be consistent in vboot 1.0 and vboot 2.0 (AVB), use "system" for the partition even
+        // for system as root, so it has property [partition.system.verified].
+        std::string partition = entry.mount_point == "/" ? "system" : Basename(entry.mount_point);
+        property_set("partition." + partition + ".verified", std::to_string(mode));
+    }
+
+    return {};
 }
 
-static Result<Success> do_write(const BuiltinArguments& args) {
+static Result<void> 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 Success();
+    return {};
 }
 
-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)));
+static Result<void> readahead_file(const std::string& filename, bool fully) {
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY | O_CLOEXEC)));
     if (fd == -1) {
         return ErrnoError() << "Error opening file";
     }
@@ -749,10 +803,10 @@
             return ErrnoError() << "Error reading file";
         }
     }
-    return Success();
+    return {};
 }
 
-static Result<Success> do_readahead(const BuiltinArguments& args) {
+static Result<void> do_readahead(const BuiltinArguments& args) {
     struct stat sb;
 
     if (stat(args[1].c_str(), &sb)) {
@@ -808,10 +862,10 @@
     } else if (pid < 0) {
         return ErrnoError() << "Fork failed";
     }
-    return Success();
+    return {};
 }
 
-static Result<Success> do_copy(const BuiltinArguments& args) {
+static Result<void> 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();
@@ -820,10 +874,10 @@
         return Error() << "Could not write to output file '" << args[2] << "': " << result.error();
     }
 
-    return Success();
+    return {};
 }
 
-static Result<Success> do_chown(const BuiltinArguments& args) {
+static Result<void> do_chown(const BuiltinArguments& args) {
     auto uid = DecodeUid(args[1]);
     if (!uid) {
         return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error();
@@ -844,7 +898,7 @@
         return ErrnoError() << "lchown() failed";
     }
 
-    return Success();
+    return {};
 }
 
 static mode_t get_mode(const char *s) {
@@ -860,15 +914,15 @@
     return mode;
 }
 
-static Result<Success> do_chmod(const BuiltinArguments& args) {
+static Result<void> 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 ErrnoError() << "fchmodat() failed";
     }
-    return Success();
+    return {};
 }
 
-static Result<Success> do_restorecon(const BuiltinArguments& args) {
+static Result<void> do_restorecon(const BuiltinArguments& args) {
     int ret = 0;
 
     struct flag_type {const char* name; int value;};
@@ -907,16 +961,16 @@
     }
 
     if (ret) return ErrnoError() << "selinux_android_restorecon() failed";
-    return Success();
+    return {};
 }
 
-static Result<Success> do_restorecon_recursive(const BuiltinArguments& args) {
+static Result<void> 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({std::move(non_const_args), args.context});
 }
 
-static Result<Success> do_loglevel(const BuiltinArguments& args) {
+static Result<void> do_loglevel(const BuiltinArguments& args) {
     // TODO: support names instead/as well?
     int log_level = -1;
     android::base::ParseInt(args[1], &log_level);
@@ -934,20 +988,20 @@
             return Error() << "invalid log level " << log_level;
     }
     android::base::SetMinimumLogSeverity(severity);
-    return Success();
+    return {};
 }
 
-static Result<Success> do_load_persist_props(const BuiltinArguments& args) {
+static Result<void> do_load_persist_props(const BuiltinArguments& args) {
     load_persist_props();
-    return Success();
+    return {};
 }
 
-static Result<Success> do_load_system_props(const BuiltinArguments& args) {
-    load_system_props();
-    return Success();
+static Result<void> do_load_system_props(const BuiltinArguments& args) {
+    LOG(INFO) << "deprecated action `load_system_props` called.";
+    return {};
 }
 
-static Result<Success> do_wait(const BuiltinArguments& args) {
+static Result<void> do_wait(const BuiltinArguments& args) {
     auto timeout = kCommandRetryTimeout;
     if (args.size() == 3) {
         int timeout_int;
@@ -961,10 +1015,10 @@
         return Error() << "wait_for_file() failed";
     }
 
-    return Success();
+    return {};
 }
 
-static Result<Success> do_wait_for_prop(const BuiltinArguments& args) {
+static Result<void> 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);
@@ -978,24 +1032,29 @@
     if (!start_waiting_for_property(name, value)) {
         return Error() << "already waiting for a property";
     }
-    return Success();
+    return {};
 }
 
 static bool is_file_crypto() {
     return android::base::GetProperty("ro.crypto.type", "") == "file";
 }
 
-static Result<Success> ExecWithRebootOnFailure(const std::string& reboot_reason,
-                                               const BuiltinArguments& args) {
+static Result<void> 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()) {
+            // TODO (b/122850122): support this in gsi
+            if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
                 LOG(ERROR) << "Rebooting into recovery, reason: " << reboot_reason;
-                reboot_into_recovery({"--prompt_and_wipe_data", "--reason="s + reboot_reason});
+                if (auto result = reboot_into_recovery(
+                            {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
+                    !result) {
+                    LOG(FATAL) << "Could not reboot into recovery: " << result.error();
+                }
             } else {
                 LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
             }
@@ -1005,13 +1064,13 @@
         return Error() << "Could not start exec service: " << result.error();
     }
     ServiceList::GetInstance().AddService(std::move(service));
-    return Success();
+    return {};
 }
 
-static Result<Success> do_installkey(const BuiltinArguments& args) {
-    if (!is_file_crypto()) return Success();
+static Result<void> do_installkey(const BuiltinArguments& args) {
+    if (!is_file_crypto()) return {};
 
-    auto unencrypted_dir = args[1] + e4crypt_unencrypted_folder;
+    auto unencrypted_dir = args[1] + fscrypt_unencrypted_folder;
     if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
         return ErrnoError() << "Failed to create " << unencrypted_dir;
     }
@@ -1020,12 +1079,65 @@
         {{"exec", "/system/bin/vdc", "--wait", "cryptfs", "enablefilecrypto"}, args.context});
 }
 
-static Result<Success> do_init_user0(const BuiltinArguments& args) {
+static Result<void> do_init_user0(const BuiltinArguments& args) {
     return ExecWithRebootOnFailure(
         "init_user0_failed",
         {{"exec", "/system/bin/vdc", "--wait", "cryptfs", "init_user0"}, args.context});
 }
 
+static Result<void> do_mark_post_data(const BuiltinArguments& args) {
+    ServiceList::GetInstance().MarkPostData();
+
+    return {};
+}
+
+static Result<void> do_parse_apex_configs(const BuiltinArguments& args) {
+    glob_t glob_result;
+    static constexpr char glob_pattern[] = "/apex/*/etc/*.rc";
+    const int ret = glob(glob_pattern, GLOB_MARK, nullptr, &glob_result);
+    if (ret != 0 && ret != GLOB_NOMATCH) {
+        globfree(&glob_result);
+        return Error() << "glob pattern '" << glob_pattern << "' failed";
+    }
+    std::vector<std::string> configs;
+    Parser parser = CreateServiceOnlyParser(ServiceList::GetInstance());
+    for (size_t i = 0; i < glob_result.gl_pathc; i++) {
+        std::string path = glob_result.gl_pathv[i];
+        // Filter-out /apex/<name>@<ver> paths. The paths are bind-mounted to
+        // /apex/<name> paths, so unless we filter them out, we will parse the
+        // same file twice.
+        std::vector<std::string> paths = android::base::Split(path, "/");
+        if (paths.size() >= 3 && paths[2].find('@') != std::string::npos) {
+            continue;
+        }
+        configs.push_back(path);
+    }
+    globfree(&glob_result);
+
+    bool success = true;
+    for (const auto& c : configs) {
+        if (c.back() == '/') {
+            // skip if directory
+            continue;
+        }
+        success &= parser.ParseConfigFile(c);
+    }
+    ServiceList::GetInstance().MarkServicesUpdate();
+    if (success) {
+        return {};
+    } else {
+        return Error() << "Could not parse apex configs";
+    }
+}
+
+static Result<void> do_enter_default_mount_ns(const BuiltinArguments& args) {
+    if (SwitchToDefaultMountNamespace()) {
+        return {};
+    } else {
+        return Error() << "Failed to enter into default mount namespace";
+    }
+}
+
 // Builtin-function-map start
 const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
@@ -1035,8 +1147,10 @@
         {"chmod",                   {2,     2,    {true,   do_chmod}}},
         {"chown",                   {2,     3,    {true,   do_chown}}},
         {"class_reset",             {1,     1,    {false,  do_class_reset}}},
+        {"class_reset_post_data",   {1,     1,    {false,  do_class_reset_post_data}}},
         {"class_restart",           {1,     1,    {false,  do_class_restart}}},
         {"class_start",             {1,     1,    {false,  do_class_start}}},
+        {"class_start_post_data",   {1,     1,    {false,  do_class_start_post_data}}},
         {"class_stop",              {1,     1,    {false,  do_class_stop}}},
         {"copy",                    {2,     2,    {true,   do_copy}}},
         {"domainname",              {1,     1,    {true,   do_domainname}}},
@@ -1050,9 +1164,13 @@
         {"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}}},
+        {"mark_post_data",          {0,     0,    {false,  do_mark_post_data}}},
         {"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,
@@ -1060,7 +1178,9 @@
         // 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}}},
+        {"parse_apex_configs",      {0,     0,    {false,  do_parse_apex_configs}}},
         {"umount",                  {1,     1,    {false,  do_umount}}},
+        {"umount_all",              {1,     1,    {false,  do_umount_all}}},
         {"readahead",               {1,     2,    {true,   do_readahead}}},
         {"restart",                 {1,     1,    {false,  do_restart}}},
         {"restorecon",              {1,     kMax, {true,   do_restorecon}}},
@@ -1072,10 +1192,10 @@
         {"start",                   {1,     1,    {false,  do_start}}},
         {"stop",                    {1,     1,    {false,  do_stop}}},
         {"swapon_all",              {1,     1,    {false,  do_swapon_all}}},
+        {"enter_default_mount_ns",  {0,     0,    {false,  do_enter_default_mount_ns}}},
         {"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}}},
diff --git a/init/builtins.h b/init/builtins.h
index 814b2d5..7bbf6aa 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -29,7 +29,7 @@
 namespace android {
 namespace init {
 
-using BuiltinFunction = std::function<Result<Success>(const BuiltinArguments&)>;
+using BuiltinFunction = std::function<Result<void>(const BuiltinArguments&)>;
 
 using KeywordFunctionMap = KeywordMap<std::pair<bool, BuiltinFunction>>;
 class BuiltinFunctionMap : public KeywordFunctionMap {
@@ -40,6 +40,8 @@
     const Map& map() const override;
 };
 
+extern std::vector<std::string> late_import_paths;
+
 }  // namespace init
 }  // namespace android
 
diff --git a/init/debug_ramdisk.h b/init/debug_ramdisk.h
new file mode 100644
index 0000000..4e3a395
--- /dev/null
+++ b/init/debug_ramdisk.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 {
+
+constexpr const char kDebugRamdiskProp[] = "/debug_ramdisk/adb_debug.prop";
+constexpr const char kDebugRamdiskSEPolicy[] = "/debug_ramdisk/userdebug_plat_sepolicy.cil";
+
+}  // namespace init
+}  // namespace android
diff --git a/init/devices.cpp b/init/devices.cpp
index ada1e28..e8e6cd7 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -21,8 +21,14 @@
 #include <sys/sysmacros.h>
 #include <unistd.h>
 
+#include <chrono>
+#include <map>
 #include <memory>
+#include <string>
+#include <thread>
 
+#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>
@@ -30,20 +36,19 @@
 #include <selinux/android.h>
 #include <selinux/selinux.h>
 
-#include "selinux.h"
-#include "ueventd.h"
+#include "selabel.h"
 #include "util.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
+using namespace std::chrono_literals;
 
 using android::base::Basename;
 using android::base::Dirname;
+using android::base::ReadFileToString;
 using android::base::Readlink;
 using android::base::Realpath;
 using android::base::StartsWith;
 using android::base::StringPrintf;
+using android::base::Trim;
 
 namespace android {
 namespace init {
@@ -102,6 +107,31 @@
     return true;
 }
 
+// Given a path that may start with a virtual dm block device, populate
+// the supplied buffer with the dm module's instantiated name.
+// If it doesn't start with a virtual block device, or there is some
+// error, return false.
+static bool FindDmDevicePartition(const std::string& path, std::string* result) {
+    result->clear();
+    if (!StartsWith(path, "/devices/virtual/block/dm-")) return false;
+    if (getpid() == 1) return false;  // first_stage_init has no sepolicy needs
+
+    static std::map<std::string, std::string> cache;
+    // wait_for_file will not work, the content is also delayed ...
+    for (android::base::Timer t; t.duration() < 200ms; std::this_thread::sleep_for(10ms)) {
+        if (ReadFileToString("/sys" + path + "/dm/name", result) && !result->empty()) {
+            // Got it, set cache with result, when node arrives
+            cache[path] = *result = Trim(*result);
+            return true;
+        }
+    }
+    auto it = cache.find(path);
+    if ((it == cache.end()) || (it->second.empty())) return false;
+    // Return cached results, when node goes away
+    *result = it->second;
+    return true;
+}
+
 Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
     : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
     // Set 'prefix_' or 'wildcard_' based on the below cases:
@@ -294,6 +324,7 @@
 std::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uevent) const {
     std::string device;
     std::string type;
+    std::string partition;
 
     if (FindPlatformDevice(uevent.path, &device)) {
         // Skip /devices/platform or /devices/ if present
@@ -311,6 +342,8 @@
         type = "pci";
     } else if (FindVbdDevicePrefix(uevent.path, &device)) {
         type = "vbd";
+    } else if (FindDmDevicePartition(uevent.path, &partition)) {
+        return {"/dev/block/mapper/" + partition};
     } else {
         return {};
     }
@@ -321,6 +354,7 @@
 
     auto link_path = "/dev/block/" + type + "/" + device;
 
+    bool is_boot_device = boot_devices_.find(device) != boot_devices_.end();
     if (!uevent.partition_name.empty()) {
         std::string partition_name_sanitized(uevent.partition_name);
         SanitizePartitionName(&partition_name_sanitized);
@@ -330,9 +364,13 @@
         }
         links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
         // Adds symlink: /dev/block/by-name/<partition_name>.
-        if (boot_devices_.find(device) != boot_devices_.end()) {
+        if (is_boot_device) {
             links.emplace_back("/dev/block/by-name/" + partition_name_sanitized);
         }
+    } else if (is_boot_device) {
+        // If we don't have a partition name but we are a partition on a boot device, create a
+        // symlink of /dev/block/by-name/<device_name> for symmetry.
+        links.emplace_back("/dev/block/by-name/" + uevent.device_name);
     }
 
     auto last_slash = uevent.path.rfind('/');
@@ -373,7 +411,7 @@
     }
 }
 
-void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) {
+void DeviceHandler::HandleUevent(const Uevent& uevent) {
     if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
         FixupSysPermissions(uevent.path, uevent.subsystem);
     }
@@ -419,6 +457,10 @@
     HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);
 }
 
+void DeviceHandler::ColdbootDone() {
+    skip_restorecon_ = false;
+}
+
 DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
                              std::vector<SysfsPermissions> sysfs_permissions,
                              std::vector<Subsystem> subsystems, std::set<std::string> boot_devices,
diff --git a/init/devices.h b/init/devices.h
index f9035da..9d39eaa 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -29,12 +29,15 @@
 #include <selinux/label.h>
 
 #include "uevent.h"
+#include "uevent_handler.h"
 
 namespace android {
 namespace init {
 
 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;
@@ -57,6 +60,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) {}
@@ -71,16 +76,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(std::string name) : name_(std::move(name)) {}
+    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;
     }
@@ -88,17 +101,12 @@
     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 {
+class DeviceHandler : public UeventHandler {
   public:
     friend class DeviceHandlerTester;
 
@@ -106,12 +114,12 @@
     DeviceHandler(std::vector<Permissions> dev_permissions,
                   std::vector<SysfsPermissions> sysfs_permissions, std::vector<Subsystem> subsystems,
                   std::set<std::string> boot_devices, bool skip_restorecon);
-    ~DeviceHandler(){};
+    virtual ~DeviceHandler() = default;
 
-    void HandleDeviceEvent(const Uevent& uevent);
+    void HandleUevent(const Uevent& uevent) override;
+    void ColdbootDone() override;
 
     std::vector<std::string> GetBlockDeviceSymlinks(const Uevent& uevent) const;
-    void set_skip_restorecon(bool value) { skip_restorecon_ = value; }
 
   private:
     bool FindPlatformDevice(std::string path, std::string* platform_device_path) const;
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index d658f4d..3e7c1a8 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -16,8 +16,8 @@
 
 #include "devices.h"
 
+#include <android-base/file.h>
 #include <android-base/scopeguard.h>
-#include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
 #include "util.h"
diff --git a/init/epoll.cpp b/init/epoll.cpp
new file mode 100644
index 0000000..01d8867
--- /dev/null
+++ b/init/epoll.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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 <stdint.h>
+#include <sys/epoll.h>
+
+#include <chrono>
+#include <functional>
+#include <map>
+
+namespace android {
+namespace init {
+
+Epoll::Epoll() {}
+
+Result<void> Epoll::Open() {
+    if (epoll_fd_ >= 0) return {};
+    epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
+
+    if (epoll_fd_ == -1) {
+        return ErrnoError() << "epoll_create1 failed";
+    }
+    return {};
+}
+
+Result<void> Epoll::RegisterHandler(int fd, std::function<void()> handler, uint32_t events) {
+    if (!events) {
+        return Error() << "Must specify events";
+    }
+    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 = events;
+    // 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<void> result = ErrnoError() << "epoll_ctl failed to add fd";
+        epoll_handlers_.erase(fd);
+        return result;
+    }
+    return {};
+}
+
+Result<void> 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 {};
+}
+
+Result<void> 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 {};
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/epoll.h b/init/epoll.h
new file mode 100644
index 0000000..ca84266
--- /dev/null
+++ b/init/epoll.h
@@ -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.
+ */
+
+#ifndef _INIT_EPOLL_H
+#define _INIT_EPOLL_H
+
+#include <stdint.h>
+#include <sys/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<void> Open();
+    Result<void> RegisterHandler(int fd, std::function<void()> handler, uint32_t events = EPOLLIN);
+    Result<void> UnregisterHandler(int fd);
+    Result<void> 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 8c8d9f2..c067f6f 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>
@@ -57,7 +56,10 @@
     return access("/dev/.booting", F_OK) == 0;
 }
 
-static void ProcessFirmwareEvent(const Uevent& uevent) {
+FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories)
+    : firmware_directories_(std::move(firmware_directories)) {}
+
+void FirmwareHandler::ProcessFirmwareEvent(const Uevent& uevent) {
     int booting = IsBooting();
 
     LOG(INFO) << "firmware: loading '" << uevent.firmware << "' for '" << uevent.path << "'";
@@ -78,18 +80,26 @@
         return;
     }
 
-    static const char* firmware_dirs[] = {"/etc/firmware/", "/odm/firmware/",
-                                          "/vendor/firmware/", "/firmware/image/"};
+    std::vector<std::string> attempted_paths_and_errors;
 
 try_loading_again:
-    for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
-        std::string file = firmware_dirs[i] + uevent.firmware;
+    attempted_paths_and_errors.clear();
+    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) {
-            LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
-            return;
+        if (fw_fd == -1) {
+            attempted_paths_and_errors.emplace_back("firmware: attempted " + file +
+                                                    ", open failed: " + strerror(errno));
+            continue;
         }
+        struct stat sb;
+        if (fstat(fw_fd, &sb) == -1) {
+            attempted_paths_and_errors.emplace_back("firmware: attempted " + file +
+                                                    ", fstat failed: " + strerror(errno));
+            continue;
+        }
+        LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
+        return;
     }
 
     if (booting) {
@@ -101,12 +111,15 @@
     }
 
     LOG(ERROR) << "firmware: could not find firmware for " << uevent.firmware;
+    for (const auto& message : attempted_paths_and_errors) {
+        LOG(ERROR) << message;
+    }
 
     // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
     write(loading_fd, "-1", 2);
 }
 
-void HandleFirmwareEvent(const Uevent& uevent) {
+void FirmwareHandler::HandleUevent(const Uevent& uevent) {
     if (uevent.subsystem != "firmware" || uevent.action != "add") return;
 
     // Loading the firmware in a child means we can do that in parallel...
diff --git a/init/firmware_handler.h b/init/firmware_handler.h
index e456ac4..3996096 100644
--- a/init/firmware_handler.h
+++ b/init/firmware_handler.h
@@ -17,12 +17,27 @@
 #ifndef _INIT_FIRMWARE_HANDLER_H
 #define _INIT_FIRMWARE_HANDLER_H
 
+#include <string>
+#include <vector>
+
 #include "uevent.h"
+#include "uevent_handler.h"
 
 namespace android {
 namespace init {
 
-void HandleFirmwareEvent(const Uevent& uevent);
+class FirmwareHandler : public UeventHandler {
+  public:
+    explicit FirmwareHandler(std::vector<std::string> firmware_directories);
+    virtual ~FirmwareHandler() = default;
+
+    void HandleUevent(const Uevent& uevent) override;
+
+  private:
+    void ProcessFirmwareEvent(const Uevent& uevent);
+
+    std::vector<std::string> firmware_directories_;
+};
 
 }  // namespace init
 }  // namespace android
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
new file mode 100644
index 0000000..b60c450
--- /dev/null
+++ b/init/first_stage_init.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 "first_stage_init.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <filesystem>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <modprobe/modprobe.h>
+#include <private/android_filesystem_config.h>
+
+#include "debug_ramdisk.h"
+#include "first_stage_mount.h"
+#include "reboot_utils.h"
+#include "switch_root.h"
+#include "util.h"
+
+using android::base::boot_clock;
+
+using namespace std::literals;
+
+namespace fs = std::filesystem;
+
+namespace android {
+namespace init {
+
+namespace {
+
+void FreeRamdisk(DIR* dir, dev_t dev) {
+    int dfd = dirfd(dir);
+
+    dirent* de;
+    while ((de = readdir(dir)) != nullptr) {
+        if (de->d_name == "."s || de->d_name == ".."s) {
+            continue;
+        }
+
+        bool is_dir = false;
+
+        if (de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) {
+            struct stat info;
+            if (fstatat(dfd, de->d_name, &info, AT_SYMLINK_NOFOLLOW) != 0) {
+                continue;
+            }
+
+            if (info.st_dev != dev) {
+                continue;
+            }
+
+            if (S_ISDIR(info.st_mode)) {
+                is_dir = true;
+                auto fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+                if (fd >= 0) {
+                    auto subdir =
+                            std::unique_ptr<DIR, decltype(&closedir)>{fdopendir(fd), closedir};
+                    if (subdir) {
+                        FreeRamdisk(subdir.get(), dev);
+                    } else {
+                        close(fd);
+                    }
+                }
+            }
+        }
+        unlinkat(dfd, de->d_name, is_dir ? AT_REMOVEDIR : 0);
+    }
+}
+
+void StartConsole() {
+    if (mknod("/dev/console", S_IFCHR | 0600, makedev(5, 1))) {
+        PLOG(ERROR) << "unable to create /dev/console";
+        return;
+    }
+    pid_t pid = fork();
+    if (pid != 0) {
+        int status;
+        waitpid(pid, &status, 0);
+        LOG(ERROR) << "console shell exited with status " << status;
+        return;
+    }
+    int fd = -1;
+    int tries = 10;
+    // The device driver for console may not be ready yet so retry for a while in case of failure.
+    while (tries--) {
+        fd = open("/dev/console", O_RDWR);
+        if (fd != -1) {
+            break;
+        }
+        std::this_thread::sleep_for(100ms);
+    }
+    if (fd == -1) {
+        LOG(ERROR) << "Could not open /dev/console, errno = " << errno;
+        _exit(127);
+    }
+    ioctl(fd, TIOCSCTTY, 0);
+    dup2(fd, 0);
+    dup2(fd, 1);
+    dup2(fd, 2);
+    close(fd);
+
+    const char* path = "/system/bin/sh";
+    const char* args[] = {path, nullptr};
+    int rv = execv(path, const_cast<char**>(args));
+    LOG(ERROR) << "unable to execv, returned " << rv << " errno " << errno;
+    _exit(127);
+}
+
+bool FirstStageConsole(const std::string& cmdline) {
+    return cmdline.find("androidboot.first_stage_console=1") != std::string::npos;
+}
+
+bool ForceNormalBoot(const std::string& cmdline) {
+    return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
+}
+
+}  // namespace
+
+int FirstStageMain(int argc, char** argv) {
+    if (REBOOT_BOOTLOADER_ON_PANIC) {
+        InstallRebootSignalHandlers();
+    }
+
+    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.
+    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.
+    CHECKCALL(chmod("/proc/cmdline", 0440));
+    std::string cmdline;
+    android::base::ReadFileToString("/proc/cmdline", &cmdline);
+    gid_t groups[] = {AID_READPROC};
+    CHECKCALL(setgroups(arraysize(groups), groups));
+    CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
+    CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
+
+    CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));
+
+    if constexpr (WORLD_WRITABLE_KMSG) {
+        CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
+    }
+
+    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)));
+
+    // These below mounts are done in first stage init so that first stage mount can mount
+    // subdirectories of /mnt/{vendor,product}/.  Other mounts, not required by first stage mount,
+    // should be done in rc files.
+    // 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));
+
+    // /apex is used to mount APEXes
+    CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+                    "mode=0755,uid=0,gid=0"));
+
+    // /debug_ramdisk is used to preserve additional files from the debug ramdisk
+    CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+                    "mode=0755,uid=0,gid=0"));
+#undef CHECKCALL
+
+    SetStdioToDevNull(argv);
+    // 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!";
+
+    auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};
+    if (!old_root_dir) {
+        PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";
+    }
+
+    struct stat old_root_info;
+    if (stat("/", &old_root_info) != 0) {
+        PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
+        old_root_dir.reset();
+    }
+
+    Modprobe m({"/lib/modules"});
+    if (!m.LoadListedModules()) {
+        LOG(FATAL) << "Failed to load kernel modules";
+    }
+
+    if (ALLOW_FIRST_STAGE_CONSOLE && FirstStageConsole(cmdline)) {
+        StartConsole();
+    }
+
+    if (ForceNormalBoot(cmdline)) {
+        mkdir("/first_stage_ramdisk", 0755);
+        // SwitchRoot() must be called with a mount point as the target, so we bind mount the
+        // target directory to itself here.
+        if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
+            LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
+        }
+        SwitchRoot("/first_stage_ramdisk");
+    }
+
+    // If this file is present, the second-stage init will use a userdebug sepolicy
+    // and load adb_debug.prop to allow adb root, if the device is unlocked.
+    if (access("/force_debuggable", F_OK) == 0) {
+        std::error_code ec;  // to invoke the overloaded copy_file() that won't throw.
+        if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) ||
+            !fs::copy_file("/userdebug_plat_sepolicy.cil", kDebugRamdiskSEPolicy, ec)) {
+            LOG(ERROR) << "Failed to setup debug ramdisk";
+        } else {
+            // setenv for second-stage init to read above kDebugRamdisk* files.
+            setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
+        }
+    }
+
+    if (!DoFirstStageMount()) {
+        LOG(FATAL) << "Failed to mount required partitions early ...";
+    }
+
+    struct stat new_root_info;
+    if (stat("/", &new_root_info) != 0) {
+        PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
+        old_root_dir.reset();
+    }
+
+    if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) {
+        FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);
+    }
+
+    SetInitAvbVersionInRecovery();
+
+    setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),
+           1);
+
+    const char* path = "/system/bin/init";
+    const char* args[] = {path, "selinux_setup", nullptr};
+    execv(path, const_cast<char**>(args));
+
+    // execv() only returns if an error happened, in which case we
+    // panic and never fall through this conditional.
+    PLOG(FATAL) << "execv(\"" << path << "\") failed";
+
+    return 1;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/first_stage_init.h b/init/first_stage_init.h
new file mode 100644
index 0000000..7de816f
--- /dev/null
+++ b/init/first_stage_init.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
+
+namespace android {
+namespace init {
+
+int FirstStageMain(int argc, char** argv);
+
+static constexpr char kEnvFirstStageStartedAt[] = "FIRST_STAGE_STARTED_AT";
+
+}  // namespace init
+}  // namespace android
diff --git a/init/first_stage_main.cpp b/init/first_stage_main.cpp
new file mode 100644
index 0000000..7bae84c
--- /dev/null
+++ b/init/first_stage_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 "first_stage_init.h"
+
+int main(int argc, char** argv) {
+    return android::init::FirstStageMain(argc, argv);
+}
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
new file mode 100644
index 0000000..1a5ed28
--- /dev/null
+++ b/init/first_stage_mount.cpp
@@ -0,0 +1,866 @@
+/*
+ * 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 <sys/mount.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 <fs_avb/fs_avb.h>
+#include <fs_mgr.h>
+#include <fs_mgr_dm_linear.h>
+#include <fs_mgr_overlayfs.h>
+#include <libgsi/libgsi.h>
+#include <liblp/liblp.h>
+
+#include "devices.h"
+#include "switch_root.h"
+#include "uevent.h"
+#include "uevent_listener.h"
+#include "util.h"
+
+using android::base::Split;
+using android::base::Timer;
+using android::fs_mgr::AvbHandle;
+using android::fs_mgr::AvbHandleStatus;
+using android::fs_mgr::AvbHashtreeResult;
+using android::fs_mgr::AvbUniquePtr;
+using android::fs_mgr::BuildGsiSystemFstabEntry;
+using android::fs_mgr::Fstab;
+using android::fs_mgr::FstabEntry;
+using android::fs_mgr::ReadDefaultFstab;
+using android::fs_mgr::ReadFstabFromDt;
+using android::fs_mgr::SkipMountingPartitions;
+
+using namespace std::literals;
+
+namespace android {
+namespace init {
+
+// Class Declarations
+// ------------------
+class FirstStageMount {
+  public:
+    FirstStageMount(Fstab fstab);
+    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 InitDeviceMapper();
+    bool CreateLogicalPartitions();
+    bool MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
+                        Fstab::iterator* end = nullptr);
+
+    bool MountPartitions();
+    bool TrySwitchSystemAsRoot();
+    bool TrySkipMountingPartitions();
+    bool IsDmLinearEnabled();
+    bool GetDmLinearMetadataDevice();
+    bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
+    void UseGsiIfPresent();
+
+    ListenerAction UeventCallback(const Uevent& uevent);
+
+    // Pure virtual functions.
+    virtual bool GetDmVerityDevices() = 0;
+    virtual bool SetUpDmVerity(FstabEntry* fstab_entry) = 0;
+
+    bool need_dm_verity_;
+    bool gsi_not_on_userdata_ = false;
+
+    Fstab fstab_;
+    std::string lp_metadata_partition_;
+    std::set<std::string> required_devices_partition_names_;
+    std::string super_partition_name_;
+    std::unique_ptr<DeviceHandler> device_handler_;
+    UeventListener uevent_listener_;
+};
+
+class FirstStageMountVBootV1 : public FirstStageMount {
+  public:
+    FirstStageMountVBootV1(Fstab fstab) : FirstStageMount(std::move(fstab)) {}
+    ~FirstStageMountVBootV1() override = default;
+
+  protected:
+    bool GetDmVerityDevices() override;
+    bool SetUpDmVerity(FstabEntry* fstab_entry) override;
+};
+
+class FirstStageMountVBootV2 : public FirstStageMount {
+  public:
+    friend void SetInitAvbVersionInRecovery();
+
+    FirstStageMountVBootV2(Fstab fstab);
+    ~FirstStageMountVBootV2() override = default;
+
+  protected:
+    bool GetDmVerityDevices() override;
+    bool SetUpDmVerity(FstabEntry* fstab_entry) override;
+    bool InitAvbHandle();
+
+    std::vector<std::string> vbmeta_partitions_;
+    AvbUniquePtr avb_handle_;
+};
+
+// Static Functions
+// ----------------
+static inline bool IsDtVbmetaCompatible(const Fstab& fstab) {
+    if (std::any_of(fstab.begin(), fstab.end(),
+                    [](const auto& entry) { return entry.fs_mgr_flags.avb; })) {
+        return true;
+    }
+    return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
+}
+
+static Fstab ReadFirstStageFstab() {
+    Fstab fstab;
+    if (!ReadFstabFromDt(&fstab)) {
+        if (ReadDefaultFstab(&fstab)) {
+            fstab.erase(std::remove_if(fstab.begin(), fstab.end(),
+                                       [](const auto& entry) {
+                                           return !entry.fs_mgr_flags.first_stage_mount;
+                                       }),
+                        fstab.end());
+        } else {
+            LOG(INFO) << "Failed to fstab for first stage mount";
+        }
+    }
+    return fstab;
+}
+
+static bool GetRootEntry(FstabEntry* root_entry) {
+    Fstab proc_mounts;
+    if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
+        LOG(ERROR) << "Could not read /proc/mounts and /system not in fstab, /system will not be "
+                      "available for overlayfs";
+        return false;
+    }
+
+    auto entry = std::find_if(proc_mounts.begin(), proc_mounts.end(), [](const auto& entry) {
+        return entry.mount_point == "/" && entry.fs_type != "rootfs";
+    });
+
+    if (entry == proc_mounts.end()) {
+        LOG(ERROR) << "Could not get mount point for '/' in /proc/mounts, /system will not be "
+                      "available for overlayfs";
+        return false;
+    }
+
+    *root_entry = std::move(*entry);
+
+    // We don't know if we're avb or not, so we query device mapper as if we are avb.  If we get a
+    // success, then mark as avb, otherwise default to verify.
+    auto& dm = android::dm::DeviceMapper::Instance();
+    if (dm.GetState("vroot") != android::dm::DmDeviceState::INVALID) {
+        root_entry->fs_mgr_flags.avb = true;
+    } else {
+        root_entry->fs_mgr_flags.verify = true;
+    }
+    return true;
+}
+
+static bool IsStandaloneImageRollback(const AvbHandle& builtin_vbmeta,
+                                      const AvbHandle& standalone_vbmeta,
+                                      const FstabEntry& fstab_entry) {
+    std::string old_spl = builtin_vbmeta.GetSecurityPatchLevel(fstab_entry);
+    std::string new_spl = standalone_vbmeta.GetSecurityPatchLevel(fstab_entry);
+
+    bool rollbacked = false;
+    if (old_spl.empty() || new_spl.empty() || new_spl < old_spl) {
+        rollbacked = true;
+    }
+
+    if (rollbacked) {
+        LOG(ERROR) << "Image rollback detected for " << fstab_entry.mount_point
+                   << ", SPL switches from '" << old_spl << "' to '" << new_spl << "'";
+        if (AvbHandle::IsDeviceUnlocked()) {
+            LOG(INFO) << "Allowing rollbacked standalone image when the device is unlocked";
+            return false;
+        }
+    }
+
+    return rollbacked;
+}
+
+// Class Definitions
+// -----------------
+FirstStageMount::FirstStageMount(Fstab fstab)
+    : need_dm_verity_(false), fstab_(std::move(fstab)), uevent_listener_(16 * 1024 * 1024) {
+    auto boot_devices = android::fs_mgr::GetBootDevices();
+    device_handler_ = std::make_unique<DeviceHandler>(
+            std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, std::vector<Subsystem>{},
+            std::move(boot_devices), false);
+
+    super_partition_name_ = fs_mgr_get_super_partition_name();
+}
+
+std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
+    auto fstab = ReadFirstStageFstab();
+    if (IsDtVbmetaCompatible(fstab)) {
+        return std::make_unique<FirstStageMountVBootV2>(std::move(fstab));
+    } else {
+        return std::make_unique<FirstStageMountVBootV1>(std::move(fstab));
+    }
+}
+
+bool FirstStageMount::DoFirstStageMount() {
+    if (!IsDmLinearEnabled() && fstab_.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 GetDmLinearMetadataDevice() && GetDmVerityDevices() && InitRequiredDevices();
+}
+
+bool FirstStageMount::IsDmLinearEnabled() {
+    for (const auto& entry : fstab_) {
+        if (entry.fs_mgr_flags.logical) return true;
+    }
+    return false;
+}
+
+bool FirstStageMount::GetDmLinearMetadataDevice() {
+    // Add any additional devices required for dm-linear mappings.
+    if (!IsDmLinearEnabled()) {
+        return true;
+    }
+
+    required_devices_partition_names_.emplace(super_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 (!InitDeviceMapper()) {
+        return false;
+    }
+
+    if (required_devices_partition_names_.empty()) {
+        return true;
+    }
+
+    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::InitDeviceMapper() {
+    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_->HandleUevent(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;
+    }
+    return true;
+}
+
+bool FirstStageMount::InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata) {
+    auto partition_names = android::fs_mgr::GetBlockDevicePartitionNames(metadata);
+    for (const auto& partition_name : partition_names) {
+        // The super partition was found in the earlier pass.
+        if (partition_name == super_partition_name_) {
+            continue;
+        }
+        required_devices_partition_names_.emplace(partition_name);
+    }
+    if (required_devices_partition_names_.empty()) {
+        return true;
+    }
+
+    auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
+    uevent_listener_.RegenerateUevents(uevent_callback);
+
+    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 "
+                   << super_partition_name_;
+        return false;
+    }
+
+    auto metadata = android::fs_mgr::ReadCurrentMetadata(lp_metadata_partition_);
+    if (!metadata) {
+        LOG(ERROR) << "Could not read logical partition metadata from " << lp_metadata_partition_;
+        return false;
+    }
+    if (!InitDmLinearBackingDevices(*metadata.get())) {
+        return false;
+    }
+    return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), 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 == super_partition_name_) {
+            std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
+            lp_metadata_partition_ = links[0];
+        }
+        required_devices_partition_names_.erase(iter);
+        device_handler_->HandleUevent(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_->HandleUevent(uevent);
+            found = true;
+            return ListenerAction::kStop;
+        }
+        return ListenerAction::kContinue;
+    };
+
+    uevent_listener_.RegenerateUeventsForPath(syspath, verity_callback);
+    if (!found) {
+        LOG(INFO) << "dm device '" << dm_device << "' not found in /sys, waiting for its uevent";
+        Timer t;
+        uevent_listener_.Poll(verity_callback, 10s);
+        LOG(INFO) << "wait for dm device '" << dm_device << "' returned after " << t;
+    }
+    if (!found) {
+        LOG(ERROR) << "dm device '" << dm_device << "' not found after polling timeout";
+        return false;
+    }
+
+    return true;
+}
+
+bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
+                                     Fstab::iterator* end) {
+    // Sets end to begin + 1, so we can just return on failure below.
+    if (end) {
+        *end = begin + 1;
+    }
+
+    if (begin->fs_mgr_flags.logical) {
+        if (!fs_mgr_update_logical_partition(&(*begin))) {
+            return false;
+        }
+        if (!InitMappedDevice(begin->blk_device)) {
+            return false;
+        }
+    }
+    if (!SetUpDmVerity(&(*begin))) {
+        PLOG(ERROR) << "Failed to setup verity for '" << begin->mount_point << "'";
+        return false;
+    }
+
+    bool mounted = (fs_mgr_do_mount_one(*begin) == 0);
+
+    // Try other mounts with the same mount point.
+    Fstab::iterator current = begin + 1;
+    for (; current != fstab_.end() && current->mount_point == begin->mount_point; current++) {
+        if (!mounted) {
+            // blk_device is already updated to /dev/dm-<N> by SetUpDmVerity() above.
+            // Copy it from the begin iterator.
+            current->blk_device = begin->blk_device;
+            mounted = (fs_mgr_do_mount_one(*current) == 0);
+        }
+    }
+    if (erase_same_mounts) {
+        current = fstab_.erase(begin, current);
+    }
+    if (end) {
+        *end = current;
+    }
+    return mounted;
+}
+
+// If system is in the fstab then we're not a system-as-root device, and in
+// this case, we mount system first then pivot to it.  From that point on,
+// we are effectively identical to a system-as-root device.
+bool FirstStageMount::TrySwitchSystemAsRoot() {
+    auto metadata_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
+        return entry.mount_point == "/metadata";
+    });
+    if (metadata_partition != fstab_.end()) {
+        if (MountPartition(metadata_partition, true /* erase_same_mounts */)) {
+            UseGsiIfPresent();
+        }
+    }
+
+    auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
+        return entry.mount_point == "/system";
+    });
+
+    if (system_partition == fstab_.end()) return true;
+
+    if (MountPartition(system_partition, false /* erase_same_mounts */)) {
+        if (gsi_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {
+            LOG(ERROR) << "check_most_at_once forbidden on external media";
+            return false;
+        }
+        SwitchRoot("/system");
+    } else {
+        PLOG(ERROR) << "Failed to mount /system";
+        return false;
+    }
+
+    return true;
+}
+
+bool FirstStageMount::MountPartitions() {
+    if (!TrySwitchSystemAsRoot()) return false;
+
+    if (!SkipMountingPartitions(&fstab_)) return false;
+
+    for (auto current = fstab_.begin(); current != fstab_.end();) {
+        // We've already mounted /system above.
+        if (current->mount_point == "/system") {
+            ++current;
+            continue;
+        }
+
+        Fstab::iterator end;
+        if (!MountPartition(current, false /* erase_same_mounts */, &end)) {
+            if (current->fs_mgr_flags.no_fail) {
+                LOG(INFO) << "Failed to mount " << current->mount_point
+                          << ", ignoring mount for no_fail partition";
+            } else if (current->fs_mgr_flags.formattable) {
+                LOG(INFO) << "Failed to mount " << current->mount_point
+                          << ", ignoring mount for formattable partition";
+            } else {
+                PLOG(ERROR) << "Failed to mount " << current->mount_point;
+                return false;
+            }
+        }
+        current = end;
+    }
+
+    // If we don't see /system or / in the fstab, then we need to create an root entry for
+    // overlayfs.
+    if (!GetEntryForMountPoint(&fstab_, "/system") && !GetEntryForMountPoint(&fstab_, "/")) {
+        FstabEntry root_entry;
+        if (GetRootEntry(&root_entry)) {
+            fstab_.emplace_back(std::move(root_entry));
+        }
+    }
+
+    // heads up for instantiating required device(s) for overlayfs logic
+    const auto devices = fs_mgr_overlayfs_required_devices(&fstab_);
+    for (auto const& device : devices) {
+        if (android::base::StartsWith(device, "/dev/block/by-name/")) {
+            required_devices_partition_names_.emplace(basename(device.c_str()));
+            auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
+            uevent_listener_.RegenerateUevents(uevent_callback);
+            if (!required_devices_partition_names_.empty()) {
+                uevent_listener_.Poll(uevent_callback, 10s);
+                if (!required_devices_partition_names_.empty()) {
+                    LOG(ERROR) << __PRETTY_FUNCTION__
+                               << ": partition(s) not found after polling timeout: "
+                               << android::base::Join(required_devices_partition_names_, ", ");
+                }
+            }
+        } else {
+            InitMappedDevice(device);
+        }
+    }
+
+    fs_mgr_overlayfs_mount_all(&fstab_);
+
+    return true;
+}
+
+void FirstStageMount::UseGsiIfPresent() {
+    std::string metadata_file, error;
+
+    if (!android::gsi::CanBootIntoGsi(&metadata_file, &error)) {
+        LOG(INFO) << "GSI " << error << ", proceeding with normal boot";
+        return;
+    }
+
+    auto metadata = android::fs_mgr::ReadFromImageFile(metadata_file.c_str());
+    if (!metadata) {
+        LOG(ERROR) << "GSI partition layout could not be read";
+        return;
+    }
+
+    if (!InitDmLinearBackingDevices(*metadata.get())) {
+        return;
+    }
+
+    // Find the name of the super partition for the GSI. It will either be
+    // "userdata", or a block device such as an sdcard. There are no by-name
+    // partitions other than userdata that we support installing GSIs to.
+    auto super = GetMetadataSuperBlockDevice(*metadata.get());
+    std::string super_name = android::fs_mgr::GetBlockDevicePartitionName(*super);
+    std::string super_path;
+    if (super_name == "userdata") {
+        super_path = "/dev/block/by-name/" + super_name;
+    } else {
+        super_path = "/dev/block/" + super_name;
+    }
+
+    if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path)) {
+        LOG(ERROR) << "GSI partition layout could not be instantiated";
+        return;
+    }
+
+    if (!android::gsi::MarkSystemAsGsi()) {
+        PLOG(ERROR) << "GSI indicator file could not be written";
+        return;
+    }
+
+    // Replace the existing system fstab entry.
+    auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
+        return entry.mount_point == "/system";
+    });
+    if (system_partition != fstab_.end()) {
+        fstab_.erase(system_partition);
+    }
+    fstab_.emplace_back(BuildGsiSystemFstabEntry());
+    gsi_not_on_userdata_ = (super_name != "userdata");
+}
+
+bool FirstStageMountVBootV1::GetDmVerityDevices() {
+    need_dm_verity_ = false;
+
+    for (const auto& fstab_entry : fstab_) {
+        // Don't allow verifyatboot in the first stage.
+        if (fstab_entry.fs_mgr_flags.verify_at_boot) {
+            LOG(ERROR) << "Partitions can't be verified at boot";
+            return false;
+        }
+        // Checks for verified partitions.
+        if (fstab_entry.fs_mgr_flags.verify) {
+            need_dm_verity_ = true;
+        }
+    }
+
+    // Includes the partition names of fstab records.
+    // Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
+    for (const auto& fstab_entry : fstab_) {
+        if (!fstab_entry.fs_mgr_flags.logical) {
+            required_devices_partition_names_.emplace(basename(fstab_entry.blk_device.c_str()));
+        }
+    }
+
+    return true;
+}
+
+bool FirstStageMountVBootV1::SetUpDmVerity(FstabEntry* fstab_entry) {
+    if (fstab_entry->fs_mgr_flags.verify) {
+        int ret = fs_mgr_setup_verity(fstab_entry, 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_entry->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_entry->blk_device);
+            default:
+                return false;
+        }
+    }
+    return true;  // Returns true to mount the partition.
+}
+
+// First retrieve any vbmeta partitions from device tree (legacy) then read through the fstab
+// for any further vbmeta partitions.
+FirstStageMountVBootV2::FirstStageMountVBootV2(Fstab fstab)
+    : FirstStageMount(std::move(fstab)), avb_handle_(nullptr) {
+    std::string device_tree_vbmeta_parts;
+    read_android_dt_file("vbmeta/parts", &device_tree_vbmeta_parts);
+
+    for (auto&& partition : Split(device_tree_vbmeta_parts, ",")) {
+        if (!partition.empty()) {
+            vbmeta_partitions_.emplace_back(std::move(partition));
+        }
+    }
+
+    for (const auto& entry : fstab_) {
+        if (!entry.vbmeta_partition.empty()) {
+            vbmeta_partitions_.emplace_back(entry.vbmeta_partition);
+        }
+    }
+
+    if (vbmeta_partitions_.empty()) {
+        LOG(ERROR) << "Failed to read vbmeta partitions.";
+    }
+}
+
+bool FirstStageMountVBootV2::GetDmVerityDevices() {
+    need_dm_verity_ = false;
+
+    std::set<std::string> logical_partitions;
+
+    // fstab_rec->blk_device has A/B suffix.
+    for (const auto& fstab_entry : fstab_) {
+        if (fstab_entry.fs_mgr_flags.avb) {
+            need_dm_verity_ = true;
+        }
+        if (fstab_entry.fs_mgr_flags.logical) {
+            // Don't try to find logical partitions via uevent regeneration.
+            logical_partitions.emplace(basename(fstab_entry.blk_device.c_str()));
+        } else {
+            required_devices_partition_names_.emplace(basename(fstab_entry.blk_device.c_str()));
+        }
+    }
+
+    // Any partitions needed for verifying the partitions used in first stage mount, e.g. vbmeta
+    // must be provided as vbmeta_partitions.
+    if (need_dm_verity_) {
+        if (vbmeta_partitions_.empty()) {
+            LOG(ERROR) << "Missing vbmeta partitions";
+            return false;
+        }
+        std::string ab_suffix = fs_mgr_get_slot_suffix();
+        for (const auto& partition : vbmeta_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;
+}
+
+bool FirstStageMountVBootV2::SetUpDmVerity(FstabEntry* fstab_entry) {
+    AvbHashtreeResult hashtree_result;
+
+    // It's possible for a fstab_entry to have both avb_keys and avb flag.
+    // In this case, try avb_keys first, then fallback to avb flag.
+    if (!fstab_entry->avb_keys.empty()) {
+        if (!InitAvbHandle()) return false;
+        // Checks if hashtree should be disabled from the top-level /vbmeta.
+        if (avb_handle_->status() == AvbHandleStatus::kHashtreeDisabled ||
+            avb_handle_->status() == AvbHandleStatus::kVerificationDisabled) {
+            LOG(ERROR) << "Top-level vbmeta is disabled, skip Hashtree setup for "
+                       << fstab_entry->mount_point;
+            return true;  // Returns true to mount the partition directly.
+        } else {
+            auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(*fstab_entry);
+            if (!avb_standalone_handle) {
+                LOG(ERROR) << "Failed to load offline vbmeta for " << fstab_entry->mount_point;
+                // Fallbacks to built-in hashtree if fs_mgr_flags.avb is set.
+                if (!fstab_entry->fs_mgr_flags.avb) return false;
+                LOG(INFO) << "Fallback to built-in hashtree for " << fstab_entry->mount_point;
+                hashtree_result =
+                        avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
+            } else {
+                // Sets up hashtree via the standalone handle.
+                if (IsStandaloneImageRollback(*avb_handle_, *avb_standalone_handle, *fstab_entry)) {
+                    return false;
+                }
+                hashtree_result = avb_standalone_handle->SetUpAvbHashtree(
+                        fstab_entry, false /* wait_for_verity_dev */);
+            }
+        }
+    } else if (fstab_entry->fs_mgr_flags.avb) {
+        if (!InitAvbHandle()) return false;
+        hashtree_result =
+                avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
+    } else {
+        return true;  // No need AVB, returns true to mount the partition directly.
+    }
+
+    switch (hashtree_result) {
+        case AvbHashtreeResult::kDisabled:
+            return true;  // Returns true to mount the partition.
+        case AvbHashtreeResult::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_entry->blk_device);
+        default:
+            return false;
+    }
+}
+
+bool FirstStageMountVBootV2::InitAvbHandle() {
+    if (avb_handle_) return true;  // Returns true if the handle is already initialized.
+
+    avb_handle_ = AvbHandle::Open();
+
+    if (!avb_handle_) {
+        PLOG(ERROR) << "Failed to open AvbHandle";
+        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;
+    }
+
+    auto fstab = ReadFirstStageFstab();
+
+    if (!IsDtVbmetaCompatible(fstab)) {
+        LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not vbmeta compatible)";
+        return;
+    }
+
+    // Initializes required devices for the subsequent AvbHandle::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(std::move(fstab));
+    if (!avb_first_mount.InitDevices()) {
+        LOG(ERROR) << "Failed to init devices for INIT_AVB_VERSION";
+        return;
+    }
+
+    AvbUniquePtr avb_handle = AvbHandle::Open();
+    if (!avb_handle) {
+        PLOG(ERROR) << "Failed to open AvbHandle 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/grab-bootchart.sh b/init/grab-bootchart.sh
index c4ff6df..2c56698 100755
--- a/init/grab-bootchart.sh
+++ b/init/grab-bootchart.sh
@@ -17,6 +17,6 @@
     adb "${@}" pull $LOGROOT/$f $TMPDIR/$f 2>&1 > /dev/null
 done
 (cd $TMPDIR && tar -czf $TARBALL $FILES)
-bootchart ${TMPDIR}/${TARBALL}
-gnome-open ${TARBALL%.tgz}.png
+pybootchartgui ${TMPDIR}/${TARBALL}
+xdg-open ${TARBALL%.tgz}.png
 echo "Clean up ${TMPDIR}/ and ./${TARBALL%.tgz}.png when done"
diff --git a/init/host_import_parser.cpp b/init/host_import_parser.cpp
new file mode 100644
index 0000000..aa80199
--- /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<void> HostImportParser::ParseSection(std::vector<std::string>&& args, const std::string&,
+                                            int) {
+    if (args.size() != 2) {
+        return Error() << "single argument needed for import\n";
+    }
+
+    return {};
+}
+
+Result<void> 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..d6f7286
--- /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<void> ParseSection(std::vector<std::string>&& args, const std::string&, int) override;
+    Result<void> ParseLineSection(std::vector<std::string>&&, int) override;
+};
+
+}  // namespace init
+}  // namespace android
diff --git a/init/host_init_parser.cpp b/init/host_init_parser.cpp
deleted file mode 100644
index 5232b7e..0000000
--- a/init/host_init_parser.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include <pwd.h>
-
-#include <android-base/logging.h>
-
-#include "action.h"
-#include "action_manager.h"
-#include "action_parser.h"
-#include "parser.h"
-#include "result.h"
-#include "service.h"
-
-// The host passwd file won't have the Android entries, so we fake success here.
-passwd* getpwnam(const char* login) {  // NOLINT: implementing bad function.
-    char dummy_buf[] = "dummy";
-    static passwd dummy_passwd = {
-        .pw_name = dummy_buf,
-        .pw_dir = dummy_buf,
-        .pw_shell = dummy_buf,
-        .pw_uid = 123,
-        .pw_gid = 123,
-    };
-    return &dummy_passwd;
-}
-
-namespace android {
-namespace init {
-
-static Result<Success> do_stub(const BuiltinArguments& args) {
-    return Success();
-}
-
-#include "generated_stub_builtin_function_map.h"
-
-int main(int argc, char** argv) {
-    android::base::InitLogging(argv, &android::base::StderrLogger);
-    if (argc != 2) {
-        LOG(ERROR) << "Usage: " << argv[0] << " <init file to parse>";
-        return -1;
-    }
-    const BuiltinFunctionMap function_map;
-    Action::set_function_map(&function_map);
-    ActionManager& am = ActionManager::GetInstance();
-    ServiceList& sl = ServiceList::GetInstance();
-    Parser parser;
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
-    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
-
-    size_t num_errors = 0;
-    if (!parser.ParseConfig(argv[1], &num_errors)) {
-        LOG(ERROR) << "Failed to find script";
-        return -1;
-    }
-    if (num_errors > 0) {
-        LOG(ERROR) << "Parse failed with " << num_errors << " errors";
-        return -1;
-    }
-    LOG(INFO) << "Parse success!";
-    return 0;
-}
-
-}  // namespace init
-}  // namespace android
-
-int main(int argc, char** argv) {
-    android::init::main(argc, argv);
-}
diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp
deleted file mode 100644
index 4451ac8..0000000
--- a/init/host_init_stubs.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "host_init_stubs.h"
-
-// unistd.h
-int setgroups(size_t __size, const gid_t* __list) {
-    return 0;
-}
-
-namespace android {
-namespace base {
-
-std::string GetProperty(const std::string&, const std::string& default_value) {
-    return default_value;
-}
-
-bool GetBoolProperty(const std::string&, bool default_value) {
-    return default_value;
-}
-
-}  // namespace base
-}  // namespace android
-
-namespace android {
-namespace init {
-
-// init.h
-std::string default_console = "/dev/console";
-
-// property_service.h
-uint32_t (*property_set)(const std::string& name, const std::string& value) = nullptr;
-uint32_t HandlePropertySet(const std::string&, const std::string&, const std::string&, const ucred&,
-                           std::string*) {
-    return 0;
-}
-
-// selinux.h
-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
index ad48602..7c0544a 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_HOST_INIT_STUBS_H
-#define _INIT_HOST_INIT_STUBS_H
+#pragma once
 
 #include <stddef.h>
 #include <sys/socket.h>
@@ -23,44 +22,47 @@
 
 #include <string>
 
+#include <android-base/properties.h>
+
+// android/api-level.h
+#define __ANDROID_API_P__ 28
+
 // sys/system_properties.h
 #define PROP_VALUE_MAX 92
 
-// unistd.h
-int setgroups(size_t __size, const gid_t* __list);
-
-// android-base/properties.h
-namespace android {
-namespace base {
-
-std::string GetProperty(const std::string& key, const std::string& default_value);
-bool GetBoolProperty(const std::string& key, bool default_value);
-template <typename T>
-T GetIntProperty(const std::string&, T default_value, T = std::numeric_limits<T>::min(),
-                 T = std::numeric_limits<T>::max()) {
-    return default_value;
-}
-
-}  // namespace base
-}  // namespace android
-
 namespace android {
 namespace init {
 
-// init.h
-extern std::string default_console;
-
 // property_service.h
-extern uint32_t (*property_set)(const std::string& name, const std::string& value);
-uint32_t HandlePropertySet(const std::string& name, const std::string& value,
-                           const std::string& source_context, const ucred& cr, std::string* error);
+inline bool CanReadProperty(const std::string&, const std::string&) {
+    return true;
+}
+inline uint32_t SetProperty(const std::string& key, const std::string& value) {
+    android::base::SetProperty(key, value);
+    return 0;
+}
+inline uint32_t (*property_set)(const std::string& name, const std::string& value) = SetProperty;
+inline uint32_t HandlePropertySet(const std::string&, const std::string&, const std::string&,
+                                  const ucred&, std::string*) {
+    return 0;
+}
+
+// reboot_utils.h
+inline void SetFatalRebootTarget() {}
+inline void __attribute__((noreturn)) InitFatalReboot() {
+    abort();
+}
+
+// selabel.h
+inline void SelabelInitialize() {}
+inline bool SelabelLookupFileContext(const std::string&, int, std::string*) {
+    return false;
+}
 
 // selinux.h
-bool SelinuxHasVendorInit();
-void SelabelInitialize();
-bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
+inline int SelinuxGetVendorAndroidVersion() {
+    return 10000;
+}
 
 }  // 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..8aa3509
--- /dev/null
+++ b/init/host_init_verifier.cpp
@@ -0,0 +1,238 @@
+//
+// 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 <getopt.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <iostream>
+#include <iterator>
+#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"
+#include "service_list.h"
+#include "service_parser.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::vector<std::string> passwd_files;
+
+static std::vector<std::pair<std::string, int>> GetVendorPasswd(const std::string& passwd_file) {
+    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;
+}
+
+static std::vector<std::pair<std::string, int>> GetVendorPasswd() {
+    std::vector<std::pair<std::string, int>> result;
+    for (const auto& passwd_file : passwd_files) {
+        auto individual_result = GetVendorPasswd(passwd_file);
+        std::move(individual_result.begin(), individual_result.end(),
+                  std::back_insert_iterator(result));
+    }
+    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;
+}
+
+static std::optional<std::set<std::string>> ReadKnownInterfaces(
+        const std::string& known_interfaces_file) {
+    if (known_interfaces_file.empty()) {
+        LOG(WARNING) << "Missing a known interfaces file.";
+        return {};
+    }
+
+    std::string known_interfaces;
+    if (!ReadFileToString(known_interfaces_file, &known_interfaces)) {
+        LOG(ERROR) << "Failed to read known interfaces file '" << known_interfaces_file << "'";
+        return {};
+    }
+
+    auto interfaces = Split(known_interfaces, " ");
+    return std::set<std::string>(interfaces.begin(), interfaces.end());
+}
+
+namespace android {
+namespace init {
+
+static Result<void> do_stub(const BuiltinArguments& args) {
+    return {};
+}
+
+#include "generated_stub_builtin_function_map.h"
+
+void PrintUsage() {
+    std::cout << "usage: host_init_verifier [-p FILE] -k FILE <init rc file>\n"
+                 "\n"
+                 "Tests an init script for correctness\n"
+                 "\n"
+                 "-p FILE\tSearch this passwd file for users and groups\n"
+                 "-k FILE\tUse this file as a space-separated list of known interfaces\n"
+              << std::endl;
+}
+
+int main(int argc, char** argv) {
+    android::base::InitLogging(argv, &android::base::StdioLogger);
+    android::base::SetMinimumLogSeverity(android::base::ERROR);
+
+    std::string known_interfaces_file;
+
+    while (true) {
+        static const struct option long_options[] = {
+                {"help", no_argument, nullptr, 'h'},
+                {nullptr, 0, nullptr, 0},
+        };
+
+        int arg = getopt_long(argc, argv, "p:k:", long_options, nullptr);
+
+        if (arg == -1) {
+            break;
+        }
+
+        switch (arg) {
+            case 'h':
+                PrintUsage();
+                return EXIT_FAILURE;
+            case 'p':
+                passwd_files.emplace_back(optarg);
+                break;
+            case 'k':
+                known_interfaces_file = optarg;
+                break;
+            default:
+                std::cerr << "getprop: getopt returned invalid result: " << arg << std::endl;
+                return EXIT_FAILURE;
+        }
+    }
+
+    argc -= optind;
+    argv += optind;
+
+    if (argc != 1) {
+        PrintUsage();
+        return EXIT_FAILURE;
+    }
+
+    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,
+                                                       ReadKnownInterfaces(known_interfaces_file)));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
+    parser.AddSectionParser("import", std::make_unique<HostImportParser>());
+
+    if (!parser.ParseConfigFileInsecure(*argv)) {
+        LOG(ERROR) << "Failed to open init rc script '" << *argv << "'";
+        return EXIT_FAILURE;
+    }
+    if (parser.parse_error_count() > 0) {
+        LOG(ERROR) << "Failed to parse init script '" << *argv << "' 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 e335fd1..c72b7d6 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -23,8 +23,8 @@
 namespace android {
 namespace init {
 
-Result<Success> ImportParser::ParseSection(std::vector<std::string>&& args,
-                                           const std::string& filename, int line) {
+Result<void> ImportParser::ParseSection(std::vector<std::string>&& args,
+                                        const std::string& filename, int line) {
     if (args.size() != 2) {
         return Error() << "single argument needed for import\n";
     }
@@ -38,17 +38,18 @@
     LOG(INFO) << "Added '" << conf_file << "' to import list";
     if (filename_.empty()) filename_ = filename;
     imports_.emplace_back(std::move(conf_file), line);
-    return Success();
+    return {};
+}
+
+Result<void> 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 5a2f894..5bf9c6c 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -28,8 +28,9 @@
 class ImportParser : public SectionParser {
   public:
     ImportParser(Parser* parser) : parser_(parser) {}
-    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                                 int line) override;
+    Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                              int line) override;
+    Result<void> ParseLineSection(std::vector<std::string>&&, int) override;
     void EndFile() override;
 
   private:
diff --git a/init/init.cpp b/init/init.cpp
index fc58eea..675f3e5 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -18,49 +18,64 @@
 
 #include <dirent.h>
 #include <fcntl.h>
-#include <paths.h>
 #include <pthread.h>
 #include <seccomp_policy.h>
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/epoll.h>
 #include <sys/mount.h>
 #include <sys/signalfd.h>
-#include <sys/sysmacros.h>
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <functional>
+#include <map>
+#include <memory>
+#include <optional>
+#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/stringprintf.h>
 #include <android-base/strings.h>
-#include <cutils/android_reboot.h>
+#include <fs_avb/fs_avb.h>
+#include <fs_mgr_vendor_overlay.h>
 #include <keyutils.h>
 #include <libavb/libavb.h>
-#include <private/android_filesystem_config.h>
+#include <libgsi/libgsi.h>
+#include <processgroup/processgroup.h>
+#include <processgroup/setup.h>
 #include <selinux/android.h>
 
-#include <memory>
-#include <optional>
+#ifndef RECOVERY
+#include <binder/ProcessState.h>
+#endif
 
 #include "action_parser.h"
 #include "boringssl_self_test.h"
+#include "builtins.h"
+#include "epoll.h"
+#include "first_stage_init.h"
+#include "first_stage_mount.h"
 #include "import_parser.h"
-#include "init_first_stage.h"
 #include "keychords.h"
-#include "log.h"
+#include "mount_handler.h"
+#include "mount_namespace.h"
 #include "property_service.h"
 #include "reboot.h"
+#include "reboot_utils.h"
 #include "security.h"
+#include "selabel.h"
 #include "selinux.h"
+#include "service.h"
+#include "service_parser.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;
@@ -69,6 +84,7 @@
 using android::base::StringPrintf;
 using android::base::Timer;
 using android::base::Trim;
+using android::fs_mgr::AvbHandle;
 
 namespace android {
 namespace init {
@@ -77,10 +93,7 @@
 
 static char qemu[32];
 
-std::string default_console = "/dev/console";
-
-static int epoll_fd = -1;
-static int sigterm_signal_fd = -1;
+static int signal_fd = -1;
 
 static std::unique_ptr<Timer> waiting_for_prop(nullptr);
 static std::string wait_prop_name;
@@ -88,8 +101,7 @@
 static bool shutting_down;
 static std::string shutdown_command;
 static bool do_shutdown = false;
-
-std::vector<std::string> late_import_paths;
+static bool load_debug_prop = false;
 
 static std::vector<Subcontext>* subcontexts;
 
@@ -101,13 +113,23 @@
 Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
     Parser parser;
 
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
+    parser.AddSectionParser(
+            "service", std::make_unique<ServiceParser>(&service_list, subcontexts, std::nullopt));
     parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
     return parser;
 }
 
+// parser that only accepts new services
+Parser CreateServiceOnlyParser(ServiceList& service_list) {
+    Parser parser;
+
+    parser.AddSectionParser(
+            "service", std::make_unique<ServiceParser>(&service_list, subcontexts, std::nullopt));
+    return parser;
+}
+
 static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
     Parser parser = CreateParser(action_manager, service_list);
 
@@ -120,6 +142,9 @@
         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");
         }
@@ -131,15 +156,6 @@
     }
 }
 
-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";
-    }
-}
-
 bool start_waiting_for_property(const char *name, const char *value)
 {
     if (waiting_for_prop) {
@@ -185,45 +201,65 @@
 
     if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
 
+    // We always record how long init waited for ueventd to tell us cold boot finished.
+    // If we aren't waiting on this property, it means that ueventd finished before we even started
+    // to wait.
+    if (name == kColdBootDoneProp) {
+        auto time_waited = waiting_for_prop ? waiting_for_prop->duration().count() : 0;
+        property_set("ro.boottime.init.cold_boot_wait", std::to_string(time_waited));
+    }
+
     if (waiting_for_prop) {
         if (wait_prop_name == name && wait_prop_value == value) {
-            LOG(INFO) << "Wait for property took " << *waiting_for_prop;
+            LOG(INFO) << "Wait for property '" << wait_prop_name << "=" << wait_prop_value
+                      << "' took " << *waiting_for_prop;
             ResetWaitForProp();
         }
     }
 }
 
-static std::optional<boot_clock::time_point> RestartProcesses() {
-    std::optional<boot_clock::time_point> next_process_restart_time;
+static std::optional<boot_clock::time_point> HandleProcessActions() {
+    std::optional<boot_clock::time_point> next_process_action_time;
     for (const auto& s : ServiceList::GetInstance()) {
+        if ((s->flags() & SVC_RUNNING) && s->timeout_period()) {
+            auto timeout_time = s->time_started() + *s->timeout_period();
+            if (boot_clock::now() > timeout_time) {
+                s->Timeout();
+            } else {
+                if (!next_process_action_time || timeout_time < *next_process_action_time) {
+                    next_process_action_time = timeout_time;
+                }
+            }
+        }
+
         if (!(s->flags() & SVC_RESTARTING)) continue;
 
-        auto restart_time = s->time_started() + 5s;
+        auto restart_time = s->time_started() + s->restart_period();
         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;
+            if (!next_process_action_time || restart_time < *next_process_action_time) {
+                next_process_action_time = restart_time;
             }
         }
     }
-    return next_process_restart_time;
+    return next_process_action_time;
 }
 
-static Result<Success> DoControlStart(Service* service) {
+static Result<void> DoControlStart(Service* service) {
     return service->Start();
 }
 
-static Result<Success> DoControlStop(Service* service) {
+static Result<void> DoControlStop(Service* service) {
     service->Stop();
-    return Success();
+    return {};
 }
 
-static Result<Success> DoControlRestart(Service* service) {
+static Result<void> DoControlRestart(Service* service) {
     service->Restart();
-    return Success();
+    return {};
 }
 
 enum class ControlTarget {
@@ -233,12 +269,16 @@
 
 struct ControlMessageFunction {
     ControlTarget target;
-    std::function<Result<Success>(Service*)> action;
+    std::function<Result<void>(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 Result<void>{}; }}},
+        {"sigstop_off",       {ControlTarget::SERVICE,
+                               [](auto* service) { service->set_sigstop(false); return Result<void>{}; }}},
         {"start",             {ControlTarget::SERVICE,   DoControlStart}},
         {"stop",              {ControlTarget::SERVICE,   DoControlStop}},
         {"restart",           {ControlTarget::SERVICE,   DoControlRestart}},
@@ -251,13 +291,13 @@
     return control_message_functions;
 }
 
-void HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid) {
+bool 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;
+        return false;
     }
 
     std::string cmdline_path = StringPrintf("proc/%d/cmdline", pid);
@@ -274,74 +314,50 @@
 
     const ControlMessageFunction& function = it->second;
 
-    if (function.target == ControlTarget::SERVICE) {
-        Service* svc = ServiceList::GetInstance().FindService(name);
-        if (svc == nullptr) {
-            LOG(ERROR) << "No such service '" << name << "' for ctl." << msg;
-            return;
-        }
-        if (auto result = function.action(svc); !result) {
-            LOG(ERROR) << "Could not ctl." << msg << " for service " << name << ": "
-                       << result.error();
-        }
+    Service* svc = nullptr;
 
-        return;
+    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 false;
     }
 
-    if (function.target == ControlTarget::INTERFACE) {
-        for (const auto& svc : ServiceList::GetInstance()) {
-            if (svc->interfaces().count(name) == 0) {
-                continue;
-            }
-
-            if (auto result = function.action(svc.get()); !result) {
-                LOG(ERROR) << "Could not handle ctl." << msg << " for service " << svc->name()
-                           << " with interface " << name << ": " << result.error();
-            }
-
-            return;
-        }
-
-        LOG(ERROR) << "Could not find service hosting interface " << name;
-        return;
+    if (svc == nullptr) {
+        LOG(ERROR) << "Could not find '" << name << "' for ctl." << msg;
+        return false;
     }
 
-    LOG(ERROR) << "Invalid function target from static map key '" << msg
-               << "': " << static_cast<std::underlying_type<ControlTarget>::type>(function.target);
+    if (auto result = function.action(svc); !result) {
+        LOG(ERROR) << "Could not ctl." << msg << " for '" << name << "': " << result.error();
+        return false;
+    }
+    return true;
 }
 
-static Result<Success> wait_for_coldboot_done_action(const BuiltinArguments& args) {
-    Timer t;
-
-    LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE "...";
-
-    // Historically we had a 1s timeout here because we weren't otherwise
-    // tracking boot time, and many OEMs made their sepolicy regular
-    // expressions too expensive (http://b/19899875).
-
-    // Now we're tracking boot time, just log the time taken to a system
-    // property. We still panic if it takes more than a minute though,
-    // 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(FATAL) << "Timed out waiting for " COLDBOOT_DONE;
+static Result<void> wait_for_coldboot_done_action(const BuiltinArguments& args) {
+    if (!start_waiting_for_property(kColdBootDoneProp, "true")) {
+        LOG(FATAL) << "Could not wait for '" << kColdBootDoneProp << "'";
     }
 
-    property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration().count()));
-    return Success();
+    return {};
 }
 
-static Result<Success> keychord_init_action(const BuiltinArguments& args) {
-    keychord_init();
-    return Success();
-}
-
-static Result<Success> console_init_action(const BuiltinArguments& args) {
-    std::string console = GetProperty("ro.boot.console", "");
-    if (!console.empty()) {
-        default_console = "/dev/" + console;
+static Result<void> SetupCgroupsAction(const BuiltinArguments&) {
+    // Have to create <CGROUPS_RC_DIR> using make_dir function
+    // for appropriate sepolicy to be set for it
+    make_dir(android::base::Dirname(CGROUPS_RC_PATH), 0711);
+    if (!CgroupSetup()) {
+        return ErrnoError() << "Failed to setup cgroups";
     }
-    return Success();
+
+    return {};
 }
 
 static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
@@ -364,30 +380,32 @@
     if (!android::base::GetBoolProperty("ro.oem_unlock_supported", false)) {
         return;
     }
-
-    std::string value = GetProperty("ro.boot.verifiedbootstate", "");
-
-    if (!value.empty()) {
-        property_set("ro.boot.flash.locked", value == "orange" ? "0" : "1");
-    }
+    import_kernel_cmdline(
+            false, [](const std::string& key, const std::string& value, bool in_qemu) {
+                if (key == "androidboot.verifiedbootstate") {
+                    property_set("ro.boot.flash.locked", value == "orange" ? "0" : "1");
+                }
+            });
 }
 
 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);
     }
 }
 
@@ -423,24 +441,34 @@
     if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
 }
 
-static Result<Success> property_enable_triggers_action(const BuiltinArguments& args) {
+static Result<void> property_enable_triggers_action(const BuiltinArguments& args) {
     /* Enable property triggers. */
     property_triggers_enabled = 1;
-    return Success();
+    return {};
 }
 
-static Result<Success> queue_property_triggers_action(const BuiltinArguments& args) {
+static Result<void> queue_property_triggers_action(const BuiltinArguments& args) {
     ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, "enable_property_trigger");
     ActionManager::GetInstance().QueueAllPropertyActions();
-    return Success();
+    return {};
 }
 
-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!";
-        }
-    });
+static Result<void> InitBinder(const BuiltinArguments& args) {
+    // init's use of binder is very limited. init cannot:
+    //   - have any binder threads
+    //   - receive incoming binder calls
+    //   - pass local binder services to remote processes
+    //   - use death recipients
+    // The main supported usecases are:
+    //   - notifying other daemons (oneway calls only)
+    //   - retrieving data that is necessary to boot
+    // Also, binder can't be used by recovery.
+#ifndef RECOVERY
+    android::ProcessState::self()->setThreadPoolMaxThreadCount(0);
+    android::ProcessState::self()->setCallRestriction(
+            ProcessState::CallRestriction::ERROR_IF_NOT_ONEWAY);
+#endif
+    return {};
 }
 
 // Set the UDC controller for the ConfigFS USB Gadgets.
@@ -459,48 +487,7 @@
     }
 }
 
-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
-    // recover.
-    struct sigaction action;
-    memset(&action, 0, sizeof(action));
-    sigfillset(&action.sa_mask);
-    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);
-    sigaction(SIGBUS, &action, nullptr);
-    sigaction(SIGFPE, &action, nullptr);
-    sigaction(SIGILL, &action, nullptr);
-    sigaction(SIGSEGV, &action, nullptr);
-#if defined(SIGSTKFLT)
-    sigaction(SIGSTKFLT, &action, nullptr);
-#endif
-    sigaction(SIGSYS, &action, nullptr);
-    sigaction(SIGTRAP, &action, nullptr);
-}
-
-static void HandleSigtermSignal() {
-    signalfd_siginfo siginfo;
-    ssize_t bytes_read = TEMP_FAILURE_RETRY(read(sigterm_signal_fd, &siginfo, sizeof(siginfo)));
-    if (bytes_read != sizeof(siginfo)) {
-        PLOG(ERROR) << "Failed to read siginfo from sigterm_signal_fd";
-        return;
-    }
-
+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;
@@ -510,144 +497,163 @@
     HandlePowerctlMessage("shutdown,container");
 }
 
-static void UnblockSigterm() {
-    sigset_t mask;
-    sigemptyset(&mask);
-    sigaddset(&mask, SIGTERM);
+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;
+    }
 
-    if (sigprocmask(SIG_UNBLOCK, &mask, nullptr) == -1) {
-        PLOG(FATAL) << "failed to unblock SIGTERM for PID " << getpid();
+    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 InstallSigtermHandler() {
+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_BLOCK, &mask, nullptr) == -1) {
-        PLOG(FATAL) << "failed to block 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);
     }
 
-    // Register a handler to unblock SIGTERM in the child processes.
-    const int result = pthread_atfork(nullptr, nullptr, &UnblockSigterm);
+    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);
     }
 
-    sigterm_signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
-    if (sigterm_signal_fd == -1) {
-        PLOG(FATAL) << "failed to create signalfd for SIGTERM";
+    signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
+    if (signal_fd == -1) {
+        PLOG(FATAL) << "failed to create signalfd";
     }
 
-    register_epoll_handler(sigterm_signal_fd, HandleSigtermSignal);
+    if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd); !result) {
+        LOG(FATAL) << result.error();
+    }
 }
 
-int main(int argc, char** argv) {
-    if (!strcmp(basename(argv[0]), "ueventd")) {
-        return ueventd_main(argc, argv);
+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;
     }
 
-    if (!strcmp(basename(argv[0]), "watchdogd")) {
-        return watchdogd_main(argc, argv);
+    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 (argc > 1 && !strcmp(argv[1], "subcontext")) {
-        InitKernelLogging(argv);
-        const BuiltinFunctionMap function_map;
-        return SubcontextMain(argc, argv, &function_map);
+    if (!found) {
+        LOG(ERROR) << "Service for keychord " << android::base::Join(keycodes, ' ') << " not found";
     }
+}
 
+static void GlobalSeccomp() {
+    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!";
+        }
+    });
+}
+
+static void UmountDebugRamdisk() {
+    if (umount("/debug_ramdisk") != 0) {
+        LOG(ERROR) << "Failed to umount /debug_ramdisk";
+    }
+}
+
+static void RecordStageBoottimes(const boot_clock::time_point& second_stage_start_time) {
+    int64_t first_stage_start_time_ns = -1;
+    if (auto first_stage_start_time_str = getenv(kEnvFirstStageStartedAt);
+        first_stage_start_time_str) {
+        property_set("ro.boottime.init", first_stage_start_time_str);
+        android::base::ParseInt(first_stage_start_time_str, &first_stage_start_time_ns);
+    }
+    unsetenv(kEnvFirstStageStartedAt);
+
+    int64_t selinux_start_time_ns = -1;
+    if (auto selinux_start_time_str = getenv(kEnvSelinuxStartedAt); selinux_start_time_str) {
+        android::base::ParseInt(selinux_start_time_str, &selinux_start_time_ns);
+    }
+    unsetenv(kEnvSelinuxStartedAt);
+
+    if (selinux_start_time_ns == -1) return;
+    if (first_stage_start_time_ns == -1) return;
+
+    property_set("ro.boottime.init.first_stage",
+                 std::to_string(selinux_start_time_ns - first_stage_start_time_ns));
+    property_set("ro.boottime.init.selinux",
+                 std::to_string(second_stage_start_time.time_since_epoch().count() -
+                                selinux_start_time_ns));
+}
+
+int SecondStageMain(int argc, char** argv) {
     if (REBOOT_BOOTLOADER_ON_PANIC) {
         InstallRebootSignalHandlers();
     }
 
-    bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
+    boot_clock::time_point start_time = boot_clock::now();
 
-    if (is_first_stage) {
-        boot_clock::time_point start_time = boot_clock::now();
-
-        // Clear the umask.
-        umask(0);
-
-        clearenv();
-        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));
-        // Don't expose the raw commandline to unprivileged processes.
-        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);
-
-        mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
-
-        if constexpr (WORLD_WRITABLE_KMSG) {
-            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));
-
-        // Mount staging areas for devices managed by vold
-        // See storage config details at http://source.android.com/devices/storage/
-        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.
-        mkdir("/mnt/vendor", 0755);
-
-        // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
-        // talk to the outside world...
-        InitKernelLogging(argv);
-
-        LOG(INFO) << "init first stage started!";
-
-        if (!DoFirstStageMount()) {
-            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.
-        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(FATAL) << "restorecon failed of /init failed";
-        }
-
-        setenv("INIT_SECOND_STAGE", "true", 1);
-
-        static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
-        uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
-        setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
-
-        char* path = argv[0];
-        char* args[] = { path, nullptr };
-        execv(path, args);
-
-        // execv() only returns if an error happened, in which case we
-        // panic and never fall through this conditional.
-        PLOG(FATAL) << "execv(\"" << path << "\") failed";
-    }
-
-    // At this point we're in the second stage of init.
+    SetStdioToDevNull(argv);
     InitKernelLogging(argv);
     LOG(INFO) << "init second stage started!";
 
+    // Set init and its forked children's oom_adj.
+    if (auto result = WriteFile("/proc/1/oom_score_adj", "-1000"); !result) {
+        LOG(ERROR) << "Unable to write -1000 to /proc/1/oom_score_adj: " << result.error();
+    }
+
+    // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
+    GlobalSeccomp();
+
     // Set up a session keyring that all processes will have access to. It
     // will hold things like FBE encryption keys. No process should override
     // its session keyring.
@@ -667,46 +673,50 @@
     // used by init as well as the current required properties.
     export_kernel_boot_props();
 
-    // Make the time that init started available for bootstat to log.
-    property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
-    property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
+    // Make the time that init stages started available for bootstat to log.
+    RecordStageBoottimes(start_time);
 
     // Set libavb version for Framework-only OTA match in Treble build.
     const char* avb_version = getenv("INIT_AVB_VERSION");
     if (avb_version) property_set("ro.boot.avb_version", avb_version);
 
+    // See if need to load debug props to allow adb root, when the device is unlocked.
+    const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
+    if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
+        load_debug_prop = "true"s == force_debuggable_env;
+    }
+
     // Clean up our environment.
-    unsetenv("INIT_SECOND_STAGE");
-    unsetenv("INIT_STARTED_AT");
-    unsetenv("INIT_SELINUX_TOOK");
     unsetenv("INIT_AVB_VERSION");
+    unsetenv("INIT_FORCE_DEBUGGABLE");
 
     // Now set up SELinux for second stage.
     SelinuxSetupKernelLogging();
     SelabelInitialize();
     SelinuxRestoreContext();
 
-    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
-    if (epoll_fd == -1) {
-        PLOG(FATAL) << "epoll_create1 failed";
+    Epoll epoll;
+    if (auto result = epoll.Open(); !result) {
+        PLOG(FATAL) << result.error();
     }
 
-    sigchld_handler_init();
+    InstallSignalFdHandler(&epoll);
 
-    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.
-        InstallSigtermHandler();
-    }
-
-    property_load_boot_defaults();
+    property_load_boot_defaults(load_debug_prop);
+    UmountDebugRamdisk();
+    fs_mgr_vendor_overlay_mount_all();
     export_oem_lock_status();
-    start_property_service();
+    StartPropertyService(&epoll);
+    MountHandler mount_handler(&epoll);
     set_usb_controller();
 
     const BuiltinFunctionMap function_map;
     Action::set_function_map(&function_map);
 
+    if (!SetupMountNamespaces()) {
+        PLOG(FATAL) << "SetupMountNamespaces failed";
+    }
+
     subcontexts = InitializeSubcontexts();
 
     ActionManager& am = ActionManager::GetInstance();
@@ -718,6 +728,15 @@
     // Nexus 9 boot time, so it's disabled by default.
     if (false) DumpState();
 
+    // Make the GSI status available before scripts start running.
+    if (android::gsi::IsGsiRunning()) {
+        property_set("ro.gsid.image_running", "1");
+    } else {
+        property_set("ro.gsid.image_running", "0");
+    }
+
+    am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
+
     am.QueueEventTrigger("early-init");
 
     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
@@ -726,8 +745,16 @@
     am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
     am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
     am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
-    am.QueueBuiltinAction(keychord_init_action, "keychord_init");
-    am.QueueBuiltinAction(console_init_action, "console_init");
+    Keychords keychords;
+    am.QueueBuiltinAction(
+            [&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {
+                for (const auto& svc : ServiceList::GetInstance()) {
+                    keychords.Register(svc->keycodes());
+                }
+                keychords.Start(&epoll, HandleKeychord);
+                return {};
+            },
+            "KeychordInit");
 
     // Trigger all the boot actions to get us started.
     am.QueueEventTrigger("init");
@@ -739,6 +766,9 @@
     // wasn't ready immediately after wait_for_coldboot_done
     am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
 
+    // Initialize binder before bringing up other system services
+    am.QueueBuiltinAction(InitBinder, "InitBinder");
+
     // Don't mount filesystems or start core system services in charger mode.
     std::string bootmode = GetProperty("ro.bootmode", "");
     if (bootmode == "charger") {
@@ -752,7 +782,7 @@
 
     while (true) {
         // By default, sleep until something happens.
-        int epoll_timeout_ms = -1;
+        auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
 
         if (do_shutdown && !shutting_down) {
             do_shutdown = false;
@@ -766,27 +796,22 @@
         }
         if (!(waiting_for_prop || Service::is_exec_service_running())) {
             if (!shutting_down) {
-                auto next_process_restart_time = RestartProcesses();
+                auto next_process_action_time = HandleProcessActions();
 
                 // If there's a process that needs restarting, wake up in time for that.
-                if (next_process_restart_time) {
-                    epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
-                                           *next_process_restart_time - boot_clock::now())
-                                           .count();
-                    if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
+                if (next_process_action_time) {
+                    epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
+                            *next_process_action_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();
         }
     }
 
diff --git a/init/init.h b/init/init.h
index d4a0e96..cfc28f1 100644
--- a/init/init.h
+++ b/init/init.h
@@ -14,45 +14,34 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_INIT_H
-#define _INIT_INIT_H
+#pragma once
 
 #include <sys/types.h>
 
 #include <string>
-#include <vector>
 
 #include "action.h"
 #include "action_manager.h"
 #include "parser.h"
-#include "service.h"
+#include "service_list.h"
 
 namespace android {
 namespace init {
 
-// 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 std::string default_console;
-extern std::vector<std::string> late_import_paths;
-
 Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
+Parser CreateServiceOnlyParser(ServiceList& service_list);
 
-void HandleControlMessage(const std::string& msg, const std::string& arg, pid_t pid);
+bool 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)());
-
 bool start_waiting_for_property(const char *name, const char *value);
 
 void DumpState();
 
 void ResetWaitForProp();
 
-int main(int argc, char** argv);
+int SecondStageMain(int argc, char** argv);
 
 }  // namespace init
 }  // namespace android
-
-#endif  /* _INIT_INIT_H */
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
deleted file mode 100644
index 033ce41..0000000
--- a/init/init_first_stage.cpp
+++ /dev/null
@@ -1,532 +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:
-    ListenerAction HandleBlockDevice(const std::string& name, const Uevent&);
-    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_;
-    std::unique_ptr<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(INFO) << "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]);
-    }
-
-    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() {
-    // 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::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;
-        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::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/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_test.cpp b/init/init_test.cpp
index 0f9635f..a09db18 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -17,7 +17,6 @@
 #include <functional>
 
 #include <android-base/file.h>
-#include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
 #include "action.h"
@@ -28,6 +27,8 @@
 #include "keyword_map.h"
 #include "parser.h"
 #include "service.h"
+#include "service_list.h"
+#include "service_parser.h"
 #include "test_function_map.h"
 #include "util.h"
 
@@ -43,7 +44,8 @@
     Action::set_function_map(&test_function_map);
 
     Parser parser;
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(service_list, nullptr));
+    parser.AddSectionParser("service",
+                            std::make_unique<ServiceParser>(service_list, nullptr, std::nullopt));
     parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
@@ -181,7 +183,7 @@
     auto execute_command = [&num_executed](const BuiltinArguments& args) {
         EXPECT_EQ(2U, args.size());
         EXPECT_EQ(++num_executed, std::stoi(args[1]));
-        return Success();
+        return Result<void>{};
     };
 
     TestFunctionMap test_function_map;
diff --git a/init/keychords.cpp b/init/keychords.cpp
index e686ce1..d0ca3e7 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -16,110 +16,277 @@
 
 #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 = ServiceList::GetInstance().FindService(id, &Service::keychord_id);
-        if (svc) {
-            LOG(INFO) << "Starting service '" << svc->name() << "' from keychord " << id;
-            if (auto result = svc->Start(); !result) {
-                LOG(ERROR) << "Could not start service '" << svc->name() << "' from keychord " << id
-                           << ": " << result.error();
-            }
-        } 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() {
-    for (const auto& service : ServiceList::GetInstance()) {
-        add_service_keycodes(service.get());
+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))));
+}
+
+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);
+        }
     }
 
-    // Nothing to do if no services require keychords.
-    if (!keychords) {
+    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();
+    }
+    if (auto result = epoll_->RegisterHandler(fd, [this, fd]() { this->LambdaHandler(fd); });
+        !result) {
+        LOG(WARNING) << "Could not register keychord epoll handler: " << result.error();
+        return false;
+    }
+    return true;
+}
+
+void Keychords::GeteventOpenDevice(const std::string& device) {
+    if (registration_.count(device)) return;
+    auto fd = TEMP_FAILURE_RETRY(::open(device.c_str(), O_RDONLY | 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) {
+        if (auto result =
+                    epoll_->RegisterHandler(inotify_fd_, [this]() { this->InotifyHandler(); });
+            !result) {
+            LOG(WARNING) << "Could not register keychord epoll handler: " << result.error();
+        }
+    }
+}
 
-    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..33373d4
--- /dev/null
+++ b/init/keychords_test.cpp
@@ -0,0 +1,347 @@
+/*
+ * 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/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&&) noexcept;
+    EventHandler& operator=(const EventHandler&) = delete;
+    EventHandler& operator=(EventHandler&&) noexcept;
+    ~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) noexcept : fd_(rval.fd_) {
+    rval.fd_ = -1;
+}
+
+EventHandler& EventHandler::operator=(EventHandler&& rval) noexcept {
+    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 c95fc73..7837bb3 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -14,14 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_KEYWORD_MAP_H_
-#define _INIT_KEYWORD_MAP_H_
+#pragma once
 
 #include <map>
 #include <string>
 
-#include <android-base/stringprintf.h>
-
 #include "result.h"
 
 namespace android {
@@ -37,8 +34,6 @@
     }
 
     const Result<Function> FindFunction(const std::vector<std::string>& args) const {
-        using android::base::StringPrintf;
-
         if (args.empty()) return Error() << "Keyword needed, but not provided";
 
         auto& keyword = args[0];
@@ -46,7 +41,7 @@
 
         auto function_info_it = map().find(keyword);
         if (function_info_it == map().end()) {
-            return Error() << StringPrintf("Invalid keyword '%s'", keyword.c_str());
+            return Errorf("Invalid keyword '{}'", keyword);
         }
 
         auto function_info = function_info_it->second;
@@ -54,17 +49,17 @@
         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) {
-            return Error() << StringPrintf("%s requires %zu argument%s", keyword.c_str(), min_args,
-                                           (min_args > 1 || min_args == 0) ? "s" : "");
+            return Errorf("{} requires {} argument{}", keyword, 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()) {
-                return Error() << StringPrintf("%s requires at least %zu argument%s",
-                                               keyword.c_str(), min_args, min_args > 1 ? "s" : "");
+                return Errorf("{} requires at least {} argument{}", keyword, min_args,
+                              min_args > 1 ? "s" : "");
             } else {
-                return Error() << StringPrintf("%s requires between %zu and %zu arguments",
-                                               keyword.c_str(), min_args, max_args);
+                return Errorf("{} requires between {} and {} arguments", keyword, min_args,
+                              max_args);
             }
         }
 
@@ -79,5 +74,3 @@
 
 }  // namespace init
 }  // namespace android
-
-#endif
diff --git a/init/log.cpp b/init/log.cpp
deleted file mode 100644
index 6198fc2..0000000
--- a/init/log.cpp
+++ /dev/null
@@ -1,89 +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 <unistd.h>
-
-#include <android-base/logging.h>
-#include <cutils/android_reboot.h>
-#include <selinux/selinux.h>
-
-#include "reboot.h"
-
-namespace android {
-namespace init {
-
-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;
-    }
-
-    // DoReboot() does a lot to try to shutdown the system cleanly.  If something happens to call
-    // LOG(FATAL) in the shutdown path, we want to catch this and immediately use the syscall to
-    // reboot instead of recursing here.
-    static bool has_aborted = false;
-    if (!has_aborted) {
-        has_aborted = true;
-        // Do not queue "shutdown" trigger since we want to shutdown immediately and it's not likely
-        // that we can even run the ActionQueue at this point.
-        DoReboot(ANDROID_RB_RESTART2, "reboot", "bootloader", false);
-    } else {
-        RebootSystem(ANDROID_RB_RESTART2, "bootloader");
-    }
-}
-
-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 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
index 9ed451b..2ce46ef 100644
--- a/init/main.cpp
+++ b/init/main.cpp
@@ -14,8 +14,65 @@
  * limitations under the License.
  */
 
+#include "builtins.h"
+#include "first_stage_init.h"
 #include "init.h"
+#include "selinux.h"
+#include "subcontext.h"
+#include "ueventd.h"
+
+#include <android-base/logging.h>
+
+#if __has_feature(address_sanitizer)
+#include <sanitizer/asan_interface.h>
+#endif
+
+#if __has_feature(address_sanitizer)
+// Load asan.options if it exists since these are not yet in the environment.
+// Always ensure detect_container_overflow=0 as there are false positives with this check.
+// Always ensure abort_on_error=1 to ensure we reboot to bootloader for development builds.
+extern "C" const char* __asan_default_options() {
+    return "include_if_exists=/system/asan.options:detect_container_overflow=0:abort_on_error=1";
+}
+
+__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) extern "C" void
+__sanitizer_report_error_summary(const char* summary) {
+    LOG(ERROR) << "Init (error summary): " << summary;
+}
+
+__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) static void
+AsanReportCallback(const char* str) {
+    LOG(ERROR) << "Init: " << str;
+}
+#endif
+
+using namespace android::init;
 
 int main(int argc, char** argv) {
-    android::init::main(argc, argv);
+#if __has_feature(address_sanitizer)
+    __asan_set_error_report_callback(AsanReportCallback);
+#endif
+
+    if (!strcmp(basename(argv[0]), "ueventd")) {
+        return ueventd_main(argc, argv);
+    }
+
+    if (argc > 1) {
+        if (!strcmp(argv[1], "subcontext")) {
+            android::base::InitLogging(argv, &android::base::KernelLogger);
+            const BuiltinFunctionMap function_map;
+
+            return SubcontextMain(argc, argv, &function_map);
+        }
+
+        if (!strcmp(argv[1], "selinux_setup")) {
+            return SetupSelinux(argv);
+        }
+
+        if (!strcmp(argv[1], "second_stage")) {
+            return SecondStageMain(argc, argv);
+        }
+    }
+
+    return FirstStageMain(argc, argv);
 }
diff --git a/init/modalias_handler.cpp b/init/modalias_handler.cpp
new file mode 100644
index 0000000..07b05d8
--- /dev/null
+++ b/init/modalias_handler.cpp
@@ -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.
+ */
+
+#include "modalias_handler.h"
+
+#include <string>
+#include <vector>
+
+#include <modprobe/modprobe.h>
+
+namespace android {
+namespace init {
+
+ModaliasHandler::ModaliasHandler(const std::vector<std::string>& base_paths)
+    : modprobe_(base_paths) {}
+
+void ModaliasHandler::HandleUevent(const Uevent& uevent) {
+    if (uevent.modalias.empty()) return;
+    modprobe_.LoadWithAliases(uevent.modalias, true);
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/modalias_handler.h b/init/modalias_handler.h
new file mode 100644
index 0000000..ce89a05
--- /dev/null
+++ b/init/modalias_handler.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.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <modprobe/modprobe.h>
+
+#include "uevent.h"
+#include "uevent_handler.h"
+
+namespace android {
+namespace init {
+
+class ModaliasHandler : public UeventHandler {
+  public:
+    ModaliasHandler(const std::vector<std::string>&);
+    virtual ~ModaliasHandler() = default;
+
+    void HandleUevent(const Uevent& uevent) override;
+
+  private:
+    Modprobe modprobe_;
+};
+
+}  // namespace init
+}  // namespace android
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
new file mode 100644
index 0000000..b0b63c5
--- /dev/null
+++ b/init/mount_handler.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "mount_handler.h"
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <fs_mgr.h>
+#include <fstab/fstab.h>
+#include <libdm/dm.h>
+
+#include "epoll.h"
+#include "property_service.h"
+
+namespace android {
+namespace init {
+
+namespace {
+
+MountHandlerEntry ParseMount(const std::string& line) {
+    auto fields = android::base::Split(line, " ");
+    while (fields.size() < 3) fields.emplace_back("");
+    if (fields[0] == "/dev/root") {
+        auto& dm = dm::DeviceMapper::Instance();
+        std::string path;
+        if (dm.GetDmDevicePathByName("system", &path) || dm.GetDmDevicePathByName("vroot", &path)) {
+            fields[0] = path;
+        } else if (android::fs_mgr::Fstab fstab; android::fs_mgr::ReadDefaultFstab(&fstab)) {
+            auto entry = GetEntryForMountPoint(&fstab, "/");
+            if (entry || (entry = GetEntryForMountPoint(&fstab, "/system"))) {
+                fields[0] = entry->blk_device;
+            }
+        }
+    }
+    if (android::base::StartsWith(fields[0], "/dev/")) {
+        if (std::string link; android::base::Readlink(fields[0], &link)) {
+            fields[0] = link;
+        }
+    }
+    return MountHandlerEntry(fields[0], fields[1], fields[2]);
+}
+
+void SetMountProperty(const MountHandlerEntry& entry, bool add) {
+    static constexpr char devblock[] = "/dev/block/";
+    if (!android::base::StartsWith(entry.blk_device, devblock)) return;
+    std::string value;
+    if (add) {
+        value = entry.blk_device.substr(strlen(devblock));
+        if (android::base::StartsWith(value, "sd")) {
+            // All sd partitions inherit their queue characteristics
+            // from the whole device reference.  Strip partition number.
+            auto it = std::find_if(value.begin(), value.end(), [](char c) { return isdigit(c); });
+            if (it != value.end()) value.erase(it, value.end());
+        }
+        auto queue = "/sys/block/" + value + "/queue";
+        struct stat sb;
+        if (stat(queue.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = "";
+        if (stat(entry.mount_point.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = "";
+        // Clear the noise associated with loopback and APEX.
+        if (android::base::StartsWith(value, "loop")) value = "";
+        if (android::base::StartsWith(entry.mount_point, "/apex/")) value = "";
+    }
+    auto mount_prop = entry.mount_point;
+    if (mount_prop == "/") mount_prop = "/root";
+    std::replace(mount_prop.begin(), mount_prop.end(), '/', '.');
+    mount_prop = "dev.mnt.blk" + mount_prop;
+    // Set property even if its value does not change to trigger 'on property:'
+    // handling, except for clearing non-existent or already clear property.
+    // Goal is reduction of empty properties and associated triggers.
+    if (value.empty() && android::base::GetProperty(mount_prop, "").empty()) return;
+    property_set(mount_prop, value);
+}
+
+}  // namespace
+
+MountHandlerEntry::MountHandlerEntry(const std::string& blk_device, const std::string& mount_point,
+                                     const std::string& fs_type)
+    : blk_device(blk_device), mount_point(mount_point), fs_type(fs_type) {}
+
+bool MountHandlerEntry::operator<(const MountHandlerEntry& r) const {
+    if (blk_device < r.blk_device) return true;
+    if (blk_device > r.blk_device) return false;
+    if (mount_point < r.mount_point) return true;
+    if (mount_point > r.mount_point) return false;
+    return fs_type < r.fs_type;
+}
+
+MountHandler::MountHandler(Epoll* epoll) : epoll_(epoll), fp_(fopen("/proc/mounts", "re"), fclose) {
+    if (!fp_) PLOG(FATAL) << "Could not open /proc/mounts";
+    auto result = epoll->RegisterHandler(
+            fileno(fp_.get()), [this]() { this->MountHandlerFunction(); }, EPOLLERR | EPOLLPRI);
+    if (!result) LOG(FATAL) << result.error();
+}
+
+MountHandler::~MountHandler() {
+    if (fp_) epoll_->UnregisterHandler(fileno(fp_.get()));
+}
+
+void MountHandler::MountHandlerFunction() {
+    rewind(fp_.get());
+    std::vector<MountHandlerEntry> touched;
+    auto untouched = mounts_;
+    char* buf = nullptr;
+    size_t len = 0;
+    while (getline(&buf, &len, fp_.get()) != -1) {
+        auto entry = ParseMount(std::string(buf));
+        auto match = untouched.find(entry);
+        if (match == untouched.end()) {
+            touched.emplace_back(std::move(entry));
+        } else {
+            untouched.erase(match);
+        }
+    }
+    free(buf);
+    for (auto entry : untouched) {
+        SetMountProperty(entry, false);
+        mounts_.erase(entry);
+    }
+    for (auto entry : touched) {
+        SetMountProperty(entry, true);
+        mounts_.emplace(std::move(entry));
+    }
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/mount_handler.h b/init/mount_handler.h
new file mode 100644
index 0000000..e524a74
--- /dev/null
+++ b/init/mount_handler.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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>
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "epoll.h"
+
+namespace android {
+namespace init {
+
+struct MountHandlerEntry {
+    MountHandlerEntry(const std::string& blk_device, const std::string& mount_point,
+                      const std::string& fs_type);
+
+    bool operator<(const MountHandlerEntry& r) const;
+
+    const std::string blk_device;
+    const std::string mount_point;
+    const std::string fs_type;
+};
+
+class MountHandler {
+  public:
+    explicit MountHandler(Epoll* epoll);
+    MountHandler(const MountHandler&) = delete;
+    MountHandler(MountHandler&&) = delete;
+    MountHandler& operator=(const MountHandler&) = delete;
+    MountHandler& operator=(MountHandler&&) = delete;
+    ~MountHandler();
+
+  private:
+    void MountHandlerFunction();
+
+    Epoll* epoll_;
+    std::unique_ptr<FILE, decltype(&fclose)> fp_;
+    std::set<MountHandlerEntry> mounts_;
+};
+
+}  // namespace init
+}  // namespace android
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
new file mode 100644
index 0000000..12144c1
--- /dev/null
+++ b/init/mount_namespace.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "mount_namespace.h"
+
+#include <sys/mount.h>
+
+#include <string>
+#include <vector>
+
+#include <ApexProperties.sysprop.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+
+#include "util.h"
+
+namespace android {
+namespace init {
+namespace {
+
+static bool MakeShared(const std::string& mount_point, bool recursive = false) {
+    unsigned long mountflags = MS_SHARED;
+    if (recursive) {
+        mountflags |= MS_REC;
+    }
+    if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
+        PLOG(ERROR) << "Failed to change propagation type to shared";
+        return false;
+    }
+    return true;
+}
+
+static bool MakePrivate(const std::string& mount_point, bool recursive = false) {
+    unsigned long mountflags = MS_PRIVATE;
+    if (recursive) {
+        mountflags |= MS_REC;
+    }
+    if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
+        PLOG(ERROR) << "Failed to change propagation type to private";
+        return false;
+    }
+    return true;
+}
+
+static int OpenMountNamespace() {
+    int fd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
+    if (fd < 0) {
+        PLOG(ERROR) << "Cannot open fd for current mount namespace";
+    }
+    return fd;
+}
+
+static std::string GetMountNamespaceId() {
+    std::string ret;
+    if (!android::base::Readlink("/proc/self/ns/mnt", &ret)) {
+        PLOG(ERROR) << "Failed to read namespace ID";
+        return "";
+    }
+    return ret;
+}
+
+static bool IsApexUpdatable() {
+    static bool updatable = android::sysprop::ApexProperties::updatable().value_or(false);
+    return updatable;
+}
+
+static bool ActivateFlattenedApexesIfPossible() {
+    if (IsRecoveryMode() || IsApexUpdatable()) {
+        return true;
+    }
+
+    constexpr const char kSystemApex[] = "/system/apex";
+    constexpr const char kApexTop[] = "/apex";
+    if (mount(kSystemApex, kApexTop, nullptr, MS_BIND, nullptr) != 0) {
+        PLOG(ERROR) << "Could not bind mount " << kSystemApex << " to " << kApexTop;
+        return false;
+    }
+
+    // Special casing for the runtime APEX
+    constexpr const char kRuntimeApexMountPath[] = "/system/apex/com.android.runtime";
+    static const std::vector<std::string> kRuntimeApexDirNames = {"com.android.runtime.release",
+                                                                  "com.android.runtime.debug"};
+    bool success = false;
+    for (const auto& name : kRuntimeApexDirNames) {
+        std::string path = std::string(kSystemApex) + "/" + name;
+        if (access(path.c_str(), F_OK) == 0) {
+            if (mount(path.c_str(), kRuntimeApexMountPath, nullptr, MS_BIND, nullptr) == 0) {
+                success = true;
+                break;
+            }
+        }
+    }
+    if (!success) {
+        PLOG(ERROR) << "Failed to bind mount the runtime APEX to " << kRuntimeApexMountPath;
+    }
+    return success;
+}
+
+static android::base::unique_fd bootstrap_ns_fd;
+static android::base::unique_fd default_ns_fd;
+
+static std::string bootstrap_ns_id;
+static std::string default_ns_id;
+
+}  // namespace
+
+bool SetupMountNamespaces() {
+    // Set the propagation type of / as shared so that any mounting event (e.g.
+    // /data) is by default visible to all processes. When private mounting is
+    // needed for /foo/bar, then we will make /foo/bar as a mount point (by
+    // bind-mounting by to itself) and set the propagation type of the mount
+    // point to private.
+    if (!MakeShared("/", true /*recursive*/)) return false;
+
+    // /apex is a private mountpoint to give different sets of APEXes for
+    // the bootstrap and default mount namespaces. The processes running with
+    // the bootstrap namespace get APEXes from the read-only partition.
+    if (!(MakePrivate("/apex"))) return false;
+
+    bootstrap_ns_fd.reset(OpenMountNamespace());
+    bootstrap_ns_id = GetMountNamespaceId();
+
+    // When APEXes are updatable (e.g. not-flattened), we create separate mount
+    // namespaces for processes that are started before and after the APEX is
+    // activated by apexd. In the namespace for pre-apexd processes, small
+    // number of essential APEXes (e.g. com.android.runtime) are activated.
+    // In the namespace for post-apexd processes, all APEXes are activated.
+    bool success = true;
+    if (IsApexUpdatable() && !IsRecoveryMode()) {
+        // Creating a new namespace by cloning, saving, and switching back to
+        // the original namespace.
+        if (unshare(CLONE_NEWNS) == -1) {
+            PLOG(ERROR) << "Cannot create mount namespace";
+            return false;
+        }
+        default_ns_fd.reset(OpenMountNamespace());
+        default_ns_id = GetMountNamespaceId();
+
+        if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
+            PLOG(ERROR) << "Cannot switch back to bootstrap mount namespace";
+            return false;
+        }
+    } else {
+        // Otherwise, default == bootstrap
+        default_ns_fd.reset(OpenMountNamespace());
+        default_ns_id = GetMountNamespaceId();
+    }
+
+    success &= ActivateFlattenedApexesIfPossible();
+
+    LOG(INFO) << "SetupMountNamespaces done";
+    return success;
+}
+
+bool SwitchToDefaultMountNamespace() {
+    if (IsRecoveryMode()) {
+        // we don't have multiple namespaces in recovery mode
+        return true;
+    }
+    if (default_ns_id != GetMountNamespaceId()) {
+        if (setns(default_ns_fd.get(), CLONE_NEWNS) == -1) {
+            PLOG(ERROR) << "Failed to switch back to the default mount namespace.";
+            return false;
+        }
+    }
+
+    LOG(INFO) << "Switched to default mount namespace";
+    return true;
+}
+
+bool SwitchToBootstrapMountNamespaceIfNeeded() {
+    if (IsRecoveryMode()) {
+        // we don't have multiple namespaces in recovery mode
+        return true;
+    }
+    if (bootstrap_ns_id != GetMountNamespaceId() && bootstrap_ns_fd.get() != -1 &&
+        IsApexUpdatable()) {
+        if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
+            PLOG(ERROR) << "Failed to switch to bootstrap mount namespace.";
+            return false;
+        }
+    }
+    return true;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/mount_namespace.h b/init/mount_namespace.h
new file mode 100644
index 0000000..c41a449
--- /dev/null
+++ b/init/mount_namespace.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 SetupMountNamespaces();
+bool SwitchToDefaultMountNamespace();
+bool SwitchToBootstrapMountNamespaceIfNeeded();
+
+}  // namespace init
+}  // namespace android
diff --git a/init/parser.cpp b/init/parser.cpp
index 4453aaa..bbfbdc6 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -19,6 +19,7 @@
 #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>
@@ -39,25 +40,29 @@
     line_callbacks_.emplace_back(prefix, callback);
 }
 
-void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
-    // TODO: Use a parser with const input and remove this copy
-    std::vector<char> data_copy(data.begin(), data.end());
-    data_copy.push_back('\0');
+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_copy[0];
+    state.ptr = data->data();
     state.nexttoken = 0;
 
     SectionParser* section_parser = nullptr;
     int section_start_line = -1;
     std::vector<std::string> args;
 
+    // If we encounter a bad section start, there is no valid parser object to parse the subsequent
+    // sections, so we must suppress errors until the next valid section is found.
+    bool bad_section_found = false;
+
     auto end_section = [&] {
+        bad_section_found = false;
         if (section_parser == nullptr) return;
 
         if (auto result = section_parser->EndSection(); !result) {
-            (*parse_errors)++;
+            parse_error_count_++;
             LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
         }
 
@@ -69,44 +74,54 @@
         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:
+            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)) {
-                        end_section();
+                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 = callback(std::move(args)); !result) {
-                            (*parse_errors)++;
-                            LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
-                        }
-                        break;
+                    if (auto result = line_callback->second(std::move(args)); !result) {
+                        parse_error_count_++;
+                        LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                     }
-                }
-                if (section_parsers_.count(args[0])) {
+                } 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_errors)++;
+                        parse_error_count_++;
                         LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                         section_parser = nullptr;
+                        bad_section_found = true;
                     }
                 } else if (section_parser) {
                     if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
                         !result) {
-                        (*parse_errors)++;
+                        parse_error_count_++;
                         LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                     }
+                } else if (!bad_section_found) {
+                    parse_error_count_++;
+                    LOG(ERROR) << filename << ": " << state.line
+                               << ": Invalid section keyword found";
                 }
                 args.clear();
                 break;
+            }
             case T_TEXT:
                 args.emplace_back(state.text);
                 break;
@@ -114,30 +129,36 @@
     }
 }
 
-bool Parser::ParseConfigFile(const std::string& path, size_t* parse_errors) {
+bool Parser::ParseConfigFileInsecure(const std::string& path) {
+    std::string config_contents;
+    if (!android::base::ReadFileToString(path, &config_contents)) {
+        return false;
+    }
+
+    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(ERROR) << "Unable to read config file '" << path << "': " << config_contents.error();
+        LOG(INFO) << "Unable to read config file '" << path << "': " << config_contents.error();
         return false;
     }
 
-    config_contents->push_back('\n');  // TODO: fix parse_config.
-    ParseData(path, *config_contents, parse_errors);
-    for (const auto& [section_name, section_parser] : section_parsers_) {
-        section_parser->EndFile();
-    }
+    ParseData(path, &config_contents.value());
 
     LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
     return true;
 }
 
-bool Parser::ParseConfigDir(const std::string& path, size_t* parse_errors) {
+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(ERROR) << "Could not import directory '" << path << "'";
+        PLOG(INFO) << "Could not import directory '" << path << "'";
         return false;
     }
     dirent* current_file;
@@ -153,7 +174,7 @@
     // Sort first so we load files in a consistent order (bug 31996208)
     std::sort(files.begin(), files.end());
     for (const auto& file : files) {
-        if (!ParseConfigFile(file, parse_errors)) {
+        if (!ParseConfigFile(file)) {
             LOG(ERROR) << "could not import file '" << file << "'";
         }
     }
@@ -161,16 +182,10 @@
 }
 
 bool Parser::ParseConfig(const std::string& path) {
-    size_t parse_errors;
-    return ParseConfig(path, &parse_errors);
-}
-
-bool Parser::ParseConfig(const std::string& path, size_t* parse_errors) {
-    *parse_errors = 0;
     if (is_dir(path.c_str())) {
-        return ParseConfigDir(path, parse_errors);
+        return ParseConfigDir(path);
     }
-    return ParseConfigFile(path, parse_errors);
+    return ParseConfigFile(path);
 }
 
 }  // namespace init
diff --git a/init/parser.h b/init/parser.h
index f6e237f..95b0cd7 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -27,7 +27,7 @@
 //  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
+//  functions return Result<void> indicating if they have an error. It will be reported along
 //  with the filename and line number of where the error occurred.
 //
 //  1) ParseSection
@@ -51,10 +51,10 @@
 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 Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                      int line) = 0;
+    virtual Result<void> ParseLineSection(std::vector<std::string>&&, int) { return {}; };
+    virtual Result<void> EndSection() { return {}; };
     virtual void EndFile(){};
 };
 
@@ -67,22 +67,27 @@
     //  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>&&)>;
+    using LineCallback = std::function<Result<void>(std::vector<std::string>&&)>;
 
     Parser();
 
     bool ParseConfig(const std::string& path);
-    bool ParseConfig(const std::string& path, size_t* parse_errors);
+    bool ParseConfigFile(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, const std::string& data, size_t* parse_errors);
-    bool ParseConfigFile(const std::string& path, size_t* parse_errors);
-    bool ParseConfigDir(const std::string& path, size_t* parse_errors);
+    void ParseData(const std::string& filename, std::string* data);
+    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
diff --git a/init/persistent_properties.cpp b/init/persistent_properties.cpp
index 21adce9..baa9ad4 100644
--- a/init/persistent_properties.cpp
+++ b/init/persistent_properties.cpp
@@ -69,7 +69,7 @@
             continue;
         }
 
-        unique_fd fd(openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW));
+        unique_fd fd(openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
         if (fd == -1) {
             PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
             continue;
@@ -169,7 +169,7 @@
     return Error() << "Unable to parse persistent property file: Could not parse protobuf";
 }
 
-Result<Success> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
+Result<void> 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)));
@@ -191,7 +191,7 @@
         unlink(temp_filename.c_str());
         return Error(saved_errno) << "Unable to rename persistent property file";
     }
-    return Success();
+    return {};
 }
 
 // Persistent properties are not written often, so we rather not keep any data in memory and read
diff --git a/init/persistent_properties.h b/init/persistent_properties.h
index 5f4df85..3845a0d 100644
--- a/init/persistent_properties.h
+++ b/init/persistent_properties.h
@@ -30,7 +30,7 @@
 
 // Exposed only for testing
 Result<PersistentProperties> LoadPersistentPropertyFile();
-Result<Success> WritePersistentPropertyFile(const PersistentProperties& persistent_properties);
+Result<void> WritePersistentPropertyFile(const PersistentProperties& persistent_properties);
 extern std::string persistent_property_filename;
 
 }  // namespace init
diff --git a/init/persistent_properties_test.cpp b/init/persistent_properties_test.cpp
index 872e9a1..13796a6 100644
--- a/init/persistent_properties_test.cpp
+++ b/init/persistent_properties_test.cpp
@@ -20,7 +20,7 @@
 
 #include <vector>
 
-#include <android-base/test_utils.h>
+#include <android-base/file.h>
 #include <gtest/gtest.h>
 
 #include "util.h"
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 4172ba7..b89914f 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -16,6 +16,7 @@
 
 #include "property_service.h"
 
+#include <android/api-level.h>
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -38,8 +39,12 @@
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 
+#include <atomic>
+#include <map>
 #include <memory>
+#include <mutex>
 #include <queue>
+#include <thread>
 #include <vector>
 
 #include <android-base/chrono_utils.h>
@@ -48,14 +53,14 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <bootimg.h>
-#include <fs_mgr.h>
+#include <android-base/unique_fd.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 "debug_ramdisk.h"
 #include "init.h"
 #include "persistent_properties.h"
 #include "property_type.h"
@@ -65,24 +70,25 @@
 
 using namespace std::literals;
 
-using android::base::GetIntProperty;
+using android::base::GetProperty;
 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::unique_fd;
 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 constexpr const char kRestoreconProperty[] = "selinux.restorecon_recursive";
+
 static bool persistent_properties_loaded = false;
 
 static int property_set_fd = -1;
@@ -100,7 +106,24 @@
     const char* name;
 };
 
+static int PropertyAuditCallback(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;
+}
+
 void property_init() {
+    selinux_callback cb;
+    cb.func_audit = PropertyAuditCallback;
+    selinux_set_callback(SELINUX_CB_AUDIT, cb);
+
     mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
     CreateSerializedPropertyInfo();
     if (__system_property_area_init()) {
@@ -110,6 +133,22 @@
         LOG(FATAL) << "Failed to load serialized property info file";
     }
 }
+
+bool CanReadProperty(const std::string& source_context, const std::string& name) {
+    const char* target_context = nullptr;
+    property_info_area->GetPropertyInfo(name.c_str(), &target_context, nullptr);
+
+    PropertyAuditData audit_data;
+
+    audit_data.name = name.c_str();
+
+    ucred cr = {.pid = 0, .uid = 0, .gid = 0};
+    audit_data.cr = &cr;
+
+    return selinux_check_access(source_context.c_str(), target_context, "file", "read",
+                                &audit_data) == 0;
+}
+
 static bool CheckMacPerms(const std::string& name, const char* target_context,
                           const char* source_context, const ucred& cr) {
     if (!target_context || !source_context) {
@@ -171,88 +210,51 @@
     return PROP_SUCCESS;
 }
 
-typedef int (*PropertyAsyncFunc)(const std::string&, const std::string&);
+class AsyncRestorecon {
+  public:
+    void TriggerRestorecon(const std::string& path) {
+        auto guard = std::lock_guard{mutex_};
+        paths_.emplace(path);
 
-struct PropertyChildInfo {
-    pid_t pid;
-    PropertyAsyncFunc func;
-    std::string name;
-    std::string value;
+        if (!thread_started_) {
+            thread_started_ = true;
+            std::thread{&AsyncRestorecon::ThreadFunction, this}.detach();
+        }
+    }
+
+  private:
+    void ThreadFunction() {
+        auto lock = std::unique_lock{mutex_};
+
+        while (!paths_.empty()) {
+            auto path = paths_.front();
+            paths_.pop();
+
+            lock.unlock();
+            if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+                LOG(ERROR) << "Asynchronous restorecon of '" << path << "' failed'";
+            }
+            android::base::SetProperty(kRestoreconProperty, path);
+            lock.lock();
+        }
+
+        thread_started_ = false;
+    }
+
+    std::mutex mutex_;
+    std::queue<std::string> paths_;
+    bool thread_started_ = false;
 };
 
-static std::queue<PropertyChildInfo> property_children;
-
-static void PropertyChildLaunch() {
-    auto& info = property_children.front();
-    pid_t pid = fork();
-    if (pid < 0) {
-        LOG(ERROR) << "Failed to fork for property_set_async";
-        while (!property_children.empty()) {
-            property_children.pop();
-        }
-        return;
-    }
-    if (pid != 0) {
-        info.pid = pid;
-    } else {
-        if (info.func(info.name, info.value) != 0) {
-            LOG(ERROR) << "property_set_async(\"" << info.name << "\", \"" << info.value
-                       << "\") failed";
-        }
-        _exit(0);
-    }
-}
-
-bool PropertyChildReap(pid_t pid) {
-    if (property_children.empty()) {
-        return false;
-    }
-    auto& info = property_children.front();
-    if (info.pid != pid) {
-        return false;
-    }
-    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()) {
-        PropertyChildLaunch();
-    }
-    return true;
-}
-
-static uint32_t PropertySetAsync(const std::string& name, const std::string& value,
-                                 PropertyAsyncFunc func, std::string* error) {
-    if (value.empty()) {
-        return PropertySet(name, value, error);
-    }
-
-    PropertyChildInfo info;
-    info.func = func;
-    info.name = name;
-    info.value = value;
-    property_children.push(info);
-    if (property_children.size() == 1) {
-        PropertyChildLaunch();
-    }
-    return PROP_SUCCESS;
-}
-
-static int RestoreconRecursiveAsync(const std::string& name, const std::string& value) {
-    return selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
-}
-
 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") {
-        LOG(ERROR) << "InitPropertySet: Do not set selinux.restorecon_recursive from init; use the "
-                      "restorecon builtin directly";
+    if (name == kRestoreconProperty) {
+        LOG(ERROR) << "InitPropertySet: Do not set '" << kRestoreconProperty
+                   << "' from init; use the restorecon builtin directly";
         return PROP_ERROR_INVALID_NAME;
     }
 
@@ -313,18 +315,20 @@
         return result == sizeof(value);
     }
 
+    bool GetSourceContext(std::string* source_context) const {
+        char* c_source_context = nullptr;
+        if (getpeercon(socket_, &c_source_context) != 0) {
+            return false;
+        }
+        *source_context = c_source_context;
+        freecon(c_source_context);
+        return true;
+    }
+
     int socket() { return socket_; }
 
     const ucred& cred() { return cred_; }
 
-    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];
@@ -377,6 +381,7 @@
 
             int result = TEMP_FAILURE_RETRY(recv(socket_, data, bytes_left, MSG_DONTWAIT));
             if (result <= 0) {
+                PLOG(ERROR) << "sys_prop: recv error";
                 return false;
             }
 
@@ -384,6 +389,10 @@
             data += result;
         }
 
+        if (bytes_left != 0) {
+            LOG(ERROR) << "sys_prop: recv data is not properly obtained.";
+        }
+
         return bytes_left == 0;
     }
 
@@ -423,8 +432,8 @@
 }
 
 // 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) {
+uint32_t CheckPermissions(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;
@@ -437,7 +446,6 @@
             return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
         }
 
-        HandleControlMessage(name.c_str() + 4, value, cr.pid);
         return PROP_SUCCESS;
     }
 
@@ -456,6 +464,22 @@
         return PROP_ERROR_INVALID_VALUE;
     }
 
+    return PROP_SUCCESS;
+}
+
+// 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 (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) {
+        return ret;
+    }
+
+    if (StartsWith(name, "ctl.")) {
+        return HandleControlMessage(name.c_str() + 4, value, cr.pid)
+                       ? PROP_SUCCESS
+                       : PROP_ERROR_HANDLE_CONTROL_MESSAGE;
+    }
+
     // 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") {
@@ -471,8 +495,14 @@
                   << process_log_string;
     }
 
-    if (name == "selinux.restorecon_recursive") {
-        return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
+    // If a process other than init is writing a non-empty value, it means that process is
+    // requesting that init performs a restorecon operation on the path specified by 'value'.
+    // We use a thread to do this restorecon operation to prevent holding up init, as it may take
+    // a long time to complete.
+    if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) {
+        static AsyncRestorecon async_restorecon;
+        async_restorecon.TriggerRestorecon(value);
+        return PROP_SUCCESS;
     }
 
     return PropertySet(name, value, error);
@@ -518,14 +548,18 @@
         prop_name[PROP_NAME_MAX-1] = 0;
         prop_value[PROP_VALUE_MAX-1] = 0;
 
+        std::string source_context;
+        if (!socket.GetSourceContext(&source_context)) {
+            PLOG(ERROR) << "Unable to set property '" << prop_name << "': getpeercon() failed";
+            return;
+        }
+
         const auto& cr = socket.cred();
         std::string error;
-        uint32_t result =
-            HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
+        uint32_t result = HandlePropertySet(prop_name, prop_value, 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;
+            LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
+                       << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
         }
 
         break;
@@ -541,13 +575,19 @@
           return;
         }
 
+        std::string source_context;
+        if (!socket.GetSourceContext(&source_context)) {
+            PLOG(ERROR) << "Unable to set property '" << name << "': getpeercon() failed";
+            socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
+            return;
+        }
+
         const auto& cr = socket.cred();
         std::string error;
-        uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error);
+        uint32_t result = HandlePropertySet(name, value, 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;
+            LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
+                       << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
         }
         socket.SendUint32(result);
         break;
@@ -560,18 +600,20 @@
     }
 }
 
-static bool load_properties_from_file(const char *, const char *);
+static bool load_properties_from_file(const char*, const char*,
+                                      std::map<std::string, std::string>*);
 
 /*
  * 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 LoadProperties(char* data, const char* filter, const char* filename) {
+static void LoadProperties(char* data, const char* filter, const char* filename,
+                           std::map<std::string, std::string>* properties) {
     char *key, *value, *eol, *sol, *tmp, *fn;
     size_t flen = 0;
 
     const char* context = kInitContext.c_str();
-    if (SelinuxHasVendorInit()) {
+    if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
         for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
             if (StartsWith(filename, path_prefix)) {
                 context = secontext;
@@ -605,8 +647,14 @@
                 while (isspace(*key)) key++;
             }
 
-            load_properties_from_file(fn, key);
+            std::string raw_filename(fn);
+            std::string expanded_filename;
+            if (!expand_props(raw_filename, &expanded_filename)) {
+                LOG(ERROR) << "Could not expand filename '" << raw_filename << "'";
+                continue;
+            }
 
+            load_properties_from_file(expanded_filename.c_str(), key, properties);
         } else {
             value = strchr(key, '=');
             if (!value) continue;
@@ -619,25 +667,32 @@
 
             if (flen > 0) {
                 if (filter[flen - 1] == '*') {
-                    if (strncmp(key, filter, flen - 1)) continue;
+                    if (strncmp(key, filter, flen - 1) != 0) continue;
                 } else {
-                    if (strcmp(key, filter)) continue;
+                    if (strcmp(key, filter) != 0) continue;
                 }
             }
 
             if (StartsWith(key, "ctl.") || key == "sys.powerctl"s ||
-                key == "selinux.restorecon_recursive"s) {
+                std::string{key} == kRestoreconProperty) {
                 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
+            if (CheckPermissions(key, value, context, cr, &error) == PROP_SUCCESS) {
+                auto it = properties->find(key);
+                if (it == properties->end()) {
+                    (*properties)[key] = value;
+                } else if (it->second != value) {
+                    LOG(WARNING) << "Overriding previous 'ro.' property '" << key << "':'"
+                                 << it->second << "' with new value '" << value << "'";
+                    it->second = value;
+                }
+            } else {
+                LOG(ERROR) << "Do not have permissions to set '" << key << "' to '" << value
                            << "' in property file '" << filename << "': " << error;
             }
         }
@@ -646,7 +701,8 @@
 
 // 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 bool load_properties_from_file(const char* filename, const char* filter) {
+static bool load_properties_from_file(const char* filename, const char* filter,
+                                      std::map<std::string, std::string>* properties) {
     Timer t;
     auto file_contents = ReadFile(filename);
     if (!file_contents) {
@@ -656,7 +712,7 @@
     }
     file_contents->push_back('\n');
 
-    LoadProperties(file_contents->data(), filter, filename);
+    LoadProperties(file_contents->data(), filter, filename, properties);
     LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
     return true;
 }
@@ -677,24 +733,17 @@
     }
 }
 
-void property_load_boot_defaults() {
-    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("/odm/default.prop", NULL);
-    load_properties_from_file("/vendor/default.prop", NULL);
-
-    update_sys_usb_config();
-}
-
 static void load_override_properties() {
     if (ALLOW_LOCAL_PROP_OVERRIDE) {
-        load_properties_from_file("/data/local.prop", NULL);
+        std::map<std::string, std::string> properties;
+        load_properties_from_file("/data/local.prop", nullptr, &properties);
+        for (const auto& [name, value] : properties) {
+            std::string error;
+            if (PropertySet(name, value, &error) != PROP_SUCCESS) {
+                LOG(ERROR) << "Could not set '" << name << "' to '" << value
+                           << "' in /data/local.prop: " << error;
+            }
+        }
     }
 }
 
@@ -725,56 +774,153 @@
     property_set("ro.persistent_properties.ready", "true");
 }
 
-void load_recovery_id_prop() {
-    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                               fs_mgr_free_fstab);
-    if (!fstab) {
-        PLOG(ERROR) << "unable to read default fstab";
-        return;
-    }
+// If the ro.product.[brand|device|manufacturer|model|name] properties have not been explicitly
+// set, derive them from ro.product.${partition}.* properties
+static void property_initialize_ro_product_props() {
+    const char* RO_PRODUCT_PROPS_PREFIX = "ro.product.";
+    const char* RO_PRODUCT_PROPS[] = {
+            "brand", "device", "manufacturer", "model", "name",
+    };
+    const char* RO_PRODUCT_PROPS_ALLOWED_SOURCES[] = {
+            "odm", "product", "product_services", "system", "vendor",
+    };
+    const char* RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER =
+            "product,product_services,odm,vendor,system";
+    const std::string EMPTY = "";
 
-    fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab.get(), RECOVERY_MOUNT_POINT);
-    if (rec == NULL) {
-        LOG(ERROR) << "/recovery not specified in fstab";
-        return;
-    }
+    std::string ro_product_props_source_order =
+            GetProperty("ro.product.property_source_order", EMPTY);
 
-    int fd = open(rec->blk_device, O_RDONLY);
-    if (fd == -1) {
-        PLOG(ERROR) << "error opening block device " << rec->blk_device;
-        return;
-    }
-
-    boot_img_hdr hdr;
-    if (android::base::ReadFully(fd, &hdr, sizeof(hdr))) {
-        std::string hex = bytes_to_hex(reinterpret_cast<uint8_t*>(hdr.id), sizeof(hdr.id));
-        property_set("ro.recovery_id", hex);
+    if (!ro_product_props_source_order.empty()) {
+        // Verify that all specified sources are valid
+        for (const auto& source : Split(ro_product_props_source_order, ",")) {
+            // Verify that the specified source is valid
+            bool is_allowed_source = false;
+            for (const auto& allowed_source : RO_PRODUCT_PROPS_ALLOWED_SOURCES) {
+                if (source == allowed_source) {
+                    is_allowed_source = true;
+                    break;
+                }
+            }
+            if (!is_allowed_source) {
+                LOG(ERROR) << "Found unexpected source in ro.product.property_source_order; "
+                              "using the default property source order";
+                ro_product_props_source_order = RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER;
+                break;
+            }
+        }
     } else {
-        PLOG(ERROR) << "error reading /recovery";
+        ro_product_props_source_order = RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER;
     }
 
-    close(fd);
+    for (const auto& ro_product_prop : RO_PRODUCT_PROPS) {
+        std::string base_prop(RO_PRODUCT_PROPS_PREFIX);
+        base_prop += ro_product_prop;
+
+        std::string base_prop_val = GetProperty(base_prop, EMPTY);
+        if (!base_prop_val.empty()) {
+            continue;
+        }
+
+        for (const auto& source : Split(ro_product_props_source_order, ",")) {
+            std::string target_prop(RO_PRODUCT_PROPS_PREFIX);
+            target_prop += source;
+            target_prop += '.';
+            target_prop += ro_product_prop;
+
+            std::string target_prop_val = GetProperty(target_prop, EMPTY);
+            if (!target_prop_val.empty()) {
+                LOG(INFO) << "Setting product property " << base_prop << " to '" << target_prop_val
+                          << "' (from " << target_prop << ")";
+                std::string error;
+                uint32_t res = PropertySet(base_prop, target_prop_val, &error);
+                if (res != PROP_SUCCESS) {
+                    LOG(ERROR) << "Error setting product property " << base_prop << ": err=" << res
+                               << " (" << error << ")";
+                }
+                break;
+            }
+        }
+    }
 }
 
-void load_system_props() {
-    load_properties_from_file("/system/build.prop", NULL);
-    load_properties_from_file("/odm/build.prop", NULL);
-    load_properties_from_file("/vendor/build.prop", NULL);
-    load_properties_from_file("/factory/factory.prop", "ro.*");
-    load_recovery_id_prop();
-}
-
-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;
+// If the ro.build.fingerprint property has not been set, derive it from constituent pieces
+static void property_derive_build_fingerprint() {
+    std::string build_fingerprint = GetProperty("ro.build.fingerprint", "");
+    if (!build_fingerprint.empty()) {
+        return;
     }
 
-    snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid,
-             d->cr->gid);
-    return 0;
+    const std::string UNKNOWN = "unknown";
+    build_fingerprint = GetProperty("ro.product.brand", UNKNOWN);
+    build_fingerprint += '/';
+    build_fingerprint += GetProperty("ro.product.name", UNKNOWN);
+    build_fingerprint += '/';
+    build_fingerprint += GetProperty("ro.product.device", UNKNOWN);
+    build_fingerprint += ':';
+    build_fingerprint += GetProperty("ro.build.version.release", UNKNOWN);
+    build_fingerprint += '/';
+    build_fingerprint += GetProperty("ro.build.id", UNKNOWN);
+    build_fingerprint += '/';
+    build_fingerprint += GetProperty("ro.build.version.incremental", UNKNOWN);
+    build_fingerprint += ':';
+    build_fingerprint += GetProperty("ro.build.type", UNKNOWN);
+    build_fingerprint += '/';
+    build_fingerprint += GetProperty("ro.build.tags", UNKNOWN);
+
+    LOG(INFO) << "Setting property 'ro.build.fingerprint' to '" << build_fingerprint << "'";
+
+    std::string error;
+    uint32_t res = PropertySet("ro.build.fingerprint", build_fingerprint, &error);
+    if (res != PROP_SUCCESS) {
+        LOG(ERROR) << "Error setting property 'ro.build.fingerprint': err=" << res << " (" << error
+                   << ")";
+    }
+}
+
+void property_load_boot_defaults(bool load_debug_prop) {
+    // TODO(b/117892318): merge prop.default and build.prop files into one
+    // We read the properties and their values into a map, in order to always allow properties
+    // loaded in the later property files to override the properties in loaded in the earlier
+    // property files, regardless of if they are "ro." properties or not.
+    std::map<std::string, std::string> properties;
+    if (!load_properties_from_file("/system/etc/prop.default", nullptr, &properties)) {
+        // Try recovery path
+        if (!load_properties_from_file("/prop.default", nullptr, &properties)) {
+            // Try legacy path
+            load_properties_from_file("/default.prop", nullptr, &properties);
+        }
+    }
+    load_properties_from_file("/system/build.prop", nullptr, &properties);
+    load_properties_from_file("/vendor/default.prop", nullptr, &properties);
+    load_properties_from_file("/vendor/build.prop", nullptr, &properties);
+    if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
+        load_properties_from_file("/odm/etc/build.prop", nullptr, &properties);
+    } else {
+        load_properties_from_file("/odm/default.prop", nullptr, &properties);
+        load_properties_from_file("/odm/build.prop", nullptr, &properties);
+    }
+    load_properties_from_file("/product/build.prop", nullptr, &properties);
+    load_properties_from_file("/product_services/build.prop", nullptr, &properties);
+    load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
+
+    if (load_debug_prop) {
+        LOG(INFO) << "Loading " << kDebugRamdiskProp;
+        load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
+    }
+
+    for (const auto& [name, value] : properties) {
+        std::string error;
+        if (PropertySet(name, value, &error) != PROP_SUCCESS) {
+            LOG(ERROR) << "Could not set '" << name << "' to '" << value
+                       << "' while loading .prop files" << error;
+        }
+    }
+
+    property_initialize_ro_product_props();
+    property_derive_build_fingerprint();
+
+    update_sys_usb_config();
 }
 
 bool LoadPropertyInfoFromFile(const std::string& filename,
@@ -812,6 +958,13 @@
             LoadPropertyInfoFromFile("/vendor/etc/selinux/nonplat_property_contexts",
                                      &property_infos);
         }
+        if (access("/product/etc/selinux/product_property_contexts", R_OK) != -1) {
+            LoadPropertyInfoFromFile("/product/etc/selinux/product_property_contexts",
+                                     &property_infos);
+        }
+        if (access("/odm/etc/selinux/odm_property_contexts", R_OK) != -1) {
+            LoadPropertyInfoFromFile("/odm/etc/selinux/odm_property_contexts", &property_infos);
+        }
     } else {
         if (!LoadPropertyInfoFromFile("/plat_property_contexts", &property_infos)) {
             return;
@@ -820,6 +973,8 @@
             // Fallback to nonplat_* if vendor_* doesn't exist.
             LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
         }
+        LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
+        LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
     }
 
     auto serialized_contexts = std::string();
@@ -837,11 +992,7 @@
     selinux_android_restorecon(kPropertyInfosPath, 0);
 }
 
-void start_property_service() {
-    selinux_callback cb;
-    cb.func_audit = SelinuxAuditCallback;
-    selinux_set_callback(SELINUX_CB_AUDIT, cb);
-
+void StartPropertyService(Epoll* epoll) {
     property_set("ro.property_service.version", "2");
 
     property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
@@ -852,7 +1003,46 @@
 
     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();
+    }
+}
+
+Result<int> CallFunctionAndHandlePropertiesImpl(const std::function<int()>& f) {
+    unique_fd reader;
+    unique_fd writer;
+    if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &reader, &writer)) {
+        return ErrnoError() << "Could not create socket pair";
+    }
+
+    int result = 0;
+    std::atomic<bool> end = false;
+    auto thread = std::thread{[&f, &result, &end, &writer] {
+        result = f();
+        end = true;
+        send(writer, "1", 1, 0);
+    }};
+
+    Epoll epoll;
+    if (auto result = epoll.Open(); !result) {
+        return Error() << "Could not create epoll: " << result.error();
+    }
+    if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
+        return Error() << "Could not register epoll handler for property fd: " << result.error();
+    }
+
+    // No-op function, just used to break from loop.
+    if (auto result = epoll.RegisterHandler(reader, [] {}); !result) {
+        return Error() << "Could not register epoll handler for ending thread:" << result.error();
+    }
+
+    while (!end) {
+        epoll.Wait({});
+    }
+
+    thread.join();
+
+    return result;
 }
 
 }  // namespace init
diff --git a/init/property_service.h b/init/property_service.h
index 897ac15..dc47b4d 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -14,30 +14,38 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_PROPERTY_H
-#define _INIT_PROPERTY_H
+#pragma once
 
 #include <sys/socket.h>
 
+#include <functional>
 #include <string>
 
+#include "epoll.h"
+#include "result.h"
+
 namespace android {
 namespace init {
 
+bool CanReadProperty(const std::string& source_context, const std::string& 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);
+void property_init();
+void property_load_boot_defaults(bool load_debug_prop);
+void load_persist_props();
+void StartPropertyService(Epoll* epoll);
 
-void property_init(void);
-void property_load_boot_defaults(void);
-void load_persist_props(void);
-void load_system_props(void);
-void start_property_service(void);
+template <typename F, typename... Args>
+Result<int> CallFunctionAndHandleProperties(F&& f, Args&&... args) {
+    Result<int> CallFunctionAndHandlePropertiesImpl(const std::function<int()>& f);
+
+    auto func = [&] { return f(args...); };
+    return CallFunctionAndHandlePropertiesImpl(func);
+}
 
 }  // namespace init
 }  // namespace android
-
-#endif  /* _INIT_PROPERTY_H */
diff --git a/init/property_type.cpp b/init/property_type.cpp
index 249b12b..7d80555 100644
--- a/init/property_type.cpp
+++ b/init/property_type.cpp
@@ -29,6 +29,11 @@
 namespace init {
 
 bool CheckType(const std::string& type_string, const std::string& value) {
+    // Always allow clearing a property such that the default value when it is not set takes over.
+    if (value.empty()) {
+        return true;
+    }
+
     auto type_strings = Split(type_string, " ");
     if (type_strings.empty()) {
         return false;
diff --git a/init/property_type_test.cpp b/init/property_type_test.cpp
index 068bccc..6d7f927 100644
--- a/init/property_type_test.cpp
+++ b/init/property_type_test.cpp
@@ -32,7 +32,7 @@
 }
 
 TEST(property_type, CheckType_int) {
-    EXPECT_FALSE(CheckType("int", ""));
+    EXPECT_TRUE(CheckType("int", ""));
     EXPECT_FALSE(CheckType("int", "abc"));
     EXPECT_FALSE(CheckType("int", "-abc"));
     EXPECT_TRUE(CheckType("int", "0"));
@@ -43,7 +43,7 @@
 }
 
 TEST(property_type, CheckType_uint) {
-    EXPECT_FALSE(CheckType("uint", ""));
+    EXPECT_TRUE(CheckType("uint", ""));
     EXPECT_FALSE(CheckType("uint", "abc"));
     EXPECT_FALSE(CheckType("uint", "-abc"));
     EXPECT_TRUE(CheckType("uint", "0"));
@@ -53,7 +53,7 @@
 }
 
 TEST(property_type, CheckType_double) {
-    EXPECT_FALSE(CheckType("double", ""));
+    EXPECT_TRUE(CheckType("double", ""));
     EXPECT_FALSE(CheckType("double", "abc"));
     EXPECT_FALSE(CheckType("double", "-abc"));
     EXPECT_TRUE(CheckType("double", "0.0"));
@@ -64,7 +64,7 @@
 }
 
 TEST(property_type, CheckType_size) {
-    EXPECT_FALSE(CheckType("size", ""));
+    EXPECT_TRUE(CheckType("size", ""));
     EXPECT_FALSE(CheckType("size", "ab"));
     EXPECT_FALSE(CheckType("size", "abcd"));
     EXPECT_FALSE(CheckType("size", "0"));
@@ -80,7 +80,7 @@
 }
 
 TEST(property_type, CheckType_enum) {
-    EXPECT_FALSE(CheckType("enum abc", ""));
+    EXPECT_TRUE(CheckType("enum abc", ""));
     EXPECT_FALSE(CheckType("enum abc", "ab"));
     EXPECT_FALSE(CheckType("enum abc", "abcd"));
     EXPECT_FALSE(CheckType("enum 123 456 789", "0"));
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 503afdd..d9d885c 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -19,13 +19,14 @@
 #include <dirent.h>
 #include <fcntl.h>
 #include <linux/fs.h>
+#include <linux/loop.h>
 #include <mntent.h>
-#include <sys/capability.h>
+#include <semaphore.h>
 #include <sys/cdefs.h>
 #include <sys/ioctl.h>
 #include <sys/mount.h>
-#include <sys/reboot.h>
 #include <sys/stat.h>
+#include <sys/swap.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
 #include <sys/wait.h>
@@ -40,7 +41,6 @@
 #include <android-base/logging.h>
 #include <android-base/macros.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>
@@ -51,15 +51,20 @@
 #include <selinux/selinux.h>
 
 #include "action_manager.h"
-#include "capabilities.h"
 #include "init.h"
 #include "property_service.h"
+#include "reboot_utils.h"
 #include "service.h"
+#include "service_list.h"
 #include "sigchld_handler.h"
 
+#define PROC_SYSRQ "/proc/sysrq-trigger"
+
+using android::base::GetBoolProperty;
 using android::base::Split;
-using android::base::StringPrintf;
 using android::base::Timer;
+using android::base::unique_fd;
+using android::base::WriteStringToFile;
 
 namespace android {
 namespace init {
@@ -105,13 +110,17 @@
         int st;
         if (IsF2Fs()) {
             const char* f2fs_argv[] = {
-                "/system/bin/fsck.f2fs", "-f", mnt_fsname_.c_str(),
+                    "/system/bin/fsck.f2fs",
+                    "-a",
+                    mnt_fsname_.c_str(),
             };
             android_fork_execvp_ext(arraysize(f2fs_argv), (char**)f2fs_argv, &st, true, LOG_KLOG,
                                     true, nullptr, nullptr, 0);
         } else if (IsExt4()) {
             const char* ext4_argv[] = {
-                "/system/bin/e2fsck", "-f", "-y", mnt_fsname_.c_str(),
+                    "/system/bin/e2fsck",
+                    "-y",
+                    mnt_fsname_.c_str(),
             };
             android_fork_execvp_ext(arraysize(ext4_argv), (char**)ext4_argv, &st, true, LOG_KLOG,
                                     true, nullptr, nullptr, 0);
@@ -144,7 +153,9 @@
         LOG(WARNING) << "cannot find blank_screen in TurnOffBacklight";
         return;
     }
-    service->Start();
+    if (auto result = service->Start(); !result) {
+        LOG(WARNING) << "Could not start blank_screen service: " << result.error();
+    }
 }
 
 static void ShutdownVold() {
@@ -159,60 +170,12 @@
                  << stat;
 }
 
-bool IsRebootCapable() {
-    if (!CAP_IS_SUPPORTED(CAP_SYS_BOOT)) {
-        PLOG(WARNING) << "CAP_SYS_BOOT is not supported";
-        return true;
-    }
-
-    ScopedCaps caps(cap_get_proc());
-    if (!caps) {
-        PLOG(WARNING) << "cap_get_proc() failed";
-        return true;
-    }
-
-    cap_flag_value_t value = CAP_SET;
-    if (cap_get_flag(caps.get(), CAP_SYS_BOOT, CAP_EFFECTIVE, &value) != 0) {
-        PLOG(WARNING) << "cap_get_flag(CAP_SYS_BOOT, EFFECTIVE) failed";
-        return true;
-    }
-    return value == CAP_SET;
-}
-
-void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
-    LOG(INFO) << "Reboot ending, jumping to kernel";
-
-    if (!IsRebootCapable()) {
-        // On systems where init does not have the capability of rebooting the
-        // device, just exit cleanly.
-        exit(0);
-    }
-
-    switch (cmd) {
-        case ANDROID_RB_POWEROFF:
-            reboot(RB_POWER_OFF);
-            break;
-
-        case ANDROID_RB_RESTART2:
-            syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
-                    LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
-            break;
-
-        case ANDROID_RB_THERMOFF:
-            reboot(RB_POWER_OFF);
-            break;
-    }
-    // In normal case, reboot should not return.
-    PLOG(ERROR) << "reboot call returned";
-    abort();
-}
-
 /* Find all read+write block devices and emulated devices in /proc/mounts
  * and add them to correpsponding list.
  */
 static bool FindPartitionsToUmount(std::vector<MountEntry>* blockDevPartitions,
                                    std::vector<MountEntry>* emulatedPartitions, bool dump) {
-    std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
+    std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "re"), endmntent);
     if (fp == nullptr) {
         PLOG(ERROR) << "Failed to open /proc/mounts";
         return false;
@@ -237,7 +200,7 @@
     return true;
 }
 
-static void DumpUmountDebuggingInfo(bool dump_all) {
+static void DumpUmountDebuggingInfo() {
     int status;
     if (!security_getenforce()) {
         LOG(INFO) << "Run lsof";
@@ -246,10 +209,9 @@
                                 true, nullptr, nullptr, 0);
     }
     FindPartitionsToUmount(nullptr, nullptr, true);
-    if (dump_all) {
-        // dump current tasks, this log can be lengthy, so only dump with dump_all
-        android::base::WriteStringToFile("t", "/proc/sysrq-trigger");
-    }
+    // dump current CPU stack traces and uninterruptible tasks
+    WriteStringToFile("l", PROC_SYSRQ);
+    WriteStringToFile("w", PROC_SYSRQ);
 }
 
 static UmountStat UmountPartitions(std::chrono::milliseconds timeout) {
@@ -289,7 +251,91 @@
     }
 }
 
-static void KillAllProcesses() { android::base::WriteStringToFile("i", "/proc/sysrq-trigger"); }
+static void KillAllProcesses() {
+    WriteStringToFile("i", PROC_SYSRQ);
+}
+
+// Create reboot/shutdwon monitor thread
+void RebootMonitorThread(unsigned int cmd, const std::string& rebootTarget, sem_t* reboot_semaphore,
+                         std::chrono::milliseconds shutdown_timeout, bool* reboot_monitor_run) {
+    unsigned int remaining_shutdown_time = 0;
+
+    // 30 seconds more than the timeout passed to the thread as there is a final Umount pass
+    // after the timeout is reached.
+    constexpr unsigned int shutdown_watchdog_timeout_default = 30;
+    auto shutdown_watchdog_timeout = android::base::GetUintProperty(
+            "ro.build.shutdown.watchdog.timeout", shutdown_watchdog_timeout_default);
+    remaining_shutdown_time = shutdown_watchdog_timeout + shutdown_timeout.count() / 1000;
+
+    while (*reboot_monitor_run == true) {
+        if (TEMP_FAILURE_RETRY(sem_wait(reboot_semaphore)) == -1) {
+            LOG(ERROR) << "sem_wait failed and exit RebootMonitorThread()";
+            return;
+        }
+
+        timespec shutdown_timeout_timespec;
+        if (clock_gettime(CLOCK_MONOTONIC, &shutdown_timeout_timespec) == -1) {
+            LOG(ERROR) << "clock_gettime() fail! exit RebootMonitorThread()";
+            return;
+        }
+
+        // If there are some remaining shutdown time left from previous round, we use
+        // remaining time here.
+        shutdown_timeout_timespec.tv_sec += remaining_shutdown_time;
+
+        LOG(INFO) << "shutdown_timeout_timespec.tv_sec: " << shutdown_timeout_timespec.tv_sec;
+
+        int sem_return = 0;
+        while ((sem_return = sem_timedwait_monotonic_np(reboot_semaphore,
+                                                        &shutdown_timeout_timespec)) == -1 &&
+               errno == EINTR) {
+        }
+
+        if (sem_return == -1) {
+            LOG(ERROR) << "Reboot thread timed out";
+
+            if (android::base::GetBoolProperty("ro.debuggable", false) == true) {
+                LOG(INFO) << "Try to dump init process call trace:";
+                const char* vdc_argv[] = {"/system/bin/debuggerd", "-b", "1"};
+                int status;
+                android_fork_execvp_ext(arraysize(vdc_argv), (char**)vdc_argv, &status, true,
+                                        LOG_KLOG, true, nullptr, nullptr, 0);
+
+                LOG(INFO) << "Show stack for all active CPU:";
+                WriteStringToFile("l", PROC_SYSRQ);
+
+                LOG(INFO) << "Show tasks that are in disk sleep(uninterruptable sleep), which are "
+                             "like "
+                             "blocked in mutex or hardware register access:";
+                WriteStringToFile("w", PROC_SYSRQ);
+            }
+
+            // In shutdown case,notify kernel to sync and umount fs to read-only before shutdown.
+            if (cmd == ANDROID_RB_POWEROFF || cmd == ANDROID_RB_THERMOFF) {
+                WriteStringToFile("s", PROC_SYSRQ);
+
+                WriteStringToFile("u", PROC_SYSRQ);
+
+                RebootSystem(cmd, rebootTarget);
+            }
+
+            LOG(ERROR) << "Trigger crash at last!";
+            WriteStringToFile("c", PROC_SYSRQ);
+        } else {
+            timespec current_time_timespec;
+
+            if (clock_gettime(CLOCK_MONOTONIC, &current_time_timespec) == -1) {
+                LOG(ERROR) << "clock_gettime() fail! exit RebootMonitorThread()";
+                return;
+            }
+
+            remaining_shutdown_time =
+                    shutdown_timeout_timespec.tv_sec - current_time_timespec.tv_sec;
+
+            LOG(INFO) << "remaining_shutdown_time: " << remaining_shutdown_time;
+        }
+    }
+}
 
 /* Try umounting all emulated file systems R/W block device cfile systems.
  * This will just try umount and give it up if it fails.
@@ -300,7 +346,8 @@
  *
  * return true when umount was successful. false when timed out.
  */
-static UmountStat TryUmountAndFsck(bool runFsck, std::chrono::milliseconds timeout) {
+static UmountStat TryUmountAndFsck(unsigned int cmd, const std::string& rebootTarget, bool runFsck,
+                                   std::chrono::milliseconds timeout, sem_t* reboot_semaphore) {
     Timer t;
     std::vector<MountEntry> block_devices;
     std::vector<MountEntry> emulated_devices;
@@ -312,25 +359,80 @@
     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(true);
+        if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo();
         KillAllProcesses();
         // even if it succeeds, still it is timeout and do not run fsck with all processes killed
         UmountStat st = UmountPartitions(0ms);
-        if ((st != UMOUNT_STAT_SUCCESS) && DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(false);
+        if ((st != UMOUNT_STAT_SUCCESS) && DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo();
     }
 
     if (stat == UMOUNT_STAT_SUCCESS && runFsck) {
+        LOG(INFO) << "Pause reboot monitor thread before fsck";
+        sem_post(reboot_semaphore);
+
         // fsck part is excluded from timeout check. It only runs for user initiated shutdown
         // and should not affect reboot time.
         for (auto& entry : block_devices) {
             entry.DoFsck();
         }
+
+        LOG(INFO) << "Resume reboot monitor thread after fsck";
+        sem_post(reboot_semaphore);
     }
     return stat;
 }
 
-void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
-              bool runFsck) {
+// zram is able to use backing device on top of a loopback device.
+// In order to unmount /data successfully, we have to kill the loopback device first
+#define ZRAM_DEVICE   "/dev/block/zram0"
+#define ZRAM_RESET    "/sys/block/zram0/reset"
+#define ZRAM_BACK_DEV "/sys/block/zram0/backing_dev"
+static void KillZramBackingDevice() {
+    std::string backing_dev;
+    if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) return;
+
+    if (!android::base::StartsWith(backing_dev, "/dev/block/loop")) return;
+
+    // cut the last "\n"
+    backing_dev.erase(backing_dev.length() - 1);
+
+    // shutdown zram handle
+    Timer swap_timer;
+    LOG(INFO) << "swapoff() start...";
+    if (swapoff(ZRAM_DEVICE) == -1) {
+        LOG(ERROR) << "zram_backing_dev: swapoff (" << backing_dev << ")" << " failed";
+        return;
+    }
+    LOG(INFO) << "swapoff() took " << swap_timer;;
+
+    if (!WriteStringToFile("1", ZRAM_RESET)) {
+        LOG(ERROR) << "zram_backing_dev: reset (" << backing_dev << ")" << " failed";
+        return;
+    }
+
+    // clear loopback device
+    unique_fd loop(TEMP_FAILURE_RETRY(open(backing_dev.c_str(), O_RDWR | O_CLOEXEC)));
+    if (loop.get() < 0) {
+        LOG(ERROR) << "zram_backing_dev: open(" << backing_dev << ")" << " failed";
+        return;
+    }
+
+    if (ioctl(loop.get(), LOOP_CLR_FD, 0) < 0) {
+        LOG(ERROR) << "zram_backing_dev: loop_clear (" << backing_dev << ")" << " failed";
+        return;
+    }
+    LOG(INFO) << "zram_backing_dev: `" << backing_dev << "` is cleared successfully.";
+}
+
+//* Reboot / shutdown the system.
+// cmd ANDROID_RB_* as defined in android_reboot.h
+// 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.
+//
+static 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;
 
@@ -352,8 +454,8 @@
     if (!SHUTDOWN_ZERO_TIMEOUT) {
         constexpr unsigned int shutdown_timeout_default = 6;
         constexpr unsigned int max_thermal_shutdown_timeout = 3;
-        auto shutdown_timeout_final =
-            android::base::GetUintProperty("ro.build.shutdown_timeout", shutdown_timeout_default);
+        auto shutdown_timeout_final = android::base::GetUintProperty("ro.build.shutdown_timeout",
+                                                                     shutdown_timeout_default);
         if (is_thermal_shutdown && shutdown_timeout_final > max_thermal_shutdown_timeout) {
             shutdown_timeout_final = max_thermal_shutdown_timeout;
         }
@@ -361,6 +463,23 @@
     }
     LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms";
 
+    sem_t reboot_semaphore;
+    if (sem_init(&reboot_semaphore, false, 0) == -1) {
+        // These should never fail, but if they do, skip the graceful reboot and reboot immediately.
+        LOG(ERROR) << "sem_init() fail and RebootSystem() return!";
+        RebootSystem(cmd, rebootTarget);
+    }
+
+    // Start a thread to monitor init shutdown process
+    LOG(INFO) << "Create reboot monitor thread.";
+    bool reboot_monitor_run = true;
+    std::thread reboot_monitor_thread(&RebootMonitorThread, cmd, rebootTarget, &reboot_semaphore,
+                                      shutdown_timeout, &reboot_monitor_run);
+    reboot_monitor_thread.detach();
+
+    // Start reboot monitor thread
+    sem_post(&reboot_semaphore);
+
     // 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.
@@ -391,9 +510,31 @@
     Service* bootAnim = ServiceList::GetInstance().FindService("bootanim");
     Service* surfaceFlinger = ServiceList::GetInstance().FindService("surfaceflinger");
     if (bootAnim != nullptr && surfaceFlinger != nullptr && surfaceFlinger->IsRunning()) {
-        // will not check animation class separately
+        bool do_shutdown_animation = GetBoolProperty("ro.init.shutdown_animation", false);
+
+        if (do_shutdown_animation) {
+            property_set("service.bootanim.exit", "0");
+            // Could be in the middle of animation. Stop and start so that it can pick
+            // up the right mode.
+            bootAnim->Stop();
+        }
+
         for (const auto& service : ServiceList::GetInstance()) {
-            if (service->classnames().count("animation")) service->SetShutdownCritical();
+            if (service->classnames().count("animation") == 0) {
+                continue;
+            }
+
+            // start all animation classes if stopped.
+            if (do_shutdown_animation) {
+                service->Start();
+            }
+            service->SetShutdownCritical();  // will not check animation class separately
+        }
+
+        if (do_shutdown_animation) {
+            bootAnim->Start();
+            surfaceFlinger->SetShutdownCritical();
+            bootAnim->SetShutdownCritical();
         }
     }
 
@@ -442,6 +583,7 @@
     for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
         if (!s->IsShutdownCritical()) s->Stop();
     }
+    SubcontextTerminate();
     ReapAnyOutstandingChildren();
 
     // 3. send volume shutdown to vold
@@ -457,12 +599,31 @@
         if (kill_after_apps.count(s->name())) s->Stop();
     }
     // 4. sync, try umount, and optionally run fsck for user shutdown
-    sync();
-    UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
+    {
+        Timer sync_timer;
+        LOG(INFO) << "sync() before umount...";
+        sync();
+        LOG(INFO) << "sync() before umount took" << sync_timer;
+    }
+    // 5. drop caches and disable zram backing device, if exist
+    KillZramBackingDevice();
+
+    UmountStat stat = TryUmountAndFsck(cmd, rebootTarget, runFsck, shutdown_timeout - t.duration(),
+                                       &reboot_semaphore);
     // Follow what linux shutdown is doing: one more sync with little bit delay
-    sync();
+    {
+        Timer sync_timer;
+        LOG(INFO) << "sync() after umount...";
+        sync();
+        LOG(INFO) << "sync() after umount took" << sync_timer;
+    }
     if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
     LogShutdownTime(stat, &t);
+
+    // Send signal to terminate reboot monitor thread.
+    reboot_monitor_run = false;
+    sem_post(&reboot_semaphore);
+
     // Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
     RebootSystem(cmd, rebootTarget);
     abort();
@@ -495,6 +656,12 @@
         cmd = ANDROID_RB_RESTART2;
         if (cmd_params.size() >= 2) {
             reboot_target = cmd_params[1];
+            // adb reboot fastboot should boot into bootloader for devices not
+            // supporting logical partitions.
+            if (reboot_target == "fastboot" &&
+                !android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
+                reboot_target = "bootloader";
+            }
             // When rebooting to the bootloader notify the bootloader writing
             // also the BCB.
             if (reboot_target == "bootloader") {
@@ -504,7 +671,21 @@
                                   "bootloader_message: "
                                << err;
                 }
+            } else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
+                       reboot_target == "fastboot") {
+                std::string arg = reboot_target == "sideload-auto-reboot" ? "sideload_auto_reboot"
+                                                                          : reboot_target;
+                const std::vector<std::string> options = {
+                        "--" + arg,
+                };
+                std::string err;
+                if (!write_bootloader_message(options, &err)) {
+                    LOG(ERROR) << "Failed to set bootloader message: " << err;
+                    return false;
+                }
+                reboot_target = "recovery";
             }
+
             // If there is an additional parameter, pass it along
             if ((cmd_params.size() == 3) && cmd_params[2].size()) {
                 reboot_target += "," + cmd_params[2];
@@ -525,7 +706,7 @@
     // Queue built-in shutdown_done
     auto shutdown_handler = [cmd, command, reboot_target, run_fsck](const BuiltinArguments&) {
         DoReboot(cmd, command, reboot_target, run_fsck);
-        return Success();
+        return Result<void>{};
     };
     ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
 
diff --git a/init/reboot.h b/init/reboot.h
index 1c58bd1..07dcb6e 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -22,26 +22,9 @@
 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", "shutdown,userrequested"
- * rebootTarget Reboot target string like "bootloader". Otherwise, it should be an
- *              empty string.
- * runFsck Whether to run fsck after umount is done.
- */
-void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
-              bool runFsck) __attribute__((__noreturn__));
-
 // 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/reboot_utils.cpp b/init/reboot_utils.cpp
new file mode 100644
index 0000000..d1a712f
--- /dev/null
+++ b/init/reboot_utils.cpp
@@ -0,0 +1,165 @@
+/*
+ * 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 <sys/capability.h>
+#include <sys/reboot.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "android-base/file.h"
+#include "android-base/logging.h"
+#include "android-base/strings.h"
+#include "backtrace/Backtrace.h"
+#include "cutils/android_reboot.h"
+
+#include "capabilities.h"
+
+namespace android {
+namespace init {
+
+static std::string init_fatal_reboot_target = "bootloader";
+
+void SetFatalRebootTarget() {
+    std::string cmdline;
+    android::base::ReadFileToString("/proc/cmdline", &cmdline);
+    cmdline = android::base::Trim(cmdline);
+
+    const char kRebootTargetString[] = "androidboot.init_fatal_reboot_target=";
+    auto start_pos = cmdline.find(kRebootTargetString);
+    if (start_pos == std::string::npos) {
+        return;  // We already default to bootloader if no setting is provided.
+    }
+    start_pos += sizeof(kRebootTargetString) - 1;
+
+    auto end_pos = cmdline.find(' ', start_pos);
+    // if end_pos isn't found, then we've run off the end, but this is okay as this is the last
+    // entry, and -1 is a valid size for string::substr();
+    auto size = end_pos == std::string::npos ? -1 : end_pos - start_pos;
+    init_fatal_reboot_target = cmdline.substr(start_pos, size);
+}
+
+bool IsRebootCapable() {
+    if (!CAP_IS_SUPPORTED(CAP_SYS_BOOT)) {
+        PLOG(WARNING) << "CAP_SYS_BOOT is not supported";
+        return true;
+    }
+
+    ScopedCaps caps(cap_get_proc());
+    if (!caps) {
+        PLOG(WARNING) << "cap_get_proc() failed";
+        return true;
+    }
+
+    cap_flag_value_t value = CAP_SET;
+    if (cap_get_flag(caps.get(), CAP_SYS_BOOT, CAP_EFFECTIVE, &value) != 0) {
+        PLOG(WARNING) << "cap_get_flag(CAP_SYS_BOOT, EFFECTIVE) failed";
+        return true;
+    }
+    return value == CAP_SET;
+}
+
+void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
+    LOG(INFO) << "Reboot ending, jumping to kernel";
+
+    if (!IsRebootCapable()) {
+        // On systems where init does not have the capability of rebooting the
+        // device, just exit cleanly.
+        exit(0);
+    }
+
+    switch (cmd) {
+        case ANDROID_RB_POWEROFF:
+            reboot(RB_POWER_OFF);
+            break;
+
+        case ANDROID_RB_RESTART2:
+            syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
+                    LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
+            break;
+
+        case ANDROID_RB_THERMOFF:
+            reboot(RB_POWER_OFF);
+            break;
+    }
+    // In normal case, reboot should not return.
+    PLOG(ERROR) << "reboot call returned";
+    abort();
+}
+
+void __attribute__((noreturn)) InitFatalReboot() {
+    auto pid = fork();
+
+    if (pid == -1) {
+        // Couldn't fork, don't even try to backtrace, just reboot.
+        RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
+    } else if (pid == 0) {
+        // Fork a child for safety, since we always want to shut down if something goes wrong, but
+        // its worth trying to get the backtrace, even in the signal handler, since typically it
+        // does work despite not being async-signal-safe.
+        sleep(5);
+        RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
+    }
+
+    // In the parent, let's try to get a backtrace then shutdown.
+    std::unique_ptr<Backtrace> backtrace(
+            Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+    if (!backtrace->Unwind(0)) {
+        LOG(ERROR) << __FUNCTION__ << ": Failed to unwind callstack.";
+    }
+    for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+        LOG(ERROR) << backtrace->FormatFrameData(i);
+    }
+    RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
+}
+
+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
+    // recover.
+    struct sigaction action;
+    memset(&action, 0, sizeof(action));
+    sigfillset(&action.sa_mask);
+    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.
+        InitFatalReboot();
+    };
+    action.sa_flags = SA_RESTART;
+    sigaction(SIGABRT, &action, nullptr);
+    sigaction(SIGBUS, &action, nullptr);
+    sigaction(SIGFPE, &action, nullptr);
+    sigaction(SIGILL, &action, nullptr);
+    sigaction(SIGSEGV, &action, nullptr);
+#if defined(SIGSTKFLT)
+    sigaction(SIGSTKFLT, &action, nullptr);
+#endif
+    sigaction(SIGSYS, &action, nullptr);
+    sigaction(SIGTRAP, &action, nullptr);
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/reboot_utils.h b/init/reboot_utils.h
new file mode 100644
index 0000000..3fd969e
--- /dev/null
+++ b/init/reboot_utils.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <string>
+
+namespace android {
+namespace init {
+
+void SetFatalRebootTarget();
+// 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();
+// This is a wrapper around the actual reboot calls.
+void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& reboot_target);
+void __attribute__((noreturn)) InitFatalReboot();
+void InstallRebootSignalHandlers();
+
+}  // namespace init
+}  // namespace android
diff --git a/init/result.h b/init/result.h
index fc03962..b70dd1b 100644
--- a/init/result.h
+++ b/init/result.h
@@ -14,198 +14,16 @@
  * 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.
+#pragma once
 
-// 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>.
+// The implementation of this file has moved to android-base.  This file remains since historically,
+// these classes were a part of init.
 
-// 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);
+#include <android-base/result.h>
 
-#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
+using android::base::ErrnoError;
+using android::base::ErrnoErrorf;
+using android::base::Error;
+using android::base::Errorf;
+using android::base::Result;
+using android::base::ResultError;
diff --git a/init/result_test.cpp b/init/result_test.cpp
deleted file mode 100644
index 327b444..0000000
--- a/init/result_test.cpp
+++ /dev/null
@@ -1,333 +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 "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
index fe1d6a7..476a46a 100644
--- a/init/rlimit_parser.cpp
+++ b/init/rlimit_parser.cpp
@@ -65,13 +65,19 @@
     }
 
     rlimit limit;
-    if (!ParseUint(args[2], &limit.rlim_cur)) {
+    if (args[2] == "-1" || args[2] == "unlimited") {
+        limit.rlim_cur = RLIM_INFINITY;
+    } else if (!ParseUint(args[2], &limit.rlim_cur)) {
         return Error() << "Could not parse soft limit '" << args[2] << "'";
     }
-    if (!ParseUint(args[3], &limit.rlim_max)) {
+
+    if (args[3] == "-1" || args[3] == "unlimited") {
+        limit.rlim_max = RLIM_INFINITY;
+    } else if (!ParseUint(args[3], &limit.rlim_max)) {
         return Error() << "Could not parse hard limit '" << args[3] << "'";
     }
-    return {resource, limit};
+
+    return std::pair{resource, limit};
 }
 
 }  // namespace init
diff --git a/init/rlimit_parser_test.cpp b/init/rlimit_parser_test.cpp
index f3f9eb4..6a16d3b 100644
--- a/init/rlimit_parser_test.cpp
+++ b/init/rlimit_parser_test.cpp
@@ -43,64 +43,64 @@
     auto result = ParseRlimit(input);
 
     ASSERT_FALSE(result) << "input: " << input[1];
-    EXPECT_EQ(expected_result, result.error_string());
-    EXPECT_EQ(0, result.error_errno());
+    EXPECT_EQ(expected_result, result.error().message());
+    EXPECT_EQ(0, result.error().code());
 }
 
 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}}},
+            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}}},
+                    {{"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}}},
-        };
+                    {{"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", "unlimited", "10"}, {12, {RLIM_INFINITY, 10}}},
+                    {{"13", "-1", "10"}, {13, {RLIM_INFINITY, 10}}},
+                    {{"14", "10", "unlimited"}, {14, {10, RLIM_INFINITY}}},
+                    {{"15", "10", "-1"}, {15, {10, RLIM_INFINITY}}},
+            };
 
     for (const auto& [input, expected_result] : inputs_and_results) {
         TestRlimitSuccess(input, expected_result);
@@ -109,12 +109,16 @@
 
 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'"},
+            {{"-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'"},
+            {{"cpu", "unlimit", "10"}, "Could not parse soft limit 'unlimit'"},
+            {{"cpu", "10", "unlimit"}, "Could not parse hard limit 'unlimit'"},
+            {{"cpu", "-2", "10"}, "Could not parse soft limit '-2'"},
+            {{"cpu", "10", "-2"}, "Could not parse hard limit '-2'"},
     };
 
     for (const auto& [input, expected_result] : inputs_and_results) {
diff --git a/init/security.cpp b/init/security.cpp
index a3494a2..5d87f3c 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -43,14 +43,14 @@
 // 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&) {
+Result<void> 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 {};
         }
         return ErrnoError() << "Failed to open /dev/hw_random";
     }
@@ -80,7 +80,7 @@
     }
 
     LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
-    return Success();
+    return {};
 }
 
 static bool SetHighestAvailableOptionValue(std::string path, int min, int max) {
@@ -147,31 +147,31 @@
 // 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&) {
+Result<void> SetMmapRndBitsAction(const BuiltinArguments&) {
 // values are arch-dependent
 #if defined(USER_MODE_LINUX)
     // uml does not support mmap_rnd_bits
-    return Success();
+    return {};
 #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();
+        return {};
     }
 #elif defined(__x86_64__)
     // x86_64 supports 28 - 32 bits
     if (SetMmapRndBitsMin(32, 32, false) && SetMmapRndBitsMin(16, 16, true)) {
-        return Success();
+        return {};
     }
 #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();
+        return {};
     }
 #elif defined(__mips__) || defined(__mips64__)
     // TODO: add mips support b/27788820
-    return Success();
+    return {};
 #else
     LOG(ERROR) << "Unknown architecture";
 #endif
@@ -187,14 +187,14 @@
 // Set kptr_restrict to the highest available level.
 //
 // Aborts if unable to set this to an acceptable value.
-Result<Success> SetKptrRestrictAction(const BuiltinArguments&) {
+Result<void> 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();
+    return {};
 }
 
 }  // namespace init
diff --git a/init/security.h b/init/security.h
index 6f6b944..b081a05 100644
--- a/init/security.h
+++ b/init/security.h
@@ -26,9 +26,9 @@
 namespace android {
 namespace init {
 
-Result<Success> MixHwrngIntoLinuxRngAction(const BuiltinArguments&);
-Result<Success> SetMmapRndBitsAction(const BuiltinArguments&);
-Result<Success> SetKptrRestrictAction(const BuiltinArguments&);
+Result<void> MixHwrngIntoLinuxRngAction(const BuiltinArguments&);
+Result<void> SetMmapRndBitsAction(const BuiltinArguments&);
+Result<void> SetKptrRestrictAction(const BuiltinArguments&);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/selabel.cpp b/init/selabel.cpp
new file mode 100644
index 0000000..daeb832
--- /dev/null
+++ b/init/selabel.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "selabel.h"
+
+#include <selinux/android.h>
+
+namespace android {
+namespace init {
+
+namespace {
+
+selabel_handle* sehandle = nullptr;
+}
+
+// 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/selabel.h b/init/selabel.h
new file mode 100644
index 0000000..5d590b2
--- /dev/null
+++ b/init/selabel.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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>
+
+namespace android {
+namespace init {
+
+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
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 0ba5c4a..54be086 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -18,8 +18,8 @@
 // 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.
+// Init loads the SEPolicy from the file system, restores the context of /system/bin/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.
 
@@ -34,19 +34,22 @@
 // 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.
+// 1) There is a precompiled SEPolicy located at either /vendor/etc/selinux/precompiled_sepolicy or
+//    /odm/etc/selinux/precompiled_sepolicy if odm parition is present.  Stored along with this file
+//    are the sha256 hashes of the parts of the SEPolicy on /system and /product 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.  Symmetrically, product paritition contains a
+//    sha256 of its SEPolicy.  System loads this precompiled_sepolicy directly if and only if hashes
+//    for system policy match and hashes for product policy match.
+// 2) If these hashes do not match, then either /system or /product (or both) have 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 <android/api-level.h>
 #include <fcntl.h>
 #include <stdlib.h>
 #include <sys/wait.h>
@@ -57,22 +60,25 @@
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/unique_fd.h>
+#include <fs_avb/fs_avb.h>
 #include <selinux/android.h>
 
-#include "log.h"
+#include "debug_ramdisk.h"
+#include "reboot_utils.h"
 #include "util.h"
 
+using namespace std::string_literals;
+
 using android::base::ParseInt;
 using android::base::Timer;
 using android::base::unique_fd;
+using android::fs_mgr::AvbHandle;
 
 namespace android {
 namespace init {
 
 namespace {
 
-selabel_handle* sehandle = nullptr;
-
 enum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
 
 EnforcingStatus StatusFromCmdline() {
@@ -215,20 +221,35 @@
         return false;
     }
     std::string actual_plat_id;
-    if (!ReadFirstLine("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256", &actual_plat_id)) {
+    if (!ReadFirstLine("/system/etc/selinux/plat_sepolicy_and_mapping.sha256", &actual_plat_id)) {
         PLOG(INFO) << "Failed to read "
-                      "/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
+                      "/system/etc/selinux/plat_sepolicy_and_mapping.sha256";
+        return false;
+    }
+    std::string actual_product_id;
+    if (!ReadFirstLine("/product/etc/selinux/product_sepolicy_and_mapping.sha256",
+                       &actual_product_id)) {
+        PLOG(INFO) << "Failed to read "
+                      "/product/etc/selinux/product_sepolicy_and_mapping.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;
+    std::string precompiled_plat_sha256 = *file + ".plat_sepolicy_and_mapping.sha256";
+    if (!ReadFirstLine(precompiled_plat_sha256.c_str(), &precompiled_plat_id)) {
+        PLOG(INFO) << "Failed to read " << precompiled_plat_sha256;
         file->clear();
         return false;
     }
-    if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
+    std::string precompiled_product_id;
+    std::string precompiled_product_sha256 = *file + ".product_sepolicy_and_mapping.sha256";
+    if (!ReadFirstLine(precompiled_product_sha256.c_str(), &precompiled_product_id)) {
+        PLOG(INFO) << "Failed to read " << precompiled_product_sha256;
+        file->clear();
+        return false;
+    }
+    if (actual_plat_id.empty() || actual_plat_id != precompiled_plat_id ||
+        actual_product_id.empty() || actual_product_id != precompiled_product_id) {
         file->clear();
         return false;
     }
@@ -263,10 +284,21 @@
     // secilc is invoked to compile the above three policy files into a single monolithic policy
     // file. This file is then loaded into the kernel.
 
+    // See if we need to load userdebug_plat_sepolicy.cil instead of plat_sepolicy.cil.
+    const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
+    bool use_userdebug_policy =
+            ((force_debuggable_env && "true"s == force_debuggable_env) &&
+             AvbHandle::IsDeviceUnlocked() && access(kDebugRamdiskSEPolicy, F_OK) == 0);
+    if (use_userdebug_policy) {
+        LOG(WARNING) << "Using userdebug system sepolicy";
+    }
+
     // 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)) {
+    // use_userdebug_policy requires compiling sepolicy with userdebug_plat_sepolicy.cil.
+    // Thus it cannot use the precompiled policy from vendor image.
+    if (!use_userdebug_policy && 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) {
@@ -280,14 +312,6 @@
 
     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";
@@ -302,7 +326,23 @@
     if (!GetVendorMappingVersion(&vend_plat_vers)) {
         return false;
     }
-    std::string mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
+    std::string plat_mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
+
+    std::string plat_compat_cil_file("/system/etc/selinux/mapping/" + vend_plat_vers +
+                                     ".compat.cil");
+    if (access(plat_compat_cil_file.c_str(), F_OK) == -1) {
+        plat_compat_cil_file.clear();
+    }
+
+    std::string product_policy_cil_file("/product/etc/selinux/product_sepolicy.cil");
+    if (access(product_policy_cil_file.c_str(), F_OK) == -1) {
+        product_policy_cil_file.clear();
+    }
+
+    std::string product_mapping_file("/product/etc/selinux/mapping/" + vend_plat_vers + ".cil");
+    if (access(product_mapping_file.c_str(), F_OK) == -1) {
+        product_mapping_file.clear();
+    }
 
     // vendor_sepolicy.cil and plat_pub_versioned.cil are the new design to replace
     // nonplat_sepolicy.cil.
@@ -324,22 +364,30 @@
     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);
+    const std::string version_as_string = std::to_string(SEPOLICY_VERSION);
 
     // clang-format off
     std::vector<const char*> compile_args {
         "/system/bin/secilc",
-        plat_policy_cil_file,
+        use_userdebug_policy ? kDebugRamdiskSEPolicy: 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(),
+        plat_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_compat_cil_file.empty()) {
+        compile_args.push_back(plat_compat_cil_file.c_str());
+    }
+    if (!product_policy_cil_file.empty()) {
+        compile_args.push_back(product_policy_cil_file.c_str());
+    }
+    if (!product_mapping_file.empty()) {
+        compile_args.push_back(product_mapping_file.c_str());
+    }
     if (!plat_pub_versioned_cil_file.empty()) {
         compile_args.push_back(plat_pub_versioned_cil_file.c_str());
     }
@@ -379,11 +427,7 @@
     return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
 }
 
-}  // namespace
-
 void SelinuxInitialize() {
-    Timer t;
-
     LOG(INFO) << "Loading SELinux policy";
     if (!LoadPolicy()) {
         LOG(FATAL) << "Unable to load SELinux policy";
@@ -400,11 +444,10 @@
     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);
 }
 
+}  // namespace
+
 // 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.
@@ -415,118 +458,98 @@
     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("/apex", 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 = selinux_klog_callback;
+    cb.func_log = SelinuxKlogCallback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
 }
 
-// This function checks whether the sepolicy supports vendor init.
-bool SelinuxHasVendorInit() {
+// This function returns the Android version with which the vendor SEPolicy was compiled.
+// It is used for version checks such as whether or not vendor_init should be used
+int SelinuxGetVendorAndroidVersion() {
     if (!IsSplitPolicyDevice()) {
-        // If this device does not split sepolicy files, vendor_init will be available in the latest
-        // monolithic sepolicy file.
-        return true;
+        // If this device does not split sepolicy files, it's not a Treble device and therefore,
+        // we assume it's always on the latest platform.
+        return __ANDROID_API_FUTURE__;
     }
 
     std::string version;
     if (!GetVendorMappingVersion(&version)) {
-        // Return true as the default if we failed to load the vendor sepolicy version.
-        return true;
+        LOG(FATAL) << "Could not read vendor SELinux version";
     }
 
     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;
+        PLOG(FATAL) << "Failed to parse the vendor sepolicy major version " << major_version_str;
     }
 
-    return major_version >= 28;
+    return major_version;
 }
 
-// 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);
-}
+// This function initializes SELinux then execs init to run in the init SELinux context.
+int SetupSelinux(char** argv) {
+    InitKernelLogging(argv);
 
-// 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;
+    if (REBOOT_BOOTLOADER_ON_PANIC) {
+        InstallRebootSignalHandlers();
     }
-    *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();
+    boot_clock::time_point start_time = boot_clock::now();
 
-    if (!sehandle) return true;
+    // Set up SELinux, loading the SELinux policy.
+    SelinuxSetupKernelLogging();
+    SelinuxInitialize();
 
-    std::vector<const char*> c_aliases;
-    for (const auto& alias : aliases) {
-        c_aliases.emplace_back(alias.c_str());
+    // We're in the kernel domain and want to transition to the init domain.  File systems that
+    // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
+    // but other file systems do.  In particular, this is needed for ramdisks such as the
+    // recovery image for A/B devices.
+    if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
+        PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
     }
-    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;
+    setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);
+
+    const char* path = "/system/bin/init";
+    const char* args[] = {path, "second_stage", nullptr};
+    execv(path, const_cast<char**>(args));
+
+    // execv() only returns if an error happened, in which case we
+    // panic and never return from this function.
+    PLOG(FATAL) << "execv(\"" << path << "\") failed";
+
+    return 1;
 }
 
 }  // namespace init
diff --git a/init/selinux.h b/init/selinux.h
index 30069b5..63ad470 100644
--- a/init/selinux.h
+++ b/init/selinux.h
@@ -14,28 +14,18 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_SELINUX_H
-#define _INIT_SELINUX_H
-
-#include <string>
-#include <vector>
+#pragma once
 
 namespace android {
 namespace init {
 
-void SelinuxInitialize();
+int SetupSelinux(char** argv);
 void SelinuxRestoreContext();
 
 void SelinuxSetupKernelLogging();
-bool SelinuxHasVendorInit();
+int SelinuxGetVendorAndroidVersion();
 
-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);
+static constexpr char kEnvSelinuxStartedAt[] = "SELINUX_STARTED_AT";
 
 }  // namespace init
 }  // namespace android
-
-#endif
diff --git a/init/service.cpp b/init/service.cpp
index 37d3a88..f95b675 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -20,35 +20,27 @@
 #include <inttypes.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/time.h>
-#include <sys/wait.h>
 #include <termios.h>
 #include <unistd.h>
 
 #include <android-base/file.h>
 #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 <hidl-util/FQName.h>
 #include <processgroup/processgroup.h>
 #include <selinux/selinux.h>
-#include <system/thread_defs.h>
 
-#include "rlimit_parser.h"
+#include "service_list.h"
 #include "util.h"
 
 #if defined(__ANDROID__)
-#include <sys/system_properties.h>
+#include <ApexProperties.sysprop.h>
 
-#include <android-base/properties.h>
-
-#include "init.h"
+#include "mount_namespace.h"
 #include "property_service.h"
 #else
 #include "host_init_stubs.h"
@@ -57,10 +49,8 @@
 using android::base::boot_clock;
 using android::base::GetProperty;
 using android::base::Join;
-using android::base::ParseInt;
 using android::base::StartsWith;
 using android::base::StringPrintf;
-using android::base::unique_fd;
 using android::base::WriteStringToFile;
 
 namespace android {
@@ -102,88 +92,7 @@
     return computed_context;
 }
 
-Result<Success> Service::SetUpMountNamespace() const {
-    constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
-
-    // 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";
-    }
-
-    // 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", "sys", 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) {
-        return ErrnoError() << "Could not fork init inside the PID namespace";
-    }
-
-    if (child_pid > 0) {
-        // So that we exit with the right status.
-        static int init_exitstatus = 0;
-        signal(SIGTERM, [](int) { _exit(init_exitstatus); });
-
-        pid_t waited_pid;
-        int status;
-        while ((waited_pid = wait(&status)) > 0) {
-             // This loop will end when there are no processes left inside the
-             // PID namespace or when the init process inside the PID namespace
-             // gets a signal.
-            if (waited_pid == child_pid) {
-                init_exitstatus = status;
-            }
-        }
-        if (!WIFEXITED(init_exitstatus)) {
-            _exit(EXIT_FAILURE);
-        }
-        _exit(WEXITSTATUS(init_exitstatus));
-    }
-    return Success();
-}
-
-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) {
+static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigstop) {
     std::vector<std::string> expanded_args;
     std::vector<char*> c_strings;
 
@@ -197,41 +106,45 @@
     }
     c_strings.push_back(nullptr);
 
+    if (sigstop) {
+        kill(getpid(), SIGSTOP);
+    }
+
     return execv(c_strings[0], c_strings.data()) == 0;
 }
 
+static bool IsRuntimeApexReady() {
+    struct stat buf;
+    return stat("/apex/com.android.runtime/", &buf) == 0;
+}
+
 unsigned long Service::next_start_order_ = 1;
 bool Service::is_exec_service_running_ = false;
 
 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(name, 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,
-                 Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& args)
+                 const std::vector<gid_t>& supp_gids, int namespace_flags,
+                 const std::string& seclabel, Subcontext* subcontext_for_restart_commands,
+                 const std::vector<std::string>& args)
     : name_(name),
       classnames_({"default"}),
       flags_(flags),
       pid_(0),
       crash_count_(0),
-      uid_(uid),
-      gid_(gid),
-      supp_gids_(supp_gids),
-      capabilities_(capabilities),
-      namespace_flags_(namespace_flags),
+      proc_attr_{.ioprio_class = IoSchedClass_NONE,
+                 .ioprio_pri = 0,
+                 .uid = uid,
+                 .gid = gid,
+                 .supp_gids = supp_gids,
+                 .priority = 0},
+      namespaces_{.flags = namespace_flags},
       seclabel_(seclabel),
       onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0,
                  "onrestart", {}),
-      keychord_id_(0),
-      ioprio_class_(IoSchedClass_NONE),
-      ioprio_pri_(0),
-      priority_(0),
       oom_score_adjust_(-1000),
-      swappiness_(-1),
-      soft_limit_in_bytes_(-1),
-      limit_in_bytes_(-1),
       start_order_(0),
       args_(args) {}
 
@@ -263,24 +176,18 @@
                   << ") process group...";
         int r;
         if (signal == SIGTERM) {
-            r = killProcessGroupOnce(uid_, pid_, signal);
+            r = killProcessGroupOnce(proc_attr_.uid, pid_, signal);
         } else {
-            r = killProcessGroup(uid_, pid_, signal);
+            r = killProcessGroup(proc_attr_.uid, pid_, signal);
         }
 
         if (r == 0) process_cgroup_empty_ = true;
     }
 }
 
-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);
-        }
-    }
+void Service::SetProcessAttributesAndCaps() {
     // Keep capabilites on uid change.
-    if (capabilities_.any() && uid_) {
+    if (capabilities_ && proc_attr_.uid) {
         // If Android is running in a container, some securebits might already
         // be locked, so don't change those.
         unsigned long securebits = prctl(PR_GET_SECUREBITS);
@@ -293,37 +200,21 @@
         }
     }
 
-    // TODO: work out why this fails for `console` then upgrade to FATAL.
-    if (setpgid(0, getpid()) == -1) PLOG(ERROR) << "setpgid failed for " << name_;
+    if (auto result = SetProcessAttributes(proc_attr_); !result) {
+        LOG(FATAL) << "cannot set attribute for " << name_ << ": " << result.error();
+    }
 
-    if (gid_) {
-        if (setgid(gid_) != 0) {
-            PLOG(FATAL) << "setgid failed for " << name_;
-        }
-    }
-    if (setgroups(supp_gids_.size(), &supp_gids_[0]) != 0) {
-        PLOG(FATAL) << "setgroups failed for " << name_;
-    }
-    if (uid_) {
-        if (setuid(uid_) != 0) {
-            PLOG(FATAL) << "setuid failed for " << name_;
-        }
-    }
     if (!seclabel_.empty()) {
         if (setexeccon(seclabel_.c_str()) < 0) {
             PLOG(FATAL) << "cannot setexeccon('" << seclabel_ << "') for " << name_;
         }
     }
-    if (priority_ != 0) {
-        if (setpriority(PRIO_PROCESS, 0, priority_) != 0) {
-            PLOG(FATAL) << "setpriority failed for " << name_;
-        }
-    }
-    if (capabilities_.any()) {
-        if (!SetCapsForExec(capabilities_)) {
+
+    if (capabilities_) {
+        if (!SetCapsForExec(*capabilities_)) {
             LOG(FATAL) << "cannot set capabilities for " << name_;
         }
-    } else if (uid_) {
+    } else if (proc_attr_.uid) {
         // Inheritable caps can be non-zero when running in a container.
         if (!DropInheritableCaps()) {
             LOG(FATAL) << "cannot drop inheritable caps for " << name_;
@@ -354,7 +245,7 @@
 
     // Oneshot processes go into the disabled state on exit,
     // except when manually restarted.
-    if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART)) {
+    if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART) && !(flags_ & SVC_RESET)) {
         flags_ |= SVC_DISABLED;
     }
 
@@ -364,12 +255,30 @@
         return;
     }
 
-    // If we crash > 4 times in 4 minutes, reboot into recovery.
+#if defined(__ANDROID__)
+    static bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false);
+#else
+    static bool is_apex_updatable = false;
+#endif
+    const bool is_process_updatable = !pre_apexd_ && is_apex_updatable;
+
+    // If we crash > 4 times in 4 minutes or before boot_completed,
+    // reboot into bootloader or set crashing property
     boot_clock::time_point now = boot_clock::now();
-    if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {
-        if (now < time_crashed_ + 4min) {
+    if (((flags_ & SVC_CRITICAL) || is_process_updatable) && !(flags_ & SVC_RESTART)) {
+        bool boot_completed = android::base::GetBoolProperty("sys.boot_completed", false);
+        if (now < time_crashed_ + 4min || !boot_completed) {
             if (++crash_count_ > 4) {
-                LOG(FATAL) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
+                if (flags_ & SVC_CRITICAL) {
+                    // Aborts into bootloader
+                    LOG(FATAL) << "critical process '" << name_ << "' exited 4 times "
+                               << (boot_completed ? "in 4 minutes" : "before boot completed");
+                } else {
+                    LOG(ERROR) << "updatable process '" << name_ << "' exited 4 times "
+                               << (boot_completed ? "in 4 minutes" : "before boot completed");
+                    // Notifies update_verifier and apexd
+                    property_set("ro.init.updatable_crashing", "1");
+                }
             }
         } else {
             time_crashed_ = now;
@@ -395,377 +304,15 @@
                   [] (const auto& info) { LOG(INFO) << *info; });
 }
 
-Result<Success> Service::ParseCapabilities(const std::vector<std::string>& args) {
-    capabilities_ = 0;
 
-    if (!CapAmbientSupported()) {
-        return Error()
-               << "capabilities requested but the kernel does not support ambient capabilities";
+Result<void> Service::ExecStart() {
+    if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
+        // Don't delay the service for ExecStart() as the semantic is that
+        // the caller might depend on the side effect of the execution.
+        return Error() << "Cannot start an updatable service '" << name_
+                       << "' before configs from APEXes are all loaded";
     }
 
-    unsigned int last_valid_cap = GetLastValidCap();
-    if (last_valid_cap >= capabilities_.size()) {
-        LOG(WARNING) << "last valid run-time capability is larger than CAP_LAST_CAP";
-    }
-
-    for (size_t i = 1; i < args.size(); i++) {
-        const std::string& arg = args[i];
-        int res = LookupCap(arg);
-        if (res < 0) {
-            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) {
-            return Error() << StringPrintf("capability '%s' not supported by the kernel",
-                                           arg.c_str());
-        }
-        capabilities_[cap] = true;
-    }
-    return Success();
-}
-
-Result<Success> Service::ParseClass(const std::vector<std::string>& args) {
-    classnames_ = std::set<std::string>(args.begin() + 1, args.end());
-    return Success();
-}
-
-Result<Success> Service::ParseConsole(const std::vector<std::string>& args) {
-    flags_ |= SVC_CONSOLE;
-    console_ = args.size() > 1 ? "/dev/" + args[1] : "";
-    return Success();
-}
-
-Result<Success> Service::ParseCritical(const std::vector<std::string>& args) {
-    flags_ |= SVC_CRITICAL;
-    return Success();
-}
-
-Result<Success> Service::ParseDisabled(const std::vector<std::string>& args) {
-    flags_ |= SVC_DISABLED;
-    flags_ |= SVC_RC_DISABLED;
-    return Success();
-}
-
-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 = DecodeUid(args[n]);
-        if (!gid) {
-            return Error() << "Unable to decode GID for '" << args[n] << "': " << gid.error();
-        }
-        supp_gids_.emplace_back(*gid);
-    }
-    return Success();
-}
-
-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))) {
-        return Error() << StringPrintf("process priority value must be range %d - %d",
-                                       ANDROID_PRIORITY_HIGHEST, ANDROID_PRIORITY_LOWEST);
-    }
-    return Success();
-}
-
-Result<Success> Service::ParseInterface(const std::vector<std::string>& args) {
-    const std::string& interface_name = args[1];
-    const std::string& instance_name = args[2];
-
-    const FQName fq_name = FQName(interface_name);
-    if (!fq_name.isValid()) {
-        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)) {
-        return Error() << "priority value must be range 0 - 7";
-    }
-
-    if (args[1] == "rt") {
-        ioprio_class_ = IoSchedClass_RT;
-    } else if (args[1] == "be") {
-        ioprio_class_ = IoSchedClass_BE;
-    } else if (args[1] == "idle") {
-        ioprio_class_ = IoSchedClass_IDLE;
-    } else {
-        return Error() << "ioprio option usage: ioprio <rt|be|idle> <0-7>";
-    }
-
-    return Success();
-}
-
-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);
-        } else {
-            LOG(WARNING) << "ignoring invalid keycode: " << args[i];
-        }
-    }
-    return Success();
-}
-
-Result<Success> Service::ParseOneshot(const std::vector<std::string>& args) {
-    flags_ |= SVC_ONESHOT;
-    return Success();
-}
-
-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;
-    if (auto result = onrestart_.AddCommand(str_args, line); !result) {
-        return Error() << "cannot add Onrestart command: " << result.error();
-    }
-    return Success();
-}
-
-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;
-            // PID namespaces require mount namespaces.
-            namespace_flags_ |= CLONE_NEWNS;
-        } else if (args[i] == "mnt") {
-            namespace_flags_ |= CLONE_NEWNS;
-        } else {
-            return Error() << "namespace must be 'pid' or 'mnt'";
-        }
-    }
-    return Success();
-}
-
-Result<Success> Service::ParseOomScoreAdjust(const std::vector<std::string>& args) {
-    if (!ParseInt(args[1], &oom_score_adjust_, -1000, 1000)) {
-        return Error() << "oom_score_adjust value must be in range -1000 - +1000";
-    }
-    return Success();
-}
-
-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 Success();
-}
-
-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 Success();
-    }
-    return Error() << "Invalid shutdown option";
-}
-
-template <typename T>
-Result<Success> Service::AddDescriptor(const std::vector<std::string>& args) {
-    int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
-    Result<uid_t> uid = 0;
-    Result<gid_t> gid = 0;
-    std::string context = args.size() > 6 ? args[6] : "";
-
-    if (args.size() > 4) {
-        uid = DecodeUid(args[4]);
-        if (!uid) {
-            return Error() << "Unable to find UID for '" << args[4] << "': " << uid.error();
-        }
-    }
-
-    if (args.size() > 5) {
-        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 old =
-        std::find_if(descriptors_.begin(), descriptors_.end(),
-                     [&descriptor] (const auto& other) { return descriptor.get() == other.get(); });
-
-    if (old != descriptors_.end()) {
-        return Error() << "duplicate descriptor " << args[1] << " " << args[2];
-    }
-
-    descriptors_.emplace_back(std::move(descriptor));
-    return Success();
-}
-
-// name type perm [ uid gid context ]
-Result<Success> Service::ParseSocket(const std::vector<std::string>& args) {
-    if (!StartsWith(args[2], "dgram") && !StartsWith(args[2], "stream") &&
-        !StartsWith(args[2], "seqpacket")) {
-        return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket'";
-    }
-    return AddDescriptor<SocketInfo>(args);
-}
-
-// name type perm [ uid gid context ]
-Result<Success> Service::ParseFile(const std::vector<std::string>& args) {
-    if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
-        return Error() << "file type must be 'r', 'w' or 'rw'";
-    }
-    if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
-        return Error() << "file name must not be relative";
-    }
-    return AddDescriptor<FileInfo>(args);
-}
-
-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();
-    }
-    uid_ = *uid;
-    return Success();
-}
-
-Result<Success> Service::ParseWritepid(const std::vector<std::string>& args) {
-    writepid_files_.assign(args.begin() + 1, args.end());
-    return Success();
-}
-
-class Service::OptionParserMap : public KeywordMap<OptionParser> {
-  public:
-    OptionParserMap() {}
-
-  private:
-    const Map& map() const override;
-};
-
-const Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
-    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
-    // clang-format off
-    static const Map option_parsers = {
-        {"capabilities",
-                        {1,     kMax, &Service::ParseCapabilities}},
-        {"class",       {1,     kMax, &Service::ParseClass}},
-        {"console",     {0,     1,    &Service::ParseConsole}},
-        {"critical",    {0,     0,    &Service::ParseCritical}},
-        {"disabled",    {0,     0,    &Service::ParseDisabled}},
-        {"enter_namespace",
-                        {2,     2,    &Service::ParseEnterNamespace}},
-        {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
-        {"interface",   {2,     2,    &Service::ParseInterface}},
-        {"ioprio",      {2,     2,    &Service::ParseIoprio}},
-        {"priority",    {1,     1,    &Service::ParsePriority}},
-        {"keycodes",    {1,     kMax, &Service::ParseKeycodes}},
-        {"oneshot",     {0,     0,    &Service::ParseOneshot}},
-        {"onrestart",   {1,     kMax, &Service::ParseOnrestart}},
-        {"override",    {0,     0,    &Service::ParseOverride}},
-        {"oom_score_adjust",
-                        {1,     1,    &Service::ParseOomScoreAdjust}},
-        {"memcg.swappiness",
-                        {1,     1,    &Service::ParseMemcgSwappiness}},
-        {"memcg.soft_limit_in_bytes",
-                        {1,     1,    &Service::ParseMemcgSoftLimitInBytes}},
-        {"memcg.limit_in_bytes",
-                        {1,     1,    &Service::ParseMemcgLimitInBytes}},
-        {"namespace",   {1,     2,    &Service::ParseNamespace}},
-        {"rlimit",      {3,     3,    &Service::ParseProcessRlimit}},
-        {"seclabel",    {1,     1,    &Service::ParseSeclabel}},
-        {"setenv",      {2,     2,    &Service::ParseSetenv}},
-        {"shutdown",    {1,     1,    &Service::ParseShutdown}},
-        {"socket",      {3,     6,    &Service::ParseSocket}},
-        {"file",        {2,     2,    &Service::ParseFile}},
-        {"user",        {1,     1,    &Service::ParseUser}},
-        {"writepid",    {1,     kMax, &Service::ParseWritepid}},
-    };
-    // clang-format on
-    return option_parsers;
-}
-
-Result<Success> Service::ParseLine(const std::vector<std::string>& args) {
-    static const OptionParserMap parser_map;
-    auto parser = parser_map.FindFunction(args);
-
-    if (!parser) return parser.error();
-
-    return std::invoke(*parser, this, args);
-}
-
-Result<Success> Service::ExecStart() {
     flags_ |= SVC_ONESHOT;
 
     if (auto result = Start(); !result) {
@@ -775,14 +322,21 @@
     flags_ |= SVC_EXEC;
     is_exec_service_running_ = true;
 
-    LOG(INFO) << "SVC_EXEC pid " << pid_ << " (uid " << uid_ << " gid " << gid_ << "+"
-              << supp_gids_.size() << " context " << (!seclabel_.empty() ? seclabel_ : "default")
-              << ") started; waiting...";
+    LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << proc_attr_.uid
+              << " gid " << proc_attr_.gid << "+" << proc_attr_.supp_gids.size() << " context "
+              << (!seclabel_.empty() ? seclabel_ : "default") << ") started; waiting...";
 
-    return Success();
+    return {};
 }
 
-Result<Success> Service::Start() {
+Result<void> Service::Start() {
+    if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
+        ServiceList::GetInstance().DelayService(*this);
+        return Error() << "Cannot start an updatable service '" << name_
+                       << "' before configs from APEXes are all loaded. "
+                       << "Queued for execution.";
+    }
+
     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.
@@ -798,21 +352,21 @@
             flags_ |= SVC_RESTART;
         }
         // It is not an error to try to start a service that is already running.
-        return Success();
+        return {};
     }
 
     bool needs_console = (flags_ & SVC_CONSOLE);
     if (needs_console) {
-        if (console_.empty()) {
-            console_ = default_console;
+        if (proc_attr_.console.empty()) {
+            proc_attr_.console = "/dev/" + GetProperty("ro.boot.console", "console");
         }
 
         // Make sure that open call succeeds to ensure a console driver is
         // properly registered for the device node
-        int console_fd = open(console_.c_str(), O_RDWR | O_CLOEXEC);
+        int console_fd = open(proc_attr_.console.c_str(), O_RDWR | O_CLOEXEC);
         if (console_fd < 0) {
             flags_ |= SVC_DISABLED;
-            return ErrnoError() << "Couldn't open console '" << console_ << "'";
+            return ErrnoError() << "Couldn't open console '" << proc_attr_.console << "'";
         }
         close(console_fd);
     }
@@ -834,11 +388,21 @@
         scon = *result;
     }
 
+    if (!IsRuntimeApexReady() && !pre_apexd_) {
+        // If this service is started before the runtime APEX gets available,
+        // mark it as pre-apexd one. Note that this marking is permanent. So
+        // for example, if the service is re-launched (e.g., due to crash),
+        // it is still recognized as pre-apexd... for consistency.
+        pre_apexd_ = true;
+    }
+
+    post_data_ = ServiceList::GetInstance().IsPostData();
+
     LOG(INFO) << "starting service '" << name_ << "'...";
 
     pid_t pid = -1;
-    if (namespace_flags_) {
-        pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
+    if (namespaces_.flags) {
+        pid = clone(nullptr, nullptr, namespaces_.flags | SIGCHLD, nullptr);
     } else {
         pid = fork();
     }
@@ -846,24 +410,9 @@
     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.
-            if (auto result = SetUpPidNamespace(); !result) {
-                LOG(FATAL) << "Service '" << name_
-                           << "' could not set up PID namespace: " << result.error();
-            }
+        if (auto result = EnterNamespaces(namespaces_, name_, pre_apexd_); !result) {
+            LOG(FATAL) << "Service '" << name_
+                       << "' failed to set up namespaces: " << result.error();
         }
 
         for (const auto& [key, value] : environment_vars_) {
@@ -873,54 +422,15 @@
         std::for_each(descriptors_.begin(), descriptors_.end(),
                       std::bind(&DescriptorInfo::CreateAndPublish, std::placeholders::_1, scon));
 
-        // See if there were "writepid" instructions to write to files under /dev/cpuset/.
-        auto cpuset_predicate = [](const std::string& path) {
-            return StartsWith(path, "/dev/cpuset/");
-        };
-        auto iter = std::find_if(writepid_files_.begin(), writepid_files_.end(), cpuset_predicate);
-        if (iter == writepid_files_.end()) {
-            // There were no "writepid" instructions for cpusets, check if the system default
-            // cpuset is specified to be used for the process.
-            std::string default_cpuset = GetProperty("ro.cpuset.default", "");
-            if (!default_cpuset.empty()) {
-                // Make sure the cpuset name starts and ends with '/'.
-                // A single '/' means the 'root' cpuset.
-                if (default_cpuset.front() != '/') {
-                    default_cpuset.insert(0, 1, '/');
-                }
-                if (default_cpuset.back() != '/') {
-                    default_cpuset.push_back('/');
-                }
-                writepid_files_.push_back(
-                    StringPrintf("/dev/cpuset%stasks", default_cpuset.c_str()));
-            }
-        }
-        std::string pid_str = std::to_string(getpid());
-        for (const auto& file : writepid_files_) {
-            if (!WriteStringToFile(pid_str, file)) {
-                PLOG(ERROR) << "couldn't write " << pid_str << " to " << file;
-            }
-        }
-
-        if (ioprio_class_ != IoSchedClass_NONE) {
-            if (android_set_ioprio(getpid(), ioprio_class_, ioprio_pri_)) {
-                PLOG(ERROR) << "failed to set pid " << getpid()
-                            << " ioprio=" << ioprio_class_ << "," << ioprio_pri_;
-            }
-        }
-
-        if (needs_console) {
-            setsid();
-            OpenConsole();
-        } else {
-            ZapStdio();
+        if (auto result = WritePidToFiles(&writepid_files_); !result) {
+            LOG(ERROR) << "failed to write pid to files: " << result.error();
         }
 
         // As requested, set our gid, supplemental gids, uid, context, and
         // priority. Aborts on failure.
-        SetProcessAttributes();
+        SetProcessAttributesAndCaps();
 
-        if (!ExpandArgsAndExecv(args_)) {
+        if (!ExpandArgsAndExecv(args_, sigstop_)) {
             PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
         }
 
@@ -936,7 +446,7 @@
         std::string oom_str = std::to_string(oom_score_adjust_);
         std::string oom_file = StringPrintf("/proc/%d/oom_score_adj", pid);
         if (!WriteStringToFile(oom_str, oom_file)) {
-            PLOG(ERROR) << "couldn't write oom_score_adj: " << strerror(errno);
+            PLOG(ERROR) << "couldn't write oom_score_adj";
         }
     }
 
@@ -946,55 +456,97 @@
     start_order_ = next_start_order_++;
     process_cgroup_empty_ = false;
 
-    errno = -createProcessGroup(uid_, pid_);
+    bool use_memcg = swappiness_ != -1 || soft_limit_in_bytes_ != -1 || limit_in_bytes_ != -1 ||
+                      limit_percent_ != -1 || !limit_property_.empty();
+    errno = -createProcessGroup(proc_attr_.uid, pid_, use_memcg);
     if (errno != 0) {
-        PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '"
-                    << name_ << "'";
-    } else {
+        PLOG(ERROR) << "createProcessGroup(" << proc_attr_.uid << ", " << pid_
+                    << ") failed for service '" << name_ << "'";
+    } else if (use_memcg) {
         if (swappiness_ != -1) {
-            if (!setProcessGroupSwappiness(uid_, pid_, swappiness_)) {
+            if (!setProcessGroupSwappiness(proc_attr_.uid, pid_, swappiness_)) {
                 PLOG(ERROR) << "setProcessGroupSwappiness failed";
             }
         }
 
         if (soft_limit_in_bytes_ != -1) {
-            if (!setProcessGroupSoftLimit(uid_, pid_, soft_limit_in_bytes_)) {
+            if (!setProcessGroupSoftLimit(proc_attr_.uid, pid_, soft_limit_in_bytes_)) {
                 PLOG(ERROR) << "setProcessGroupSoftLimit failed";
             }
         }
 
-        if (limit_in_bytes_ != -1) {
-            if (!setProcessGroupLimit(uid_, pid_, limit_in_bytes_)) {
+        size_t computed_limit_in_bytes = limit_in_bytes_;
+        if (limit_percent_ != -1) {
+            long page_size = sysconf(_SC_PAGESIZE);
+            long num_pages = sysconf(_SC_PHYS_PAGES);
+            if (page_size > 0 && num_pages > 0) {
+                size_t max_mem = SIZE_MAX;
+                if (size_t(num_pages) < SIZE_MAX / size_t(page_size)) {
+                    max_mem = size_t(num_pages) * size_t(page_size);
+                }
+                computed_limit_in_bytes =
+                        std::min(computed_limit_in_bytes, max_mem / 100 * limit_percent_);
+            }
+        }
+
+        if (!limit_property_.empty()) {
+            // This ends up overwriting computed_limit_in_bytes but only if the
+            // property is defined.
+            computed_limit_in_bytes = android::base::GetUintProperty(
+                    limit_property_, computed_limit_in_bytes, SIZE_MAX);
+        }
+
+        if (computed_limit_in_bytes != size_t(-1)) {
+            if (!setProcessGroupLimit(proc_attr_.uid, pid_, computed_limit_in_bytes)) {
                 PLOG(ERROR) << "setProcessGroupLimit failed";
             }
         }
     }
 
     NotifyStateChange("running");
-    return Success();
+    return {};
 }
 
-Result<Success> Service::StartIfNotDisabled() {
+Result<void> Service::StartIfNotDisabled() {
     if (!(flags_ & SVC_DISABLED)) {
         return Start();
     } else {
         flags_ |= SVC_DISABLED_START;
     }
-    return Success();
+    return {};
 }
 
-Result<Success> Service::Enable() {
+Result<void> Service::Enable() {
     flags_ &= ~(SVC_DISABLED | SVC_RC_DISABLED);
     if (flags_ & SVC_DISABLED_START) {
         return Start();
     }
-    return Success();
+    return {};
 }
 
 void Service::Reset() {
     StopOrReset(SVC_RESET);
 }
 
+void Service::ResetIfPostData() {
+    if (post_data_) {
+        if (flags_ & SVC_RUNNING) {
+            running_at_post_data_reset_ = true;
+        }
+        StopOrReset(SVC_RESET);
+    }
+}
+
+Result<void> Service::StartIfPostData() {
+    // Start the service, but only if it was started after /data was mounted,
+    // and it was still running when we reset the post-data services.
+    if (running_at_post_data_reset_) {
+        return Start();
+    }
+
+    return {};
+}
+
 void Service::Stop() {
     StopOrReset(SVC_DISABLED);
 }
@@ -1008,6 +560,18 @@
     }
 }
 
+void Service::Timeout() {
+    // All process state flags will be taken care of in Reap(), we really just want to kill the
+    // process here when it times out.  Oneshot processes will transition to be disabled, and
+    // all other processes will transition to be restarting.
+    LOG(INFO) << "Service '" << name_ << "' expired its timeout of " << timeout_period_->count()
+              << " seconds and will now be killed";
+    if (pid_) {
+        KillProcessGroup(SIGKILL);
+        NotifyStateChange("stopping");
+    }
+}
+
 void Service::Restart() {
     if (flags_ & SVC_RUNNING) {
         /* Stop, wait, then start the service. */
@@ -1053,36 +617,6 @@
     }
 }
 
-void Service::ZapStdio() const {
-    int fd;
-    fd = open("/dev/null", O_RDWR);
-    dup2(fd, 0);
-    dup2(fd, 1);
-    dup2(fd, 2);
-    close(fd);
-}
-
-void Service::OpenConsole() const {
-    int fd = open(console_.c_str(), O_RDWR);
-    if (fd == -1) fd = open("/dev/null", O_RDWR);
-    ioctl(fd, TIOCSCTTY, 0);
-    dup2(fd, 0);
-    dup2(fd, 1);
-    dup2(fd, 2);
-    close(fd);
-}
-
-ServiceList::ServiceList() {}
-
-ServiceList& ServiceList::GetInstance() {
-    static ServiceList instance;
-    return instance;
-}
-
-void ServiceList::AddService(std::unique_ptr<Service> service) {
-    services_.emplace_back(std::move(service));
-}
-
 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
@@ -1109,7 +643,6 @@
     std::string name = "exec " + std::to_string(exec_count) + " (" + Join(str_args, " ") + ")";
 
     unsigned flags = SVC_ONESHOT | SVC_TEMPORARY;
-    CapSet no_capabilities;
     unsigned namespace_flags = 0;
 
     std::string seclabel = "";
@@ -1144,94 +677,8 @@
         }
     }
 
-    return std::make_unique<Service>(name, flags, *uid, *gid, supp_gids, no_capabilities,
-                                     namespace_flags, seclabel, nullptr, str_args);
-}
-
-// 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());
-    }
-    std::sort(shutdown_services.begin(), shutdown_services.end(),
-              [](const auto& a, const auto& b) { return a->start_order() > b->start_order(); });
-    return shutdown_services;
-}
-
-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();
-                               });
-    if (svc_it == services_.end()) {
-        return;
-    }
-
-    services_.erase(svc_it);
-}
-
-void ServiceList::DumpState() const {
-    for (const auto& s : services_) {
-        s->DumpState();
-    }
-}
-
-Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
-                                            const std::string& filename, int line) {
-    if (args.size() < 3) {
-        return Error() << "services must have a name and a program";
-    }
-
-    const std::string& name = args[1];
-    if (!IsValidName(name)) {
-        return Error() << "invalid service name '" << name << "'";
-    }
-
-    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, restart_action_subcontext, str_args);
-    return Success();
-}
-
-Result<Success> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
-    return service_ ? service_->ParseLine(std::move(args)) : Success();
-}
-
-Result<Success> ServiceParser::EndSection() {
-    if (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 {
-    // Property names can be any length, but may only contain certain characters.
-    // 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 IsLegalPropertyName("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
+    return std::make_unique<Service>(name, flags, *uid, *gid, supp_gids, namespace_flags, seclabel,
+                                     nullptr, str_args);
 }
 
 }  // namespace init
diff --git a/init/service.h b/init/service.h
index 87c9ac8..cc35a8d 100644
--- a/init/service.h
+++ b/init/service.h
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_SERVICE_H
-#define _INIT_SERVICE_H
+#pragma once
 
 #include <signal.h>
-#include <sys/resource.h>
 #include <sys/types.h>
 
+#include <chrono>
 #include <memory>
+#include <optional>
 #include <set>
 #include <string>
 #include <vector>
@@ -34,6 +34,7 @@
 #include "descriptors.h"
 #include "keyword_map.h"
 #include "parser.h"
+#include "service_utils.h"
 #include "subcontext.h"
 
 #define SVC_DISABLED 0x001        // do not autostart with class
@@ -41,7 +42,7 @@
 #define SVC_RUNNING 0x004         // currently active
 #define SVC_RESTARTING 0x008      // waiting to restart
 #define SVC_CONSOLE 0x010         // requires console
-#define SVC_CRITICAL 0x020        // will reboot into recovery if keeps crashing
+#define SVC_CRITICAL 0x020        // will reboot into bootloader if keeps crashing
 #define SVC_RESET 0x040           // Use when stopping a process,
                                   // but not disabling so it can be restarted with its class.
 #define SVC_RC_DISABLED 0x080     // Remember if the disabled flag was set in the rc script.
@@ -61,26 +62,29 @@
 namespace init {
 
 class Service {
+    friend class ServiceParser;
+
   public:
     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<gid_t>& supp_gids, int namespace_flags, const std::string& seclabel,
             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; }
-    Result<Success> ParseLine(const std::vector<std::string>& args);
-    Result<Success> ExecStart();
-    Result<Success> Start();
-    Result<Success> StartIfNotDisabled();
-    Result<Success> Enable();
+    Result<void> ExecStart();
+    Result<void> Start();
+    Result<void> StartIfNotDisabled();
+    Result<void> StartIfPostData();
+    Result<void> Enable();
     void Reset();
+    void ResetIfPostData();
     void Stop();
     void Terminate();
+    void Timeout();
     void Restart();
     void Reap(const siginfo_t& siginfo);
     void DumpState() const;
@@ -102,75 +106,38 @@
     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_; }
-    unsigned namespace_flags() const { return namespace_flags_; }
-    const std::vector<gid_t>& supp_gids() const { return supp_gids_; }
+    uid_t uid() const { return proc_attr_.uid; }
+    gid_t gid() const { return proc_attr_.gid; }
+    int namespace_flags() const { return namespaces_.flags; }
+    const std::vector<gid_t>& supp_gids() const { return proc_attr_.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_; }
+    IoSchedClass ioprio_class() const { return proc_attr_.ioprio_class; }
+    int ioprio_pri() const { return proc_attr_.ioprio_pri; }
     const std::set<std::string>& interfaces() const { return interfaces_; }
-    int priority() const { return priority_; }
+    int priority() const { return proc_attr_.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; }
+    std::chrono::seconds restart_period() const { return restart_period_; }
+    std::optional<std::chrono::seconds> timeout_period() const { return timeout_period_; }
     const std::vector<std::string>& args() const { return args_; }
+    bool is_updatable() const { return updatable_; }
+    bool is_post_data() const { return post_data_; }
 
   private:
-    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;
-    void OpenConsole() const;
     void KillProcessGroup(int signal);
-    void SetProcessAttributes();
-
-    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> 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>
-    Result<Success> AddDescriptor(const std::vector<std::string>& args);
+    void SetProcessAttributesAndCaps();
 
     static unsigned long next_start_order_;
     static bool is_exec_service_running_;
 
     std::string name_;
     std::set<std::string> classnames_;
-    std::string console_;
 
     unsigned flags_;
     pid_t pid_;
@@ -178,13 +145,9 @@
     android::base::boot_clock::time_point time_crashed_;  // first crash within inspection window
     int crash_count_;                     // number of times crashed within window
 
-    uid_t uid_;
-    gid_t gid_;
-    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::optional<CapSet> capabilities_;
+    ProcessAttributes proc_attr_;
+    NamespaceInfo namespaces_;
 
     std::string seclabel_;
 
@@ -197,19 +160,17 @@
 
     std::set<std::string> interfaces_;  // e.g. some.package.foo@1.0::IBaz/instance-name
 
-    // keycodes for triggering this service via /dev/keychord
+    // keycodes for triggering this service via /dev/input/input*
     std::vector<int> keycodes_;
-    int keychord_id_;
-
-    IoSchedClass ioprio_class_;
-    int ioprio_pri_;
-    int priority_;
 
     int oom_score_adjust_;
 
-    int swappiness_;
-    int soft_limit_in_bytes_;
-    int limit_in_bytes_;
+    int swappiness_ = -1;
+    int soft_limit_in_bytes_ = -1;
+
+    int limit_in_bytes_ = -1;
+    int limit_percent_ = -1;
+    std::string limit_property_;
 
     bool process_cgroup_empty_ = false;
 
@@ -217,64 +178,23 @@
 
     unsigned long start_order_;
 
-    std::vector<std::pair<int, rlimit>> rlimits_;
+    bool sigstop_ = false;
+
+    std::chrono::seconds restart_period_ = 5s;
+    std::optional<std::chrono::seconds> timeout_period_;
+
+    bool updatable_ = false;
 
     std::vector<std::string> args_;
 
     std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
-};
 
-class ServiceList {
-  public:
-    static ServiceList& GetInstance();
+    bool pre_apexd_ = false;
 
-    // Exposed for testing
-    ServiceList();
+    bool post_data_ = false;
 
-    void AddService(std::unique_ptr<Service> service);
-    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;
-    }
-
-    void DumpState() const;
-
-    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:
-    std::vector<std::unique_ptr<Service>> services_;
-};
-
-class ServiceParser : public SectionParser {
-  public:
-    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;
-
-    ServiceList* service_list_;
-    std::vector<Subcontext>* subcontexts_;
-    std::unique_ptr<Service> service_;
+    bool running_at_post_data_reset_ = false;
 };
 
 }  // namespace init
 }  // namespace android
-
-#endif
diff --git a/init/service_list.cpp b/init/service_list.cpp
new file mode 100644
index 0000000..3a48183
--- /dev/null
+++ b/init/service_list.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "service_list.h"
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace init {
+
+ServiceList::ServiceList() {}
+
+ServiceList& ServiceList::GetInstance() {
+    static ServiceList instance;
+    return instance;
+}
+
+void ServiceList::AddService(std::unique_ptr<Service> service) {
+    services_.emplace_back(std::move(service));
+}
+
+// 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());
+    }
+    std::sort(shutdown_services.begin(), shutdown_services.end(),
+              [](const auto& a, const auto& b) { return a->start_order() > b->start_order(); });
+    return shutdown_services;
+}
+
+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(); });
+    if (svc_it == services_.end()) {
+        return;
+    }
+
+    services_.erase(svc_it);
+}
+
+void ServiceList::DumpState() const {
+    for (const auto& s : services_) {
+        s->DumpState();
+    }
+}
+
+void ServiceList::MarkPostData() {
+    post_data_ = true;
+}
+
+bool ServiceList::IsPostData() {
+    return post_data_;
+}
+
+void ServiceList::MarkServicesUpdate() {
+    services_update_finished_ = true;
+
+    // start the delayed services
+    for (const auto& name : delayed_service_names_) {
+        Service* service = FindService(name);
+        if (service == nullptr) {
+            LOG(ERROR) << "delayed service '" << name << "' could not be found.";
+            continue;
+        }
+        if (auto result = service->Start(); !result) {
+            LOG(ERROR) << result.error().message();
+        }
+    }
+    delayed_service_names_.clear();
+}
+
+void ServiceList::DelayService(const Service& service) {
+    if (services_update_finished_) {
+        LOG(ERROR) << "Cannot delay the start of service '" << service.name()
+                   << "' because all services are already updated. Ignoring.";
+        return;
+    }
+    delayed_service_names_.emplace_back(service.name());
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/service_list.h b/init/service_list.h
new file mode 100644
index 0000000..2136a21
--- /dev/null
+++ b/init/service_list.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <memory>
+#include <vector>
+
+#include "service.h"
+
+namespace android {
+namespace init {
+
+class ServiceList {
+  public:
+    static ServiceList& GetInstance();
+
+    // Exposed for testing
+    ServiceList();
+
+    void AddService(std::unique_ptr<Service> service);
+    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;
+
+    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;
+
+    void MarkPostData();
+    bool IsPostData();
+    void MarkServicesUpdate();
+    bool IsServicesUpdated() const { return services_update_finished_; }
+    void DelayService(const Service& service);
+
+  private:
+    std::vector<std::unique_ptr<Service>> services_;
+
+    bool post_data_ = false;
+    bool services_update_finished_ = false;
+    std::vector<std::string> delayed_service_names_;
+};
+
+}  // namespace init
+}  // namespace android
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
new file mode 100644
index 0000000..ba35104
--- /dev/null
+++ b/init/service_parser.cpp
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "service_parser.h"
+
+#include <linux/input.h>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <hidl-util/FQName.h>
+#include <system/thread_defs.h>
+
+#include "rlimit_parser.h"
+#include "util.h"
+
+#if defined(__ANDROID__)
+#include <android/api-level.h>
+#include <sys/system_properties.h>
+
+#include "selinux.h"
+#else
+#include "host_init_stubs.h"
+#endif
+
+using android::base::ParseInt;
+using android::base::Split;
+using android::base::StartsWith;
+
+namespace android {
+namespace init {
+
+Result<void> ServiceParser::ParseCapabilities(std::vector<std::string>&& args) {
+    service_->capabilities_ = 0;
+
+    if (!CapAmbientSupported()) {
+        return Error()
+               << "capabilities requested but the kernel does not support ambient capabilities";
+    }
+
+    unsigned int last_valid_cap = GetLastValidCap();
+    if (last_valid_cap >= service_->capabilities_->size()) {
+        LOG(WARNING) << "last valid run-time capability is larger than CAP_LAST_CAP";
+    }
+
+    for (size_t i = 1; i < args.size(); i++) {
+        const std::string& arg = args[i];
+        int res = LookupCap(arg);
+        if (res < 0) {
+            return Errorf("invalid capability '{}'", arg);
+        }
+        unsigned int cap = static_cast<unsigned int>(res);  // |res| is >= 0.
+        if (cap > last_valid_cap) {
+            return Errorf("capability '{}' not supported by the kernel", arg);
+        }
+        (*service_->capabilities_)[cap] = true;
+    }
+    return {};
+}
+
+Result<void> ServiceParser::ParseClass(std::vector<std::string>&& args) {
+    service_->classnames_ = std::set<std::string>(args.begin() + 1, args.end());
+    return {};
+}
+
+Result<void> ServiceParser::ParseConsole(std::vector<std::string>&& args) {
+    service_->flags_ |= SVC_CONSOLE;
+    service_->proc_attr_.console = args.size() > 1 ? "/dev/" + args[1] : "";
+    return {};
+}
+
+Result<void> ServiceParser::ParseCritical(std::vector<std::string>&& args) {
+    service_->flags_ |= SVC_CRITICAL;
+    return {};
+}
+
+Result<void> ServiceParser::ParseDisabled(std::vector<std::string>&& args) {
+    service_->flags_ |= SVC_DISABLED;
+    service_->flags_ |= SVC_RC_DISABLED;
+    return {};
+}
+
+Result<void> ServiceParser::ParseEnterNamespace(std::vector<std::string>&& args) {
+    if (args[1] != "net") {
+        return Error() << "Init only supports entering network namespaces";
+    }
+    if (!service_->namespaces_.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.
+    service_->namespaces_.flags |= CLONE_NEWNS;
+    service_->namespaces_.namespaces_to_enter.emplace_back(CLONE_NEWNET, std::move(args[2]));
+    return {};
+}
+
+Result<void> ServiceParser::ParseGroup(std::vector<std::string>&& args) {
+    auto gid = DecodeUid(args[1]);
+    if (!gid) {
+        return Error() << "Unable to decode GID for '" << args[1] << "': " << gid.error();
+    }
+    service_->proc_attr_.gid = *gid;
+
+    for (std::size_t n = 2; n < args.size(); n++) {
+        gid = DecodeUid(args[n]);
+        if (!gid) {
+            return Error() << "Unable to decode GID for '" << args[n] << "': " << gid.error();
+        }
+        service_->proc_attr_.supp_gids.emplace_back(*gid);
+    }
+    return {};
+}
+
+Result<void> ServiceParser::ParsePriority(std::vector<std::string>&& args) {
+    service_->proc_attr_.priority = 0;
+    if (!ParseInt(args[1], &service_->proc_attr_.priority,
+                  static_cast<int>(ANDROID_PRIORITY_HIGHEST),  // highest is negative
+                  static_cast<int>(ANDROID_PRIORITY_LOWEST))) {
+        return Errorf("process priority value must be range {} - {}", ANDROID_PRIORITY_HIGHEST,
+                      ANDROID_PRIORITY_LOWEST);
+    }
+    return {};
+}
+
+Result<void> ServiceParser::ParseInterface(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 << "'";
+    }
+
+    if (known_interfaces_ && known_interfaces_->count(interface_name) == 0) {
+        return Error() << "Interface is not in the known set of hidl_interfaces: '"
+                       << interface_name << "'. Please ensure the interface is built "
+                       << "by a hidl_interface target.";
+    }
+
+    const std::string fullname = interface_name + "/" + instance_name;
+
+    for (const auto& svc : *service_list_) {
+        if (svc->interfaces().count(fullname) > 0) {
+            return Error() << "Interface '" << fullname << "' redefined in " << service_->name()
+                           << " but is already defined by " << svc->name();
+        }
+    }
+
+    service_->interfaces_.insert(fullname);
+
+    return {};
+}
+
+Result<void> ServiceParser::ParseIoprio(std::vector<std::string>&& args) {
+    if (!ParseInt(args[2], &service_->proc_attr_.ioprio_pri, 0, 7)) {
+        return Error() << "priority value must be range 0 - 7";
+    }
+
+    if (args[1] == "rt") {
+        service_->proc_attr_.ioprio_class = IoSchedClass_RT;
+    } else if (args[1] == "be") {
+        service_->proc_attr_.ioprio_class = IoSchedClass_BE;
+    } else if (args[1] == "idle") {
+        service_->proc_attr_.ioprio_class = IoSchedClass_IDLE;
+    } else {
+        return Error() << "ioprio option usage: ioprio <rt|be|idle> <0-7>";
+    }
+
+    return {};
+}
+
+Result<void> ServiceParser::ParseKeycodes(std::vector<std::string>&& args) {
+    auto it = args.begin() + 1;
+    if (args.size() == 2 && StartsWith(args[1], "$")) {
+        std::string expanded;
+        if (!expand_props(args[1], &expanded)) {
+            return Error() << "Could not expand property '" << args[1] << "'";
+        }
+
+        // If the property is not set, it defaults to none, in which case there are no keycodes
+        // for this service.
+        if (expanded == "none") {
+            return {};
+        }
+
+        args = Split(expanded, ",");
+        it = args.begin();
+    }
+
+    for (; it != args.end(); ++it) {
+        int code;
+        if (ParseInt(*it, &code, 0, KEY_MAX)) {
+            for (auto& key : service_->keycodes_) {
+                if (key == code) return Error() << "duplicate keycode: " << *it;
+            }
+            service_->keycodes_.insert(
+                    std::upper_bound(service_->keycodes_.begin(), service_->keycodes_.end(), code),
+                    code);
+        } else {
+            return Error() << "invalid keycode: " << *it;
+        }
+    }
+    return {};
+}
+
+Result<void> ServiceParser::ParseOneshot(std::vector<std::string>&& args) {
+    service_->flags_ |= SVC_ONESHOT;
+    return {};
+}
+
+Result<void> ServiceParser::ParseOnrestart(std::vector<std::string>&& args) {
+    args.erase(args.begin());
+    int line = service_->onrestart_.NumCommands() + 1;
+    if (auto result = service_->onrestart_.AddCommand(std::move(args), line); !result) {
+        return Error() << "cannot add Onrestart command: " << result.error();
+    }
+    return {};
+}
+
+Result<void> ServiceParser::ParseNamespace(std::vector<std::string>&& args) {
+    for (size_t i = 1; i < args.size(); i++) {
+        if (args[i] == "pid") {
+            service_->namespaces_.flags |= CLONE_NEWPID;
+            // PID namespaces require mount namespaces.
+            service_->namespaces_.flags |= CLONE_NEWNS;
+        } else if (args[i] == "mnt") {
+            service_->namespaces_.flags |= CLONE_NEWNS;
+        } else {
+            return Error() << "namespace must be 'pid' or 'mnt'";
+        }
+    }
+    return {};
+}
+
+Result<void> ServiceParser::ParseOomScoreAdjust(std::vector<std::string>&& args) {
+    if (!ParseInt(args[1], &service_->oom_score_adjust_, -1000, 1000)) {
+        return Error() << "oom_score_adjust value must be in range -1000 - +1000";
+    }
+    return {};
+}
+
+Result<void> ServiceParser::ParseOverride(std::vector<std::string>&& args) {
+    service_->override_ = true;
+    return {};
+}
+
+Result<void> ServiceParser::ParseMemcgSwappiness(std::vector<std::string>&& args) {
+    if (!ParseInt(args[1], &service_->swappiness_, 0)) {
+        return Error() << "swappiness value must be equal or greater than 0";
+    }
+    return {};
+}
+
+Result<void> ServiceParser::ParseMemcgLimitInBytes(std::vector<std::string>&& args) {
+    if (!ParseInt(args[1], &service_->limit_in_bytes_, 0)) {
+        return Error() << "limit_in_bytes value must be equal or greater than 0";
+    }
+    return {};
+}
+
+Result<void> ServiceParser::ParseMemcgLimitPercent(std::vector<std::string>&& args) {
+    if (!ParseInt(args[1], &service_->limit_percent_, 0)) {
+        return Error() << "limit_percent value must be equal or greater than 0";
+    }
+    return {};
+}
+
+Result<void> ServiceParser::ParseMemcgLimitProperty(std::vector<std::string>&& args) {
+    service_->limit_property_ = std::move(args[1]);
+    return {};
+}
+
+Result<void> ServiceParser::ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args) {
+    if (!ParseInt(args[1], &service_->soft_limit_in_bytes_, 0)) {
+        return Error() << "soft_limit_in_bytes value must be equal or greater than 0";
+    }
+    return {};
+}
+
+Result<void> ServiceParser::ParseProcessRlimit(std::vector<std::string>&& args) {
+    auto rlimit = ParseRlimit(args);
+    if (!rlimit) return rlimit.error();
+
+    service_->proc_attr_.rlimits.emplace_back(*rlimit);
+    return {};
+}
+
+Result<void> ServiceParser::ParseRestartPeriod(std::vector<std::string>&& args) {
+    int period;
+    if (!ParseInt(args[1], &period, 5)) {
+        return Error() << "restart_period value must be an integer >= 5";
+    }
+    service_->restart_period_ = std::chrono::seconds(period);
+    return {};
+}
+
+Result<void> ServiceParser::ParseSeclabel(std::vector<std::string>&& args) {
+    service_->seclabel_ = std::move(args[1]);
+    return {};
+}
+
+Result<void> ServiceParser::ParseSigstop(std::vector<std::string>&& args) {
+    service_->sigstop_ = true;
+    return {};
+}
+
+Result<void> ServiceParser::ParseSetenv(std::vector<std::string>&& args) {
+    service_->environment_vars_.emplace_back(std::move(args[1]), std::move(args[2]));
+    return {};
+}
+
+Result<void> ServiceParser::ParseShutdown(std::vector<std::string>&& args) {
+    if (args[1] == "critical") {
+        service_->flags_ |= SVC_SHUTDOWN_CRITICAL;
+        return {};
+    }
+    return Error() << "Invalid shutdown option";
+}
+
+Result<void> ServiceParser::ParseTimeoutPeriod(std::vector<std::string>&& args) {
+    int period;
+    if (!ParseInt(args[1], &period, 1)) {
+        return Error() << "timeout_period value must be an integer >= 1";
+    }
+    service_->timeout_period_ = std::chrono::seconds(period);
+    return {};
+}
+
+template <typename T>
+Result<void> ServiceParser::AddDescriptor(std::vector<std::string>&& args) {
+    int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
+    Result<uid_t> uid = 0;
+    Result<gid_t> gid = 0;
+    std::string context = args.size() > 6 ? args[6] : "";
+
+    if (args.size() > 4) {
+        uid = DecodeUid(args[4]);
+        if (!uid) {
+            return Error() << "Unable to find UID for '" << args[4] << "': " << uid.error();
+        }
+    }
+
+    if (args.size() > 5) {
+        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 old = std::find_if(
+            service_->descriptors_.begin(), service_->descriptors_.end(),
+            [&descriptor](const auto& other) { return descriptor.get() == other.get(); });
+
+    if (old != service_->descriptors_.end()) {
+        return Error() << "duplicate descriptor " << args[1] << " " << args[2];
+    }
+
+    service_->descriptors_.emplace_back(std::move(descriptor));
+    return {};
+}
+
+// name type perm [ uid gid context ]
+Result<void> ServiceParser::ParseSocket(std::vector<std::string>&& args) {
+    if (!StartsWith(args[2], "dgram") && !StartsWith(args[2], "stream") &&
+        !StartsWith(args[2], "seqpacket")) {
+        return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket'";
+    }
+    return AddDescriptor<SocketInfo>(std::move(args));
+}
+
+// name type perm [ uid gid context ]
+Result<void> ServiceParser::ParseFile(std::vector<std::string>&& args) {
+    if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
+        return Error() << "file type must be 'r', 'w' or 'rw'";
+    }
+    std::string expanded;
+    if (!expand_props(args[1], &expanded)) {
+        return Error() << "Could not expand property in file path '" << args[1] << "'";
+    }
+    args[1] = std::move(expanded);
+    if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
+        return Error() << "file name must not be relative";
+    }
+    return AddDescriptor<FileInfo>(std::move(args));
+}
+
+Result<void> ServiceParser::ParseUser(std::vector<std::string>&& args) {
+    auto uid = DecodeUid(args[1]);
+    if (!uid) {
+        return Error() << "Unable to find UID for '" << args[1] << "': " << uid.error();
+    }
+    service_->proc_attr_.uid = *uid;
+    return {};
+}
+
+Result<void> ServiceParser::ParseWritepid(std::vector<std::string>&& args) {
+    args.erase(args.begin());
+    service_->writepid_files_ = std::move(args);
+    return {};
+}
+
+Result<void> ServiceParser::ParseUpdatable(std::vector<std::string>&& args) {
+    service_->updatable_ = true;
+    return {};
+}
+
+class ServiceParser::OptionParserMap : public KeywordMap<OptionParser> {
+  public:
+    OptionParserMap() {}
+
+  private:
+    const Map& map() const override;
+};
+
+const ServiceParser::OptionParserMap::Map& ServiceParser::OptionParserMap::map() const {
+    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
+    // clang-format off
+    static const Map option_parsers = {
+        {"capabilities",
+                        {0,     kMax, &ServiceParser::ParseCapabilities}},
+        {"class",       {1,     kMax, &ServiceParser::ParseClass}},
+        {"console",     {0,     1,    &ServiceParser::ParseConsole}},
+        {"critical",    {0,     0,    &ServiceParser::ParseCritical}},
+        {"disabled",    {0,     0,    &ServiceParser::ParseDisabled}},
+        {"enter_namespace",
+                        {2,     2,    &ServiceParser::ParseEnterNamespace}},
+        {"file",        {2,     2,    &ServiceParser::ParseFile}},
+        {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &ServiceParser::ParseGroup}},
+        {"interface",   {2,     2,    &ServiceParser::ParseInterface}},
+        {"ioprio",      {2,     2,    &ServiceParser::ParseIoprio}},
+        {"keycodes",    {1,     kMax, &ServiceParser::ParseKeycodes}},
+        {"memcg.limit_in_bytes",
+                        {1,     1,    &ServiceParser::ParseMemcgLimitInBytes}},
+        {"memcg.limit_percent",
+                        {1,     1,    &ServiceParser::ParseMemcgLimitPercent}},
+        {"memcg.limit_property",
+                        {1,     1,    &ServiceParser::ParseMemcgLimitProperty}},
+        {"memcg.soft_limit_in_bytes",
+                        {1,     1,    &ServiceParser::ParseMemcgSoftLimitInBytes}},
+        {"memcg.swappiness",
+                        {1,     1,    &ServiceParser::ParseMemcgSwappiness}},
+        {"namespace",   {1,     2,    &ServiceParser::ParseNamespace}},
+        {"oneshot",     {0,     0,    &ServiceParser::ParseOneshot}},
+        {"onrestart",   {1,     kMax, &ServiceParser::ParseOnrestart}},
+        {"oom_score_adjust",
+                        {1,     1,    &ServiceParser::ParseOomScoreAdjust}},
+        {"override",    {0,     0,    &ServiceParser::ParseOverride}},
+        {"priority",    {1,     1,    &ServiceParser::ParsePriority}},
+        {"restart_period",
+                        {1,     1,    &ServiceParser::ParseRestartPeriod}},
+        {"rlimit",      {3,     3,    &ServiceParser::ParseProcessRlimit}},
+        {"seclabel",    {1,     1,    &ServiceParser::ParseSeclabel}},
+        {"setenv",      {2,     2,    &ServiceParser::ParseSetenv}},
+        {"shutdown",    {1,     1,    &ServiceParser::ParseShutdown}},
+        {"sigstop",     {0,     0,    &ServiceParser::ParseSigstop}},
+        {"socket",      {3,     6,    &ServiceParser::ParseSocket}},
+        {"timeout_period",
+                        {1,     1,    &ServiceParser::ParseTimeoutPeriod}},
+        {"updatable",   {0,     0,    &ServiceParser::ParseUpdatable}},
+        {"user",        {1,     1,    &ServiceParser::ParseUser}},
+        {"writepid",    {1,     kMax, &ServiceParser::ParseWritepid}},
+    };
+    // clang-format on
+    return option_parsers;
+}
+
+Result<void> ServiceParser::ParseSection(std::vector<std::string>&& args,
+                                         const std::string& filename, int line) {
+    if (args.size() < 3) {
+        return Error() << "services must have a name and a program";
+    }
+
+    const std::string& name = args[1];
+    if (!IsValidName(name)) {
+        return Error() << "invalid service name '" << name << "'";
+    }
+
+    filename_ = filename;
+
+    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());
+
+    if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_P__) {
+        if (str_args[0] == "/sbin/watchdogd") {
+            str_args[0] = "/system/bin/watchdogd";
+        }
+    }
+
+    service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args);
+    return {};
+}
+
+Result<void> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+    if (!service_) {
+        return {};
+    }
+
+    static const OptionParserMap parser_map;
+    auto parser = parser_map.FindFunction(args);
+
+    if (!parser) return parser.error();
+
+    return std::invoke(*parser, this, std::move(args));
+}
+
+Result<void> ServiceParser::EndSection() {
+    if (!service_) {
+        return {};
+    }
+
+    Service* old_service = service_list_->FindService(service_->name());
+    if (old_service) {
+        if (!service_->is_override()) {
+            return Error() << "ignored duplicate definition of service '" << service_->name()
+                           << "'";
+        }
+
+        if (StartsWith(filename_, "/apex/") && !old_service->is_updatable()) {
+            return Error() << "cannot update a non-updatable service '" << service_->name()
+                           << "' with a config in APEX";
+        }
+
+        service_list_->RemoveService(*old_service);
+        old_service = nullptr;
+    }
+
+    service_list_->AddService(std::move(service_));
+
+    return {};
+}
+
+bool ServiceParser::IsValidName(const std::string& name) const {
+    // Property names can be any length, but may only contain certain characters.
+    // 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 IsLegalPropertyName("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/service_parser.h b/init/service_parser.h
new file mode 100644
index 0000000..5a16768
--- /dev/null
+++ b/init/service_parser.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <vector>
+
+#include "parser.h"
+#include "service.h"
+#include "service_list.h"
+#include "subcontext.h"
+
+namespace android {
+namespace init {
+
+class ServiceParser : public SectionParser {
+  public:
+    ServiceParser(ServiceList* service_list, std::vector<Subcontext>* subcontexts,
+                  const std::optional<std::set<std::string>>& known_interfaces)
+        : service_list_(service_list),
+          subcontexts_(subcontexts),
+          known_interfaces_(known_interfaces),
+          service_(nullptr) {}
+    Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                              int line) override;
+    Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
+    Result<void> EndSection() override;
+    void EndFile() override { filename_ = ""; }
+
+  private:
+    using OptionParser = Result<void> (ServiceParser::*)(std::vector<std::string>&& args);
+    class OptionParserMap;
+
+    Result<void> ParseCapabilities(std::vector<std::string>&& args);
+    Result<void> ParseClass(std::vector<std::string>&& args);
+    Result<void> ParseConsole(std::vector<std::string>&& args);
+    Result<void> ParseCritical(std::vector<std::string>&& args);
+    Result<void> ParseDisabled(std::vector<std::string>&& args);
+    Result<void> ParseEnterNamespace(std::vector<std::string>&& args);
+    Result<void> ParseGroup(std::vector<std::string>&& args);
+    Result<void> ParsePriority(std::vector<std::string>&& args);
+    Result<void> ParseInterface(std::vector<std::string>&& args);
+    Result<void> ParseIoprio(std::vector<std::string>&& args);
+    Result<void> ParseKeycodes(std::vector<std::string>&& args);
+    Result<void> ParseOneshot(std::vector<std::string>&& args);
+    Result<void> ParseOnrestart(std::vector<std::string>&& args);
+    Result<void> ParseOomScoreAdjust(std::vector<std::string>&& args);
+    Result<void> ParseOverride(std::vector<std::string>&& args);
+    Result<void> ParseMemcgLimitInBytes(std::vector<std::string>&& args);
+    Result<void> ParseMemcgLimitPercent(std::vector<std::string>&& args);
+    Result<void> ParseMemcgLimitProperty(std::vector<std::string>&& args);
+    Result<void> ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args);
+    Result<void> ParseMemcgSwappiness(std::vector<std::string>&& args);
+    Result<void> ParseNamespace(std::vector<std::string>&& args);
+    Result<void> ParseProcessRlimit(std::vector<std::string>&& args);
+    Result<void> ParseRestartPeriod(std::vector<std::string>&& args);
+    Result<void> ParseSeclabel(std::vector<std::string>&& args);
+    Result<void> ParseSetenv(std::vector<std::string>&& args);
+    Result<void> ParseShutdown(std::vector<std::string>&& args);
+    Result<void> ParseSigstop(std::vector<std::string>&& args);
+    Result<void> ParseSocket(std::vector<std::string>&& args);
+    Result<void> ParseTimeoutPeriod(std::vector<std::string>&& args);
+    Result<void> ParseFile(std::vector<std::string>&& args);
+    Result<void> ParseUser(std::vector<std::string>&& args);
+    Result<void> ParseWritepid(std::vector<std::string>&& args);
+    Result<void> ParseUpdatable(std::vector<std::string>&& args);
+
+    template <typename T>
+    Result<void> AddDescriptor(std::vector<std::string>&& args);
+
+    bool IsValidName(const std::string& name) const;
+
+    ServiceList* service_list_;
+    std::vector<Subcontext>* subcontexts_;
+    std::optional<std::set<std::string>> known_interfaces_;
+    std::unique_ptr<Service> service_;
+    std::string filename_;
+};
+
+}  // namespace init
+}  // namespace android
diff --git a/init/service_test.cpp b/init/service_test.cpp
index b43c2e9..6a34acc 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -30,7 +30,7 @@
 
 TEST(service, pod_initialized) {
     constexpr auto memory_size = sizeof(Service);
-    alignas(alignof(Service)) char old_memory[memory_size];
+    alignas(alignof(Service)) unsigned char old_memory[memory_size];
 
     for (std::size_t i = 0; i < memory_size; ++i) {
         old_memory[i] = 0xFF;
@@ -45,8 +45,7 @@
     EXPECT_EQ(0, service_in_old_memory->crash_count());
     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(0, service_in_old_memory->namespace_flags());
     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());
@@ -58,15 +57,14 @@
     }
 
     Service* service_in_old_memory2 = new (old_memory) Service(
-        "test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), CapSet(), 0U, "", nullptr, dummy_args);
+            "test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), 0U, "", nullptr, dummy_args);
 
     EXPECT_EQ(0U, service_in_old_memory2->flags());
     EXPECT_EQ(0, service_in_old_memory2->pid());
     EXPECT_EQ(0, service_in_old_memory2->crash_count());
     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(0, service_in_old_memory2->namespace_flags());
     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());
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
new file mode 100644
index 0000000..34aa837
--- /dev/null
+++ b/init/service_utils.cpp
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "service_utils.h"
+
+#include <grp.h>
+#include <sys/mount.h>
+#include <sys/prctl.h>
+#include <sys/wait.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 <processgroup/processgroup.h>
+
+#include "mount_namespace.h"
+
+using android::base::GetProperty;
+using android::base::StartsWith;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::base::WriteStringToFile;
+
+namespace android {
+namespace init {
+
+namespace {
+
+Result<void> EnterNamespace(int nstype, const char* path) {
+    auto fd = unique_fd{open(path, O_RDONLY | O_CLOEXEC)};
+    if (fd == -1) {
+        return ErrnoError() << "Could not open namespace at " << path;
+    }
+    if (setns(fd, nstype) == -1) {
+        return ErrnoError() << "Could not setns() namespace at " << path;
+    }
+    return {};
+}
+
+Result<void> SetUpMountNamespace(bool remount_proc, bool remount_sys) {
+    constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
+
+    // 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";
+    }
+
+    // umount() then mount() /proc and/or /sys
+    // Note that it is not sufficient to mount with MS_REMOUNT.
+    if (remount_proc) {
+        if (umount("/proc") == -1) {
+            return ErrnoError() << "Could not umount(/proc)";
+        }
+        if (mount("", "/proc", "proc", kSafeFlags, "") == -1) {
+            return ErrnoError() << "Could not mount(/proc)";
+        }
+    }
+    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 {};
+}
+
+Result<void> SetUpPidNamespace(const char* name) {
+    if (prctl(PR_SET_NAME, name) == -1) {
+        return ErrnoError() << "Could not set name";
+    }
+
+    pid_t child_pid = fork();
+    if (child_pid == -1) {
+        return ErrnoError() << "Could not fork init inside the PID namespace";
+    }
+
+    if (child_pid > 0) {
+        // So that we exit with the right status.
+        static int init_exitstatus = 0;
+        signal(SIGTERM, [](int) { _exit(init_exitstatus); });
+
+        pid_t waited_pid;
+        int status;
+        while ((waited_pid = wait(&status)) > 0) {
+            // This loop will end when there are no processes left inside the
+            // PID namespace or when the init process inside the PID namespace
+            // gets a signal.
+            if (waited_pid == child_pid) {
+                init_exitstatus = status;
+            }
+        }
+        if (!WIFEXITED(init_exitstatus)) {
+            _exit(EXIT_FAILURE);
+        }
+        _exit(WEXITSTATUS(init_exitstatus));
+    }
+    return {};
+}
+
+void ZapStdio() {
+    auto fd = unique_fd{open("/dev/null", O_RDWR | O_CLOEXEC)};
+    dup2(fd, 0);
+    dup2(fd, 1);
+    dup2(fd, 2);
+}
+
+void OpenConsole(const std::string& console) {
+    auto fd = unique_fd{open(console.c_str(), O_RDWR | O_CLOEXEC)};
+    if (fd == -1) fd.reset(open("/dev/null", O_RDWR | O_CLOEXEC));
+    ioctl(fd, TIOCSCTTY, 0);
+    dup2(fd, 0);
+    dup2(fd, 1);
+    dup2(fd, 2);
+}
+
+}  // namespace
+
+Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name, bool pre_apexd) {
+    for (const auto& [nstype, path] : info.namespaces_to_enter) {
+        if (auto result = EnterNamespace(nstype, path.c_str()); !result) {
+            return result;
+        }
+    }
+
+#if defined(__ANDROID__)
+    if (pre_apexd) {
+        if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
+            return Error() << "could not enter into the bootstrap mount namespace";
+        }
+    }
+#endif
+
+    if (info.flags & CLONE_NEWNS) {
+        bool remount_proc = info.flags & CLONE_NEWPID;
+        bool remount_sys =
+                std::any_of(info.namespaces_to_enter.begin(), info.namespaces_to_enter.end(),
+                            [](const auto& entry) { return entry.first == CLONE_NEWNET; });
+        if (auto result = SetUpMountNamespace(remount_proc, remount_sys); !result) {
+            return result;
+        }
+    }
+
+    if (info.flags & CLONE_NEWPID) {
+        // This will fork again to run an init process inside the PID namespace.
+        if (auto result = SetUpPidNamespace(name.c_str()); !result) {
+            return result;
+        }
+    }
+
+    return {};
+}
+
+Result<void> SetProcessAttributes(const ProcessAttributes& attr) {
+    if (attr.ioprio_class != IoSchedClass_NONE) {
+        if (android_set_ioprio(getpid(), attr.ioprio_class, attr.ioprio_pri)) {
+            PLOG(ERROR) << "failed to set pid " << getpid() << " ioprio=" << attr.ioprio_class
+                        << "," << attr.ioprio_pri;
+        }
+    }
+
+    if (!attr.console.empty()) {
+        setsid();
+        OpenConsole(attr.console);
+    } else {
+        if (setpgid(0, getpid()) == -1) {
+            return ErrnoError() << "setpgid failed";
+        }
+        ZapStdio();
+    }
+
+    for (const auto& rlimit : attr.rlimits) {
+        if (setrlimit(rlimit.first, &rlimit.second) == -1) {
+            return ErrnoError() << StringPrintf(
+                           "setrlimit(%d, {rlim_cur=%ld, rlim_max=%ld}) failed", rlimit.first,
+                           rlimit.second.rlim_cur, rlimit.second.rlim_max);
+        }
+    }
+
+    if (attr.gid) {
+        if (setgid(attr.gid) != 0) {
+            return ErrnoError() << "setgid failed";
+        }
+    }
+    if (setgroups(attr.supp_gids.size(), const_cast<gid_t*>(&attr.supp_gids[0])) != 0) {
+        return ErrnoError() << "setgroups failed";
+    }
+    if (attr.uid) {
+        if (setuid(attr.uid) != 0) {
+            return ErrnoError() << "setuid failed";
+        }
+    }
+
+    if (attr.priority != 0) {
+        if (setpriority(PRIO_PROCESS, 0, attr.priority) != 0) {
+            return ErrnoError() << "setpriority failed";
+        }
+    }
+    return {};
+}
+
+Result<void> WritePidToFiles(std::vector<std::string>* files) {
+    // See if there were "writepid" instructions to write to files under cpuset path.
+    std::string cpuset_path;
+    if (CgroupGetControllerPath("cpuset", &cpuset_path)) {
+        auto cpuset_predicate = [&cpuset_path](const std::string& path) {
+            return StartsWith(path, cpuset_path + "/");
+        };
+        auto iter = std::find_if(files->begin(), files->end(), cpuset_predicate);
+        if (iter == files->end()) {
+            // There were no "writepid" instructions for cpusets, check if the system default
+            // cpuset is specified to be used for the process.
+            std::string default_cpuset = GetProperty("ro.cpuset.default", "");
+            if (!default_cpuset.empty()) {
+                // Make sure the cpuset name starts and ends with '/'.
+                // A single '/' means the 'root' cpuset.
+                if (default_cpuset.front() != '/') {
+                    default_cpuset.insert(0, 1, '/');
+                }
+                if (default_cpuset.back() != '/') {
+                    default_cpuset.push_back('/');
+                }
+                files->push_back(
+                        StringPrintf("%s%stasks", cpuset_path.c_str(), default_cpuset.c_str()));
+            }
+        }
+    } else {
+        LOG(ERROR) << "cpuset cgroup controller is not mounted!";
+    }
+    std::string pid_str = std::to_string(getpid());
+    for (const auto& file : *files) {
+        if (!WriteStringToFile(pid_str, file)) {
+            return ErrnoError() << "couldn't write " << pid_str << " to " << file;
+        }
+    }
+    return {};
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/service_utils.h b/init/service_utils.h
new file mode 100644
index 0000000..365cb29
--- /dev/null
+++ b/init/service_utils.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/resource.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include <cutils/iosched_policy.h>
+
+#include "result.h"
+
+namespace android {
+namespace init {
+
+struct NamespaceInfo {
+    int flags;
+    // Pair of namespace type, path to name.
+    std::vector<std::pair<int, std::string>> namespaces_to_enter;
+};
+Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name, bool pre_apexd);
+
+struct ProcessAttributes {
+    std::string console;
+    IoSchedClass ioprio_class;
+    int ioprio_pri;
+    std::vector<std::pair<int, rlimit>> rlimits;
+    uid_t uid;
+    gid_t gid;
+    std::vector<gid_t> supp_gids;
+    int priority;
+};
+Result<void> SetProcessAttributes(const ProcessAttributes& attr);
+
+Result<void> WritePidToFiles(std::vector<std::string>* files);
+
+}  // namespace init
+}  // namespace android
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index badacaf..c9a09cd 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -29,8 +29,8 @@
 #include <android-base/stringprintf.h>
 
 #include "init.h"
-#include "property_service.h"
 #include "service.h"
+#include "service_list.h"
 
 using android::base::StringPrintf;
 using android::base::boot_clock;
@@ -39,9 +39,6 @@
 namespace android {
 namespace init {
 
-static int signal_write_fd = -1;
-static int signal_read_fd = -1;
-
 static bool ReapOneProcess() {
     siginfo_t siginfo = {};
     // This returns a zombie pid or informs us that there are no zombies left to be reaped.
@@ -64,9 +61,7 @@
     std::string wait_string;
     Service* service = nullptr;
 
-    if (PropertyChildReap(pid)) {
-        name = "Async property child";
-    } else if (SubcontextChildReap(pid)) {
+    if (SubcontextChildReap(pid)) {
         name = "Subcontext";
     } else {
         service = ServiceList::GetInstance().FindService(pid, &Service::pid);
@@ -101,46 +96,10 @@
     return true;
 }
 
-static void handle_signal() {
-    // Clear outstanding requests.
-    char buf[32];
-    read(signal_read_fd, buf, sizeof(buf));
-
-    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 ReapAnyOutstandingChildren() {
     while (ReapOneProcess()) {
     }
 }
 
-void sigchld_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(FATAL) << "socketpair failed in sigchld_handler_init";
-    }
-
-    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);
-
-    ReapAnyOutstandingChildren();
-
-    register_epoll_handler(signal_read_fd, handle_signal);
-}
-
 }  // namespace init
 }  // namespace android
diff --git a/init/sigchld_handler.h b/init/sigchld_handler.h
index c86dc8d..30063f2 100644
--- a/init/sigchld_handler.h
+++ b/init/sigchld_handler.h
@@ -22,8 +22,6 @@
 
 void ReapAnyOutstandingChildren();
 
-void sigchld_handler_init(void);
-
 }  // namespace init
 }  // namespace android
 
diff --git a/init/stable_properties.h b/init/stable_properties.h
deleted file mode 100644
index 4972d10..0000000
--- a/init/stable_properties.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.usb.usbradio.config",
-    "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
index fdb4641..2f9541b 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -30,16 +30,15 @@
 #include "util.h"
 
 #if defined(__ANDROID__)
-#include <android-base/properties.h>
-
+#include <android/api-level.h>
 #include "property_service.h"
+#include "selabel.h"
 #include "selinux.h"
 #else
 #include "host_init_stubs.h"
 #endif
 
 using android::base::GetExecutablePath;
-using android::base::GetIntProperty;
 using android::base::Join;
 using android::base::Socketpair;
 using android::base::Split;
@@ -64,14 +63,16 @@
 Result<std::string> ReadMessage(int socket) {
     char buffer[kBufferSize] = {};
     auto result = TEMP_FAILURE_RETRY(recv(socket, buffer, sizeof(buffer), 0));
-    if (result <= 0) {
+    if (result == 0) {
+        return Error();
+    } else if (result < 0) {
         return ErrnoError();
     }
     return std::string(buffer, result);
 }
 
 template <typename T>
-Result<Success> SendMessage(int socket, const T& message) {
+Result<void> SendMessage(int socket, const T& message) {
     std::string message_string;
     if (!message.SerializeToString(&message_string)) {
         return Error() << "Unable to serialize message";
@@ -86,7 +87,7 @@
         result != static_cast<long>(message_string.size())) {
         return ErrnoError() << "send() failed to send message contents";
     }
-    return Success();
+    return {};
 }
 
 std::vector<std::pair<std::string, std::string>> properties_to_set;
@@ -122,7 +123,7 @@
     }
 
     auto map_result = function_map_->FindFunction(args);
-    Result<Success> result;
+    Result<void> result;
     if (!map_result) {
         result = Error() << "Cannot find command: " << map_result.error();
     } else {
@@ -141,8 +142,8 @@
         reply->set_success(true);
     } else {
         auto* failure = reply->mutable_failure();
-        failure->set_error_string(result.error_string());
-        failure->set_error_errno(result.error_errno());
+        failure->set_error_string(result.error().message());
+        failure->set_error_errno(result.error().code());
     }
 }
 
@@ -177,6 +178,12 @@
 
         auto init_message = ReadMessage(init_fd_);
         if (!init_message) {
+            if (init_message.error().code() == 0) {
+                // If the init file descriptor was closed, let's exit quietly. If
+                // this was accidental, init will restart us. If init died, this
+                // avoids calling abort(3) unnecessarily.
+                return;
+            }
             LOG(FATAL) << "Could not read message from init: " << init_message.error();
         }
 
@@ -239,7 +246,7 @@
 
         // 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);
+        int child_fd = dup(subcontext_socket);  // NOLINT(android-cloexec-dup)
         if (child_fd < 0) {
             PLOG(FATAL) << "Could not dup child_fd";
         }
@@ -292,7 +299,7 @@
     return subcontext_reply;
 }
 
-Result<Success> Subcontext::Execute(const std::vector<std::string>& args) {
+Result<void> Subcontext::Execute(const std::vector<std::string>& args) {
     auto subcontext_command = SubcontextCommand();
     std::copy(
         args.begin(), args.end(),
@@ -322,7 +329,7 @@
                        << subcontext_reply->reply_case();
     }
 
-    return Success();
+    return {};
 }
 
 Result<std::vector<std::string>> Subcontext::ExpandArgs(const std::vector<std::string>& args) {
@@ -355,9 +362,10 @@
 }
 
 static std::vector<Subcontext> subcontexts;
+static bool shutting_down;
 
 std::vector<Subcontext>* InitializeSubcontexts() {
-    if (SelinuxHasVendorInit()) {
+    if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
         for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
             subcontexts.emplace_back(path_prefix, secontext);
         }
@@ -368,12 +376,21 @@
 bool SubcontextChildReap(pid_t pid) {
     for (auto& subcontext : subcontexts) {
         if (subcontext.pid() == pid) {
-            subcontext.Restart();
+            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
index 22d7d43..16bd870 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -42,7 +42,7 @@
         Fork();
     }
 
-    Result<Success> Execute(const std::vector<std::string>& args);
+    Result<void> Execute(const std::vector<std::string>& args);
     Result<std::vector<std::string>> ExpandArgs(const std::vector<std::string>& args);
     void Restart();
 
@@ -63,6 +63,7 @@
 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
diff --git a/init/subcontext_benchmark.cpp b/init/subcontext_benchmark.cpp
index 6307993..fdbbc41 100644
--- a/init/subcontext_benchmark.cpp
+++ b/init/subcontext_benchmark.cpp
@@ -53,7 +53,7 @@
 TestFunctionMap BuildTestFunctionMap() {
     TestFunctionMap test_function_map;
     test_function_map.Add("return_success", 0, 0, true,
-                          [](const BuiltinArguments& args) { return Success(); });
+                          [](const BuiltinArguments& args) { return Result<void>{}; });
 
     return test_function_map;
 }
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index 230203a..55912d6 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -69,7 +69,7 @@
         auto result = subcontext.Execute(std::vector<std::string>{"return_pids_as_error"});
         ASSERT_FALSE(result);
 
-        auto pids = Split(result.error_string(), " ");
+        auto pids = Split(result.error().message(), " ");
         ASSERT_EQ(2U, pids.size());
         auto our_pid = std::to_string(getpid());
         EXPECT_NE(our_pid, pids[0]);
@@ -116,7 +116,7 @@
 
         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(Join(expected_words, " "), result.error().message());
         EXPECT_EQ(first_pid, subcontext.pid());
     });
 }
@@ -130,7 +130,7 @@
 
         auto result2 = subcontext.Execute(std::vector<std::string>{"generate_sane_error"});
         ASSERT_FALSE(result2);
-        EXPECT_EQ("Sane error!", result2.error_string());
+        EXPECT_EQ("Sane error!", result2.error().message());
         EXPECT_NE(subcontext.pid(), first_pid);
     });
 }
@@ -139,7 +139,7 @@
     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());
+        ASSERT_EQ(context_string, result.error().message());
     });
 }
 
@@ -167,7 +167,7 @@
         };
         auto result = subcontext.ExpandArgs(args);
         ASSERT_FALSE(result);
-        EXPECT_EQ("Failed to expand '" + args[1] + "'", result.error_string());
+        EXPECT_EQ("Failed to expand '" + args[1] + "'", result.error().message());
     });
 }
 
@@ -175,14 +175,14 @@
     TestFunctionMap test_function_map;
     // For CheckDifferentPid
     test_function_map.Add("return_pids_as_error", 0, 0, true,
-                          [](const BuiltinArguments& args) -> Result<Success> {
+                          [](const BuiltinArguments& args) -> Result<void> {
                               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();
+        return Result<void>{};
     });
 
     // For MultipleCommands
@@ -190,26 +190,26 @@
     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();
+        return Result<void>{};
     });
     test_function_map.Add("return_words_as_error", 0, 0, true,
-                          [words](const BuiltinArguments& args) -> Result<Success> {
+                          [words](const BuiltinArguments& args) -> Result<void> {
                               return Error() << Join(*words, " ");
                           });
 
     // For RecoverAfterAbort
     test_function_map.Add("cause_log_fatal", 0, 0, true,
-                          [](const BuiltinArguments& args) -> Result<Success> {
+                          [](const BuiltinArguments& args) -> Result<void> {
                               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!"; });
+            "generate_sane_error", 0, 0, true,
+            [](const BuiltinArguments& args) -> Result<void> { 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_context_as_error", 0, 0, true,
+            [](const BuiltinArguments& args) -> Result<void> { return Error() << args.context; });
 
     return test_function_map;
 }
diff --git a/init/switch_root.cpp b/init/switch_root.cpp
new file mode 100644
index 0000000..575b67f
--- /dev/null
+++ b/init/switch_root.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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 "switch_root.h"
+
+#include <fcntl.h>
+#include <mntent.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+using android::base::StartsWith;
+
+using namespace std::literals;
+
+namespace android {
+namespace init {
+
+namespace {
+
+std::vector<std::string> GetMounts(const std::string& new_root) {
+    auto fp = std::unique_ptr<std::FILE, decltype(&endmntent)>{setmntent("/proc/mounts", "re"),
+                                                               endmntent};
+    if (fp == nullptr) {
+        PLOG(FATAL) << "Failed to open /proc/mounts";
+    }
+
+    std::vector<std::string> result;
+    mntent* mentry;
+    while ((mentry = getmntent(fp.get())) != nullptr) {
+        // We won't try to move rootfs.
+        if (mentry->mnt_dir == "/"s) {
+            continue;
+        }
+
+        // The new root mount is handled separately.
+        if (mentry->mnt_dir == new_root) {
+            continue;
+        }
+
+        // Move operates on subtrees, so do not try to move children of other mounts.
+        if (std::find_if(result.begin(), result.end(), [&mentry](const auto& older_mount) {
+                return StartsWith(mentry->mnt_dir, older_mount);
+            }) != result.end()) {
+            continue;
+        }
+
+        result.emplace_back(mentry->mnt_dir);
+    }
+
+    return result;
+}
+
+}  // namespace
+
+void SwitchRoot(const std::string& new_root) {
+    auto mounts = GetMounts(new_root);
+
+    LOG(INFO) << "Switching root to '" << new_root << "'";
+
+    for (const auto& mount_path : mounts) {
+        auto new_mount_path = new_root + mount_path;
+        mkdir(new_mount_path.c_str(), 0755);
+        if (mount(mount_path.c_str(), new_mount_path.c_str(), nullptr, MS_MOVE, nullptr) != 0) {
+            PLOG(FATAL) << "Unable to move mount at '" << mount_path << "'";
+        }
+    }
+
+    if (chdir(new_root.c_str()) != 0) {
+        PLOG(FATAL) << "Could not chdir to new_root, '" << new_root << "'";
+    }
+
+    if (mount(new_root.c_str(), "/", nullptr, MS_MOVE, nullptr) != 0) {
+        PLOG(FATAL) << "Unable to move root mount to new_root, '" << new_root << "'";
+    }
+
+    if (chroot(".") != 0) {
+        PLOG(FATAL) << "Unable to chroot to new root";
+    }
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/switch_root.h b/init/switch_root.h
new file mode 100644
index 0000000..d515e5d
--- /dev/null
+++ b/init/switch_root.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>
+
+namespace android {
+namespace init {
+
+void SwitchRoot(const std::string& new_root);
+
+}  // namespace init
+}  // namespace android
diff --git a/init/test_function_map.h b/init/test_function_map.h
index 583df1a..293f1f9 100644
--- a/init/test_function_map.h
+++ b/init/test_function_map.h
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_TEST_FUNCTION_MAP_H
-#define _INIT_TEST_FUNCTION_MAP_H
+#pragma once
 
 #include <string>
 #include <vector>
 
 #include "builtin_arguments.h"
+#include "builtins.h"
 #include "keyword_map.h"
 
 namespace android {
@@ -33,7 +33,7 @@
     void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
         Add(name, 0, 0, false, [function](const BuiltinArguments&) {
             function();
-            return Success();
+            return Result<void>{};
         });
     }
 
@@ -51,5 +51,3 @@
 
 }  // namespace init
 }  // namespace android
-
-#endif
diff --git a/init/tokenizer.cpp b/init/tokenizer.cpp
index f8d9b6b..7e05a0a 100644
--- a/init/tokenizer.cpp
+++ b/init/tokenizer.cpp
@@ -1,5 +1,7 @@
 #include "tokenizer.h"
 
+#include <android-base/macros.h>
+
 namespace android {
 namespace init {
 
@@ -85,15 +87,19 @@
                 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 */
@@ -101,6 +107,8 @@
                     x++;
                     continue;
                 }
+                x++;
+                FALLTHROUGH_INTENDED;
             case '\n':
                     /* \ <lf> -> line continuation */
                 state->line++;
diff --git a/init/tokenizer_test.cpp b/init/tokenizer_test.cpp
new file mode 100644
index 0000000..6b31683
--- /dev/null
+++ b/init/tokenizer_test.cpp
@@ -0,0 +1,164 @@
+//
+// 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));
+                current_line.clear();
+                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_handler.h b/init/uevent_handler.h
new file mode 100644
index 0000000..75d1990
--- /dev/null
+++ b/init/uevent_handler.h
@@ -0,0 +1,34 @@
+/*
+ * 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 "uevent.h"
+
+namespace android {
+namespace init {
+
+class UeventHandler {
+  public:
+    virtual ~UeventHandler() = default;
+
+    virtual void HandleUevent(const Uevent& uevent) = 0;
+
+    virtual void ColdbootDone() {}
+};
+
+}  // namespace init
+}  // namespace android
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index 24b14c4..ac633776 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
@@ -82,9 +86,8 @@
     }
 }
 
-UeventListener::UeventListener() {
-    // is 2MB enough? udev uses 128MB!
-    device_fd_.reset(uevent_open_socket(2 * 1024 * 1024, true));
+UeventListener::UeventListener(size_t uevent_socket_rcvbuf_size) {
+    device_fd_.reset(uevent_open_socket(uevent_socket_rcvbuf_size, true));
     if (device_fd_ == -1) {
         LOG(FATAL) << "Could not open uevent socket";
     }
@@ -128,7 +131,7 @@
                                                        const ListenerCallback& callback) const {
     int dfd = dirfd(d);
 
-    int fd = openat(dfd, "uevent", O_WRONLY);
+    int fd = openat(dfd, "uevent", O_WRONLY | O_CLOEXEC);
     if (fd >= 0) {
         write(fd, "add\n", 4);
         close(fd);
@@ -143,7 +146,7 @@
     while ((de = readdir(d)) != nullptr) {
         if (de->d_type != DT_DIR || de->d_name[0] == '.') continue;
 
-        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
+        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
         if (fd < 0) continue;
 
         std::unique_ptr<DIR, decltype(&closedir)> d2(fdopendir(fd), closedir);
diff --git a/init/uevent_listener.h b/init/uevent_listener.h
index 5b453fe..aea094e 100644
--- a/init/uevent_listener.h
+++ b/init/uevent_listener.h
@@ -41,7 +41,7 @@
 
 class UeventListener {
   public:
-    UeventListener();
+    UeventListener(size_t uevent_socket_rcvbuf_size);
 
     void RegenerateUevents(const ListenerCallback& callback) const;
     ListenerAction RegenerateUeventsForPath(const std::string& path,
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index a284203..3b9de0f 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -36,8 +36,10 @@
 
 #include "devices.h"
 #include "firmware_handler.h"
-#include "log.h"
+#include "modalias_handler.h"
+#include "selabel.h"
 #include "selinux.h"
+#include "uevent_handler.h"
 #include "uevent_listener.h"
 #include "ueventd_parser.h"
 #include "util.h"
@@ -107,9 +109,10 @@
 
 class ColdBoot {
   public:
-    ColdBoot(UeventListener& uevent_listener, DeviceHandler& device_handler)
+    ColdBoot(UeventListener& uevent_listener,
+             std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers)
         : uevent_listener_(uevent_listener),
-          device_handler_(device_handler),
+          uevent_handlers_(uevent_handlers),
           num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4) {}
 
     void Run();
@@ -122,7 +125,7 @@
     void WaitForSubProcesses();
 
     UeventListener& uevent_listener_;
-    DeviceHandler& device_handler_;
+    std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers_;
 
     unsigned int num_handler_subprocesses_;
     std::vector<Uevent> uevent_queue_;
@@ -133,15 +136,16 @@
 void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_processes) {
     for (unsigned int i = process_num; i < uevent_queue_.size(); i += total_processes) {
         auto& uevent = uevent_queue_[i];
-        device_handler_.HandleDeviceEvent(uevent);
+
+        for (auto& uevent_handler : uevent_handlers_) {
+            uevent_handler->HandleUevent(uevent);
+        }
     }
     _exit(EXIT_SUCCESS);
 }
 
 void ColdBoot::RegenerateUevents() {
     uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {
-        HandleFirmwareEvent(uevent);
-
         uevent_queue_.emplace_back(std::move(uevent));
         return ListenerAction::kContinue;
     });
@@ -164,7 +168,6 @@
 
 void ColdBoot::DoRestoreCon() {
     selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
-    device_handler_.set_skip_restorecon(false);
 }
 
 void ColdBoot::WaitForSubProcesses() {
@@ -173,7 +176,7 @@
     //
     // When a subprocess crashes, we fatally abort from ueventd.  init will restart ueventd when
     // init reaps it, and the cold boot process will start again.  If this continues to fail, then
-    // since ueventd is marked as a critical service, init will reboot to recovery.
+    // since ueventd is marked as a critical service, init will reboot to bootloader.
     //
     // When a subprocess gets stuck, keep ueventd spinning waiting for it.  init has a timeout for
     // cold boot and will reboot to the bootloader if ueventd does not complete in time.
@@ -211,43 +214,10 @@
 
     WaitForSubProcesses();
 
-    close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
+    android::base::SetProperty(kColdBootDoneProp, "true");
     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, &sysfs_permissions, nullptr));
-    parser.AddSingleLineParser("/dev/",
-                               std::bind(ParsePermissionsLine, _1, 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");
-
-    auto boot_devices = fs_mgr_get_boot_devices();
-    return DeviceHandler(std::move(dev_permissions), std::move(sysfs_permissions),
-                         std::move(subsystems), std::move(boot_devices), true);
-}
-
 int ueventd_main(int argc, char** argv) {
     /*
      * init sets the umask to 077 for forked processes. We need to
@@ -256,21 +226,45 @@
      */
     umask(000);
 
-    InitKernelLogging(argv);
+    android::base::InitLogging(argv, &android::base::KernelLogger);
 
     LOG(INFO) << "ueventd started!";
 
     SelinuxSetupKernelLogging();
     SelabelInitialize();
 
-    DeviceHandler device_handler = CreateDeviceHandler();
-    UeventListener uevent_listener;
+    std::vector<std::unique_ptr<UeventHandler>> uevent_handlers;
 
-    if (access(COLDBOOT_DONE, F_OK) != 0) {
-        ColdBoot cold_boot(uevent_listener, device_handler);
+    // 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"});
+
+    uevent_handlers.emplace_back(std::make_unique<DeviceHandler>(
+            std::move(ueventd_configuration.dev_permissions),
+            std::move(ueventd_configuration.sysfs_permissions),
+            std::move(ueventd_configuration.subsystems), android::fs_mgr::GetBootDevices(), true));
+    uevent_handlers.emplace_back(std::make_unique<FirmwareHandler>(
+            std::move(ueventd_configuration.firmware_directories)));
+
+    if (ueventd_configuration.enable_modalias_handling) {
+        std::vector<std::string> base_paths = {"/odm/lib/modules", "/vendor/lib/modules"};
+        uevent_handlers.emplace_back(std::make_unique<ModaliasHandler>(base_paths));
+    }
+    UeventListener uevent_listener(ueventd_configuration.uevent_socket_rcvbuf_size);
+
+    if (!android::base::GetBoolProperty(kColdBootDoneProp, false)) {
+        ColdBoot cold_boot(uevent_listener, uevent_handlers);
         cold_boot.Run();
     }
 
+    for (auto& uevent_handler : uevent_handlers) {
+        uevent_handler->ColdbootDone();
+    }
+
     // We use waitpid() in ColdBoot, so we can't ignore SIGCHLD until now.
     signal(SIGCHLD, SIG_IGN);
     // Reap and pending children that exited between the last call to waitpid() and setting SIG_IGN
@@ -278,9 +272,10 @@
     while (waitpid(-1, nullptr, WNOHANG) > 0) {
     }
 
-    uevent_listener.Poll([&device_handler](const Uevent& uevent) {
-        HandleFirmwareEvent(uevent);
-        device_handler.HandleDeviceEvent(uevent);
+    uevent_listener.Poll([&uevent_handlers](const Uevent& uevent) {
+        for (auto& uevent_handler : uevent_handlers) {
+            uevent_handler->HandleUevent(uevent);
+        }
         return ListenerAction::kContinue;
     });
 
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index f74c878..25bab93 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -19,14 +19,19 @@
 #include <grp.h>
 #include <pwd.h>
 
+#include <android-base/parseint.h>
+
 #include "keyword_map.h"
+#include "parser.h"
+
+using android::base::ParseByteCount;
 
 namespace android {
 namespace init {
 
-Result<Success> ParsePermissionsLine(std::vector<std::string>&& args,
-                                     std::vector<SysfsPermissions>* out_sysfs_permissions,
-                                     std::vector<Permissions>* out_dev_permissions) {
+Result<void> 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) {
         return Error() << "/sys/ lines must have 5 entries";
@@ -69,11 +74,71 @@
     } else {
         out_dev_permissions->emplace_back(name, perm, uid, gid);
     }
-    return Success();
+    return {};
 }
 
-Result<Success> SubsystemParser::ParseSection(std::vector<std::string>&& args,
-                                              const std::string& filename, int line) {
+Result<void> 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 {};
+}
+
+Result<void> ParseModaliasHandlingLine(std::vector<std::string>&& args,
+                                       bool* enable_modalias_handling) {
+    if (args.size() != 2) {
+        return Error() << "modalias_handling lines take exactly one parameter";
+    }
+
+    if (args[1] == "enabled") {
+        *enable_modalias_handling = true;
+    } else if (args[1] == "disabled") {
+        *enable_modalias_handling = false;
+    } else {
+        return Error() << "modalias_handling takes either 'enabled' or 'disabled' as a parameter";
+    }
+
+    return {};
+}
+
+Result<void> ParseUeventSocketRcvbufSizeLine(std::vector<std::string>&& args,
+                                             size_t* uevent_socket_rcvbuf_size) {
+    if (args.size() != 2) {
+        return Error() << "uevent_socket_rcvbuf_size lines take exactly one parameter";
+    }
+
+    size_t parsed_size;
+    if (!ParseByteCount(args[1], &parsed_size)) {
+        return Error() << "could not parse size '" << args[1] << "' for uevent_socket_rcvbuf_line";
+    }
+
+    *uevent_socket_rcvbuf_size = parsed_size;
+
+    return {};
+}
+
+class SubsystemParser : public SectionParser {
+  public:
+    SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
+    Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                              int line) override;
+    Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
+    Result<void> EndSection() override;
+
+  private:
+    Result<void> ParseDevName(std::vector<std::string>&& args);
+    Result<void> ParseDirName(std::vector<std::string>&& args);
+
+    Subsystem subsystem_;
+    std::vector<Subsystem>* subsystems_;
+};
+
+Result<void> SubsystemParser::ParseSection(std::vector<std::string>&& args,
+                                           const std::string& filename, int line) {
     if (args.size() != 2) {
         return Error() << "subsystems must have exactly one name";
     }
@@ -84,33 +149,33 @@
 
     subsystem_ = Subsystem(std::move(args[1]));
 
-    return Success();
+    return {};
 }
 
-Result<Success> SubsystemParser::ParseDevName(std::vector<std::string>&& args) {
+Result<void> SubsystemParser::ParseDevName(std::vector<std::string>&& args) {
     if (args[1] == "uevent_devname") {
-        subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
-        return Success();
+        subsystem_.devname_source_ = Subsystem::DEVNAME_UEVENT_DEVNAME;
+        return {};
     }
     if (args[1] == "uevent_devpath") {
-        subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
-        return Success();
+        subsystem_.devname_source_ = Subsystem::DEVNAME_UEVENT_DEVPATH;
+        return {};
     }
 
     return Error() << "invalid devname '" << args[1] << "'";
 }
 
-Result<Success> SubsystemParser::ParseDirName(std::vector<std::string>&& args) {
+Result<void> SubsystemParser::ParseDirName(std::vector<std::string>&& args) {
     if (args[1].front() != '/') {
         return Error() << "dirname '" << args[1] << " ' does not start with '/'";
     }
 
     subsystem_.dir_name_ = args[1];
-    return Success();
+    return {};
 }
 
-Result<Success> SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line) {
-    using OptionParser = Result<Success> (SubsystemParser::*)(std::vector<std::string> && args);
+Result<void> SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+    using OptionParser = Result<void> (SubsystemParser::*)(std::vector<std::string> && args);
 
     static class OptionParserMap : public KeywordMap<OptionParser> {
       private:
@@ -132,10 +197,40 @@
     return std::invoke(*parser, this, std::move(args));
 }
 
-Result<Success> SubsystemParser::EndSection() {
+Result<void> SubsystemParser::EndSection() {
     subsystems_->emplace_back(std::move(subsystem_));
 
-    return Success();
+    return {};
+}
+
+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));
+    parser.AddSingleLineParser("modalias_handling",
+                               std::bind(ParseModaliasHandlingLine, _1,
+                                         &ueventd_configuration.enable_modalias_handling));
+    parser.AddSingleLineParser("uevent_socket_rcvbuf_size",
+                               std::bind(ParseUeventSocketRcvbufSizeLine, _1,
+                                         &ueventd_configuration.uevent_socket_rcvbuf_size));
+
+    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 83684f3..d476dec 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -21,30 +21,20 @@
 #include <vector>
 
 #include "devices.h"
-#include "parser.h"
 
 namespace android {
 namespace init {
 
-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_;
+struct UeventdConfiguration {
+    std::vector<Subsystem> subsystems;
+    std::vector<SysfsPermissions> sysfs_permissions;
+    std::vector<Permissions> dev_permissions;
+    std::vector<std::string> firmware_directories;
+    bool enable_modalias_handling = false;
+    size_t uevent_socket_rcvbuf_size = 0;
 };
 
-Result<Success> ParsePermissionsLine(std::vector<std::string>&& args,
-                                     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..9c1cedf
--- /dev/null
+++ b/init/ueventd_parser_test.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 "ueventd_parser.h"
+
+#include <android-base/file.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, UeventSocketRcvbufSize) {
+    auto ueventd_file = R"(
+uevent_socket_rcvbuf_size 8k
+uevent_socket_rcvbuf_size 8M
+)";
+
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, false, 8 * 1024 * 1024});
+}
+
+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
+
+uevent_socket_rcvbuf_size 6M
+
+#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",
+    };
+
+    size_t uevent_socket_rcvbuf_size = 6 * 1024 * 1024;
+
+    TestUeventdFile(ueventd_file, {subsystems, sysfs_permissions, permissions, firmware_directories,
+                                   false, uevent_socket_rcvbuf_size});
+}
+
+// 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
+
+uevent_socket_rcvbuf_size blah
+
+subsystem #no name
+
+)";
+
+    TestUeventdFile(ueventd_file, {});
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp
index 7290051..bfdc28e 100644
--- a/init/ueventd_test.cpp
+++ b/init/ueventd_test.cpp
@@ -27,7 +27,6 @@
 
 #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>
diff --git a/init/util.cpp b/init/util.cpp
index 4455b2e..058a111 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -33,27 +33,19 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
-#include <android-base/stringprintf.h>
+#include <android-base/properties.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 <android-base/properties.h>
-
-#include "selinux.h"
+#include "reboot_utils.h"
+#include "selabel.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"
-#endif
-
 using android::base::boot_clock;
 using namespace std::literals::string_literals;
 
@@ -201,7 +193,7 @@
     return rc;
 }
 
-Result<Success> WriteFile(const std::string& path, const std::string& content) {
+Result<void> WriteFile(const std::string& path, const std::string& content) {
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(
         OpenFile(path, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
     if (fd == -1) {
@@ -210,7 +202,7 @@
     if (!android::base::WriteStringToFd(content, fd)) {
         return ErrnoError() << "Unable to write file contents";
     }
-    return Success();
+    return {};
 }
 
 bool mkdir_recursive(const std::string& path, mode_t mode) {
@@ -273,16 +265,6 @@
 }
 
 /*
- * Writes hex_len hex characters (1/2 byte) to hex from bytes.
- */
-std::string bytes_to_hex(const uint8_t* bytes, size_t bytes_len) {
-    std::string hex("0x");
-    for (size_t i = 0; i < bytes_len; i++)
-        android::base::StringAppendF(&hex, "%02x", bytes[i]);
-    return hex;
-}
-
-/*
  * Returns true is pathname is a directory
  */
 bool is_dir(const char* pathname) {
@@ -440,5 +422,55 @@
     return true;
 }
 
+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;
+    }
+
+    InitFatalReboot();
+}
+
+// The kernel opens /dev/console and uses that fd for stdin/stdout/stderr if there is a serial
+// console enabled and no initramfs, otherwise it does not provide any fds for stdin/stdout/stderr.
+// SetStdioToDevNull() is used to close these existing fds if they exist and replace them with
+// /dev/null regardless.
+//
+// In the case that these fds are provided by the kernel, the exec of second stage init causes an
+// SELinux denial as it does not have access to /dev/console.  In the case that they are not
+// provided, exec of any further process is potentially dangerous as the first fd's opened by that
+// process will take the stdin/stdout/stderr fileno's, which can cause issues if printf(), etc is
+// then used by that process.
+//
+// Lastly, simply calling SetStdioToDevNull() in first stage init is not enough, since first
+// stage init still runs in kernel context, future child processes will not have permissions to
+// access any fds that it opens, including the one opened below for /dev/null.  Therefore,
+// SetStdioToDevNull() must be called again in second stage init.
+void SetStdioToDevNull(char** argv) {
+    // Make stdin/stdout/stderr all point to /dev/null.
+    int fd = open("/dev/null", O_RDWR);  // NOLINT(android-cloexec-open)
+    if (fd == -1) {
+        int saved_errno = errno;
+        android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
+        errno = saved_errno;
+        PLOG(FATAL) << "Couldn't open /dev/null";
+    }
+    dup2(fd, STDIN_FILENO);
+    dup2(fd, STDOUT_FILENO);
+    dup2(fd, STDERR_FILENO);
+    if (fd > STDERR_FILENO) close(fd);
+}
+
+void InitKernelLogging(char** argv) {
+    SetFatalRebootTarget();
+    android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
+}
+
+bool IsRecoveryMode() {
+    return access("/system/bin/recovery", F_OK) == 0;
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/util.h b/init/util.h
index 07e4864..1929cb5 100644
--- a/init/util.h
+++ b/init/util.h
@@ -30,19 +30,19 @@
 
 #include "result.h"
 
-#define COLDBOOT_DONE "/dev/.coldboot_done"
-
 using android::base::boot_clock;
 using namespace std::chrono_literals;
 
 namespace android {
 namespace init {
 
+static const char kColdBootDoneProp[] = "ro.cold_boot_done";
+
 int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
                  const char* socketcon);
 
 Result<std::string> ReadFile(const std::string& path);
-Result<Success> WriteFile(const std::string& path, const std::string& content);
+Result<void> WriteFile(const std::string& path, const std::string& content);
 
 Result<uid_t> DecodeUid(const std::string& name);
 
@@ -51,7 +51,6 @@
 void import_kernel_cmdline(bool in_qemu,
                            const std::function<void(const std::string&, const std::string&, bool)>&);
 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);
 
@@ -64,6 +63,9 @@
 
 bool IsLegalPropertyName(const std::string& name);
 
+void SetStdioToDevNull(char** argv);
+void InitKernelLogging(char** argv);
+bool IsRecoveryMode();
 }  // namespace init
 }  // namespace android
 
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 3ae53a4..8947256 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -20,8 +20,8 @@
 #include <fcntl.h>
 #include <sys/stat.h>
 
+#include <android-base/file.h>
 #include <android-base/stringprintf.h>
-#include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
 using namespace std::literals::string_literals;
@@ -34,7 +34,7 @@
     auto file_contents = ReadFile("/proc/does-not-exist");
     EXPECT_EQ(ENOENT, errno);
     ASSERT_FALSE(file_contents);
-    EXPECT_EQ("open() failed: No such file or directory", file_contents.error_string());
+    EXPECT_EQ("open() failed: No such file or directory", file_contents.error().message());
 }
 
 TEST(util, ReadFileGroupWriteable) {
@@ -45,7 +45,7 @@
     EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
     auto file_contents = ReadFile(tf.path);
     ASSERT_FALSE(file_contents) << strerror(errno);
-    EXPECT_EQ("Skipping insecure file", file_contents.error_string());
+    EXPECT_EQ("Skipping insecure file", file_contents.error().message());
 }
 
 TEST(util, ReadFileWorldWiteable) {
@@ -56,7 +56,7 @@
     EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
     auto file_contents = ReadFile(tf.path);
     ASSERT_FALSE(file_contents) << strerror(errno);
-    EXPECT_EQ("Skipping insecure file", file_contents.error_string());
+    EXPECT_EQ("Skipping insecure file", file_contents.error().message());
 }
 
 TEST(util, ReadFileSymbolicLink) {
@@ -65,7 +65,8 @@
     auto file_contents = ReadFile("/charger");
     EXPECT_EQ(ELOOP, errno);
     ASSERT_FALSE(file_contents);
-    EXPECT_EQ("open() failed: Too many symbolic links encountered", file_contents.error_string());
+    EXPECT_EQ("open() failed: Too many symbolic links encountered",
+              file_contents.error().message());
 }
 
 TEST(util, ReadFileSuccess) {
@@ -130,7 +131,7 @@
 
     decoded_uid = DecodeUid("toot");
     EXPECT_FALSE(decoded_uid);
-    EXPECT_EQ("getpwnam failed: No such file or directory", decoded_uid.error_string());
+    EXPECT_EQ("getpwnam failed: No such file or directory", decoded_uid.error().message());
 
     decoded_uid = DecodeUid("123");
     EXPECT_TRUE(decoded_uid);
diff --git a/init/watchdogd.cpp b/init/watchdogd.cpp
deleted file mode 100644
index e0164b4..0000000
--- a/init/watchdogd.cpp
+++ /dev/null
@@ -1,81 +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 <errno.h>
-#include <fcntl.h>
-#include <linux/watchdog.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#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
-
-#define DEV_NAME "/dev/watchdog"
-
-namespace android {
-namespace init {
-
-int watchdogd_main(int argc, char **argv) {
-    InitKernelLogging(argv);
-
-    int interval = 10;
-    if (argc >= 2) interval = atoi(argv[1]);
-
-    int margin = 10;
-    if (argc >= 3) margin = atoi(argv[2]);
-
-    LOG(INFO) << "watchdogd started (interval " << interval << ", margin " << margin << ")!";
-
-    int fd = open(DEV_NAME, O_RDWR|O_CLOEXEC);
-    if (fd == -1) {
-        PLOG(ERROR) << "Failed to open " << DEV_NAME;
-        return 1;
-    }
-
-    int timeout = interval + margin;
-    int ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
-    if (ret) {
-        PLOG(ERROR) << "Failed to set timeout to " << timeout;
-        ret = ioctl(fd, WDIOC_GETTIMEOUT, &timeout);
-        if (ret) {
-            PLOG(ERROR) << "Failed to get timeout";
-        } else {
-            if (timeout > margin) {
-                interval = timeout - margin;
-            } else {
-                interval = 1;
-            }
-            LOG(WARNING) << "Adjusted interval to timeout returned by driver: "
-                         << "timeout " << timeout
-                         << ", interval " << interval
-                         << ", margin " << margin;
-        }
-    }
-
-    while (true) {
-        write(fd, "", 1);
-        sleep(interval);
-    }
-}
-
-}  // namespace init
-}  // namespace android
diff --git a/init/watchdogd.h b/init/watchdogd.h
deleted file mode 100644
index 73f77d5..0000000
--- a/init/watchdogd.h
+++ /dev/null
@@ -1,28 +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 _INIT_WATCHDOGD_H_
-#define _INIT_WATCHDOGD_H_
-
-namespace android {
-namespace init {
-
-int watchdogd_main(int argc, char **argv);
-
-}  // namespace init
-}  // namespace android
-
-#endif
diff --git a/janitors/OWNERS b/janitors/OWNERS
new file mode 100644
index 0000000..3e32c26
--- /dev/null
+++ b/janitors/OWNERS
@@ -0,0 +1,6 @@
+# OWNERS file for projects that don't really have owners so much as volunteer janitors.
+ccross@google.com
+dwillemsen@google.com
+enh@google.com
+hhb@google.com
+narayan@google.com
diff --git a/libappfuse/FuseBridgeLoop.cc b/libappfuse/FuseBridgeLoop.cc
index 8b0c53e..f1ca446 100644
--- a/libappfuse/FuseBridgeLoop.cc
+++ b/libappfuse/FuseBridgeLoop.cc
@@ -311,7 +311,7 @@
 };
 
 FuseBridgeLoop::FuseBridgeLoop() : opened_(true) {
-    base::unique_fd epoll_fd(epoll_create1(/* no flag */ 0));
+    base::unique_fd epoll_fd(epoll_create1(EPOLL_CLOEXEC));
     if (epoll_fd.get() == -1) {
         PLOG(ERROR) << "Failed to open FD for epoll";
         opened_ = false;
@@ -353,8 +353,8 @@
         }
         if (entry->IsClosing()) {
             const int mount_id = entry->mount_id();
-            callback->OnClosed(mount_id);
             bridges_.erase(mount_id);
+            callback->OnClosed(mount_id);
             if (bridges_.size() == 0) {
                 // All bridges are now closed.
                 return false;
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
index 8a2afea..4ab439d 100644
--- a/libasyncio/Android.bp
+++ b/libasyncio/Android.bp
@@ -27,6 +27,7 @@
     name: "libasyncio",
     defaults: ["libasyncio_defaults"],
     vendor_available: true,
+    recovery_available: true,
     host_supported: true,
     srcs: [
         "AsyncIO.cpp",
diff --git a/libasyncio/AsyncIO.cpp b/libasyncio/AsyncIO.cpp
index 7430bc8..6149f09 100644
--- a/libasyncio/AsyncIO.cpp
+++ b/libasyncio/AsyncIO.cpp
@@ -17,9 +17,10 @@
 #include <asyncio/AsyncIO.h>
 #include <sys/syscall.h>
 #include <unistd.h>
+#include <cstdint>
+#include <cstring>
 
 int io_setup(unsigned nr, aio_context_t* ctxp) {
-    memset(ctxp, 0, sizeof(*ctxp));
     return syscall(__NR_io_setup, nr, ctxp);
 }
 
@@ -48,3 +49,11 @@
     iocb->aio_nbytes = count;
     iocb->aio_offset = offset;
 }
+
+void io_prep_pread(struct iocb* iocb, int fd, void* buf, size_t count, long long offset) {
+    io_prep(iocb, fd, buf, count, offset, true);
+}
+
+void io_prep_pwrite(struct iocb* iocb, int fd, void* buf, size_t count, long long offset) {
+    io_prep(iocb, fd, buf, count, offset, false);
+}
diff --git a/libasyncio/include/asyncio/AsyncIO.h b/libasyncio/include/asyncio/AsyncIO.h
index e3fb93a..9620d2a 100644
--- a/libasyncio/include/asyncio/AsyncIO.h
+++ b/libasyncio/include/asyncio/AsyncIO.h
@@ -17,9 +17,9 @@
 #ifndef _ASYNCIO_H
 #define _ASYNCIO_H
 
-#include <cstring>
-#include <cstdint>
 #include <linux/aio_abi.h>
+#include <stdbool.h>
+#include <stdint.h>
 #include <sys/cdefs.h>
 #include <sys/types.h>
 #include <time.h>
@@ -35,10 +35,14 @@
 
 int io_setup(unsigned nr, aio_context_t* ctxp);
 int io_destroy(aio_context_t ctx);
-int io_submit(aio_context_t ctx, long nr, iocb** iocbpp);
-int io_getevents(aio_context_t ctx, long min_nr, long max_nr, io_event* events, timespec* timeout);
-int io_cancel(aio_context_t ctx, iocb*, io_event* result);
-void io_prep(iocb* iocb, int fd, const void* buf, uint64_t count, int64_t offset, bool read);
+int io_submit(aio_context_t ctx, long nr, struct iocb** iocbpp);
+int io_getevents(aio_context_t ctx, long min_nr, long max_nr, struct io_event* events,
+                 struct timespec* timeout);
+int io_cancel(aio_context_t ctx, struct iocb*, struct io_event* result);
+
+void io_prep_pread(struct iocb* iocb, int fd, void* buf, size_t count, long long offset);
+void io_prep_pwrite(struct iocb* iocb, int fd, void* buf, size_t count, long long offset);
+void io_prep(struct iocb* iocb, int fd, const void* buf, uint64_t count, int64_t offset, bool read);
 
 #ifdef __cplusplus
 };
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 4bd01d2..9ece847 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -22,32 +22,17 @@
         "-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,
         },
     },
-
-    multilib: {
-        lib32: {
-            suffix: "32",
-        },
-        lib64: {
-            suffix: "64",
-        },
-    },
 }
 
 libbacktrace_sources = [
     "Backtrace.cpp",
     "BacktraceCurrent.cpp",
     "BacktracePtrace.cpp",
-    "thread_utils.c",
     "ThreadEntry.cpp",
     "UnwindStack.cpp",
     "UnwindStackMap.cpp",
@@ -56,12 +41,15 @@
 cc_library_headers {
     name: "libbacktrace_headers",
     vendor_available: true,
+    recovery_available: true,
+    native_bridge_supported: true,
     export_include_dirs: ["include"],
 }
 
 cc_library {
     name: "libbacktrace",
     vendor_available: false,
+    recovery_available: true,
     vndk: {
         enabled: true,
         support_system_process: true,
@@ -92,33 +80,27 @@
             shared_libs: [
                 "libbase",
                 "liblog",
-                "libunwind",
                 "libunwindstack",
-                "libdexfile",
             ],
 
-            static_libs: ["libcutils"],
-
-            // libdexfile will eventually properly export headers, for now
-            // include these directly.
-            include_dirs: [
-                "art/runtime",
+            static_libs: [
+                "libprocinfo",
             ],
-
-            header_libs: ["jni_headers"],
         },
         android: {
             static_libs: ["libasync_safe"],
         },
         vendor: {
             cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
-            exclude_shared_libs: ["libdexfile"],
+        },
+        recovery: {
+            cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
         },
     },
     whole_static_libs: ["libdemangle"],
 }
 
-cc_library_shared {
+cc_test_library {
     name: "libbacktrace_test",
     defaults: ["libbacktrace_common"],
     host_supported: true,
@@ -131,6 +113,17 @@
     shared_libs: [
         "libunwindstack",
     ],
+    relative_install_path: "backtrace_test_libs",
+
+    target: {
+        linux_glibc: {
+            // This forces the creation of eh_frame with unwind information
+            // for host.
+            cflags: [
+                "-fcxx-exceptions"
+            ],
+        },
+    },
 }
 
 //-------------------------------------------------------------------------
@@ -138,13 +131,11 @@
 //-------------------------------------------------------------------------
 cc_test {
     name: "backtrace_test",
+    isolated: true,
     defaults: ["libbacktrace_common"],
     host_supported: true,
     srcs: [
-        "backtrace_offline_test.cpp",
         "backtrace_test.cpp",
-        "GetPss.cpp",
-        "thread_utils.c",
     ],
 
     cflags: [
@@ -154,41 +145,29 @@
     ],
 
     shared_libs: [
-        "libbacktrace_test",
         "libbacktrace",
-        "libdexfile",
         "libbase",
-        "libcutils",
         "liblog",
         "libunwindstack",
     ],
 
     group_static_libs: true,
 
-    target: {
-        android: {
-            cflags: ["-DENABLE_PSS_TESTS"],
-            shared_libs: [
-                "libutils",
-            ],
-        },
-        linux_glibc: {
-            static_libs: ["libutils"],
-        },
-    },
-
-    // libdexfile will eventually properly export headers, for now
-    // include these directly.
-    include_dirs: [
-        "art/runtime",
+    // So that the dlopen can find the libbacktrace_test.so.
+    ldflags: [
+        "-Wl,--rpath,${ORIGIN}/../backtrace_test_libs",
     ],
 
+    test_suites: ["device-tests"],
     data: [
         "testdata/arm/*",
         "testdata/arm64/*",
         "testdata/x86/*",
         "testdata/x86_64/*",
     ],
+    required: [
+        "libbacktrace_test",
+    ],
 }
 
 cc_benchmark {
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 6445a7c..71980d7 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>
@@ -31,7 +32,6 @@
 
 #include "BacktraceLog.h"
 #include "UnwindStack.h"
-#include "thread_utils.h"
 
 using android::base::StringPrintf;
 
@@ -124,7 +124,7 @@
   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;
@@ -170,5 +170,7 @@
       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";
+    case BACKTRACE_UNWIND_ERROR_INVALID_ELF:
+      return "Failed to unwind due to invalid elf";
   }
 }
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index f6f4423..038b59e 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -28,13 +28,13 @@
 
 #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(uint64_t ptr, word_t* out_value) {
   if (!VerifyReadWordArgs(ptr, out_value)) {
@@ -76,7 +76,7 @@
     return UnwindFromContext(num_ignore_frames, ucontext);
   }
 
-  if (Tid() != gettid()) {
+  if (Tid() != static_cast<pid_t>(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;
   }
 
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index bdae140..781819a 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -28,8 +28,9 @@
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 #include <backtrace/backtrace_constants.h>
-
-#include "thread_utils.h"
+#if defined(__linux__)
+#include <procinfo/process_map.h>
+#endif
 
 using android::base::StringPrintf;
 
@@ -45,8 +46,7 @@
   }
 }
 
-BacktraceMap::~BacktraceMap() {
-}
+BacktraceMap::~BacktraceMap() {}
 
 void BacktraceMap::FillIn(uint64_t addr, backtrace_map_t* map) {
   ScopedBacktraceMapIteratorLock lock(this);
@@ -60,27 +60,20 @@
   *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
+  // 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) {
     return false;
   }
 
@@ -97,51 +90,48 @@
     map->flags |= PROT_EXEC;
   }
 
-  map->name = line+name_pos;
-  if (!map->name.empty() && map->name[map->name.length()-1] == '\n') {
-    map->name.erase(map->name.length()-1);
+  map->name = line + name_pos;
+  if (!map->name.empty() && map->name[map->name.length() - 1] == '\n') {
+    map->name.erase(map->name.length() - 1);
   }
 
-  ALOGV("Parsed map: start=%p, end=%p, flags=%x, name=%s",
-        reinterpret_cast<void*>(map->start), reinterpret_cast<void*>(map->end),
-        map->flags, map->name.c_str());
+  ALOGV("Parsed map: start=%p, end=%p, flags=%x, name=%s", reinterpret_cast<void*>(map->start),
+        reinterpret_cast<void*>(map->end), 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
+      cmd[sizeof(pid_t) * 3 + sizeof("vmmap -w -resident -submap -allSplitLibs -interleaved ") + 1];
   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;
   }
 
-  while(fgets(line, sizeof(line), fp)) {
+  while (fgets(line, sizeof(line), fp)) {
     backtrace_map_t map;
     if (ParseLine(line, &map)) {
       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, ino_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__)
diff --git a/libbacktrace/BacktracePtrace.cpp b/libbacktrace/BacktracePtrace.cpp
index bf6b16f..9da457d 100644
--- a/libbacktrace/BacktracePtrace.cpp
+++ b/libbacktrace/BacktracePtrace.cpp
@@ -28,7 +28,6 @@
 
 #include "BacktraceLog.h"
 #include "BacktracePtrace.h"
-#include "thread_utils.h"
 
 #if !defined(__APPLE__)
 static bool PtraceRead(pid_t tid, uint64_t addr, word_t* out_value) {
diff --git a/libbacktrace/BacktraceTest.h b/libbacktrace/BacktraceTest.h
new file mode 100644
index 0000000..c38af04
--- /dev/null
+++ b/libbacktrace/BacktraceTest.h
@@ -0,0 +1,78 @@
+/*
+ * 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_BACKTRACE_TEST_H
+#define _LIBBACKTRACE_BACKTRACE_TEST_H
+
+#include <dlfcn.h>
+
+#include <gtest/gtest.h>
+
+class BacktraceTest : public ::testing::Test {
+ protected:
+  static void SetUpTestCase() {
+    dl_handle_ = dlopen("libbacktrace_test.so", RTLD_NOW | RTLD_LOCAL);
+
+    test_level_one_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+        dlsym(dl_handle_, "test_level_one"));
+
+    test_level_two_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+        dlsym(dl_handle_, "test_level_two"));
+
+    test_level_three_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+        dlsym(dl_handle_, "test_level_three"));
+
+    test_level_four_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+        dlsym(dl_handle_, "test_level_four"));
+
+    test_recursive_call_ = reinterpret_cast<int (*)(int, void (*)(void*), void*)>(
+        dlsym(dl_handle_, "test_recursive_call"));
+
+    test_get_context_and_wait_ = reinterpret_cast<void (*)(void*, volatile int*)>(
+        dlsym(dl_handle_, "test_get_context_and_wait"));
+
+    test_signal_action_ =
+        reinterpret_cast<void (*)(int, siginfo_t*, void*)>(dlsym(dl_handle_, "test_signal_action"));
+
+    test_signal_handler_ =
+        reinterpret_cast<void (*)(int)>(dlsym(dl_handle_, "test_signal_handler"));
+  }
+
+  void SetUp() override {
+    ASSERT_TRUE(dl_handle_ != nullptr);
+    ASSERT_TRUE(test_level_one_ != nullptr);
+    ASSERT_TRUE(test_level_two_ != nullptr);
+    ASSERT_TRUE(test_level_three_ != nullptr);
+    ASSERT_TRUE(test_level_four_ != nullptr);
+    ASSERT_TRUE(test_recursive_call_ != nullptr);
+    ASSERT_TRUE(test_get_context_and_wait_ != nullptr);
+    ASSERT_TRUE(test_signal_action_ != nullptr);
+    ASSERT_TRUE(test_signal_handler_ != nullptr);
+  }
+
+ public:
+  static void* dl_handle_;
+  static int (*test_level_one_)(int, int, int, int, void (*)(void*), void*);
+  static int (*test_level_two_)(int, int, int, int, void (*)(void*), void*);
+  static int (*test_level_three_)(int, int, int, int, void (*)(void*), void*);
+  static int (*test_level_four_)(int, int, int, int, void (*)(void*), void*);
+  static int (*test_recursive_call_)(int, void (*)(void*), void*);
+  static void (*test_get_context_and_wait_)(void*, volatile int*);
+  static void (*test_signal_action_)(int, siginfo_t*, void*);
+  static void (*test_signal_handler_)(int);
+};
+
+#endif  // _LIBBACKTRACE_BACKTRACE_TEST_H
diff --git a/libbacktrace/GetPss.cpp b/libbacktrace/GetPss.cpp
deleted file mode 100644
index 6d750ea..0000000
--- a/libbacktrace/GetPss.cpp
+++ /dev/null
@@ -1,94 +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 <inttypes.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-// This is an extremely simplified version of libpagemap.
-
-#define _BITS(x, offset, bits) (((x) >> (offset)) & ((1LL << (bits)) - 1))
-
-#define PAGEMAP_PRESENT(x)     (_BITS(x, 63, 1))
-#define PAGEMAP_SWAPPED(x)     (_BITS(x, 62, 1))
-#define PAGEMAP_SHIFT(x)       (_BITS(x, 55, 6))
-#define PAGEMAP_PFN(x)         (_BITS(x, 0, 55))
-#define PAGEMAP_SWAP_OFFSET(x) (_BITS(x, 5, 50))
-#define PAGEMAP_SWAP_TYPE(x)   (_BITS(x, 0,  5))
-
-static bool ReadData(int fd, off_t place, uint64_t *data) {
-  if (lseek(fd, place * sizeof(uint64_t), SEEK_SET) < 0) {
-    return false;
-  }
-  if (read(fd, (void*)data, sizeof(uint64_t)) != (ssize_t)sizeof(uint64_t)) {
-    return false;
-  }
-  return true;
-}
-
-size_t GetPssBytes() {
-  FILE* maps = fopen("/proc/self/maps", "r");
-  if (maps == nullptr) {
-    return 0;
-  }
-
-  int pagecount_fd = open("/proc/kpagecount", O_RDONLY);
-  if (pagecount_fd == -1) {
-    fclose(maps);
-    return 0;
-  }
-
-  int pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
-  if (pagemap_fd == -1) {
-    fclose(maps);
-    close(pagecount_fd);
-    return 0;
-  }
-
-  char line[4096];
-  size_t total_pss = 0;
-  int pagesize = getpagesize();
-  while (fgets(line, sizeof(line), maps)) {
-    uintptr_t start, end;
-    if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " ", &start, &end) != 2) {
-      total_pss = 0;
-      break;
-    }
-    for (off_t page = static_cast<off_t>(start/pagesize);
-         page < static_cast<off_t>(end/pagesize); page++) {
-      uint64_t data;
-      if (ReadData(pagemap_fd, page, &data)) {
-        if (PAGEMAP_PRESENT(data) && !PAGEMAP_SWAPPED(data)) {
-          uint64_t count;
-          if (ReadData(pagecount_fd, static_cast<off_t>(PAGEMAP_PFN(data)), &count)) {
-            total_pss += (count >= 1) ? pagesize / count : 0;
-          }
-        }
-      }
-    }
-  }
-
-  fclose(maps);
-
-  close(pagecount_fd);
-  close(pagemap_fd);
-
-  return total_pss;
-}
diff --git a/libbacktrace/GetPss.h b/libbacktrace/GetPss.h
deleted file mode 100644
index 787c33d..0000000
--- a/libbacktrace/GetPss.h
+++ /dev/null
@@ -1,22 +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.
- */
-
-#ifndef _LIBBACKTRACE_GET_PSS_H
-#define _LIBBACKTRACE_GET_PSS_H
-
-size_t GetPssBytes();
-
-#endif // _LIBBACKTRACE_GET_PSS_H
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index e087b2e..a128623 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -23,10 +23,6 @@
 #include <set>
 #include <string>
 
-#if !defined(__ANDROID__)
-#include <cutils/threads.h>
-#endif
-
 #include <backtrace/Backtrace.h>
 #include <demangle.h>
 #include <unwindstack/Elf.h>
@@ -53,6 +49,7 @@
   unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(),
                                  regs, stack_map->process_memory());
   unwinder.SetResolveNames(stack_map->ResolveNames());
+  stack_map->SetArch(regs->Arch());
   if (stack_map->GetJitDebug() != nullptr) {
     unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch());
   }
@@ -92,6 +89,10 @@
       case unwindstack::ERROR_REPEATED_FRAME:
         error->error_code = BACKTRACE_UNWIND_ERROR_REPEATED_FRAME;
         break;
+
+      case unwindstack::ERROR_INVALID_ELF:
+        error->error_code = BACKTRACE_UNWIND_ERROR_INVALID_ELF;
+        break;
     }
   }
 
@@ -120,7 +121,7 @@
     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.offset = frame->map_elf_start_offset;
     back_frame->map.load_bias = frame->map_load_bias;
     back_frame->map.flags = frame->map_flags;
   }
@@ -128,22 +129,6 @@
   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) {}
 
@@ -170,7 +155,7 @@
 }
 
 UnwindStackPtrace::UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
-    : BacktracePtrace(pid, tid, map), memory_(pid) {}
+    : BacktracePtrace(pid, tid, map), memory_(unwindstack::Memory::CreateProcessMemory(pid)) {}
 
 std::string UnwindStackPtrace::GetFunctionNameRaw(uint64_t pc, uint64_t* offset) {
   return GetMap()->GetFunctionName(pc, offset);
@@ -188,73 +173,5 @@
 }
 
 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);
+  return memory_->Read(addr, buffer, bytes);
 }
diff --git a/libbacktrace/UnwindStack.h b/libbacktrace/UnwindStack.h
index 33c4282..47f6757 100644
--- a/libbacktrace/UnwindStack.h
+++ b/libbacktrace/UnwindStack.h
@@ -19,6 +19,7 @@
 
 #include <stdint.h>
 
+#include <memory>
 #include <string>
 
 #include <backtrace/BacktraceMap.h>
@@ -49,23 +50,7 @@
   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_;
+  std::shared_ptr<unwindstack::Memory> memory_;
 };
 
 #endif  // _LIBBACKTRACE_UNWIND_STACK_H
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index d2d6ab8..aa0b17c 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -25,6 +25,7 @@
 #include <unwindstack/Elf.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Maps.h>
+#include <unwindstack/Regs.h>
 
 #include "UnwindStackMap.h"
 
@@ -54,7 +55,7 @@
   }
 
   // Iterate through the maps and fill in the backtrace_map_t structure.
-  for (auto* map_info : *stack_maps_) {
+  for (const auto& map_info : *stack_maps_) {
     backtrace_map_t map;
     map.start = map_info->start;
     map.end = map_info->end;
@@ -106,7 +107,17 @@
     return "";
   }
 
-  unwindstack::Elf* elf = map_info->GetElf(process_memory(), true);
+  if (arch_ == unwindstack::ARCH_UNKNOWN) {
+    if (pid_ == getpid()) {
+      arch_ = unwindstack::Regs::CurrentArch();
+    } else {
+      // Create a remote regs, to figure out the architecture.
+      std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::RemoteGet(pid_));
+      arch_ = regs->Arch();
+    }
+  }
+
+  unwindstack::Elf* elf = map_info->GetElf(process_memory(), arch_);
 
   std::string name;
   uint64_t func_offset;
@@ -121,43 +132,6 @@
   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.
 //-------------------------------------------------------------------------
@@ -178,15 +152,3 @@
   }
   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
index 039f4a2..f0e7d8b 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -30,8 +30,10 @@
 #if !defined(NO_LIBDEXFILE_SUPPORT)
 #include <unwindstack/DexFiles.h>
 #endif
+#include <unwindstack/Elf.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
 
 // Forward declarations.
 class UnwindDexFile;
@@ -58,6 +60,8 @@
   unwindstack::DexFiles* GetDexFiles() { return dex_files_.get(); }
 #endif
 
+  void SetArch(unwindstack::ArchEnum arch) { arch_ = arch; }
+
  protected:
   uint64_t GetLoadBias(size_t index) override;
 
@@ -67,21 +71,8 @@
 #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;
+  unwindstack::ArchEnum arch_ = unwindstack::ARCH_UNKNOWN;
 };
 
 #endif  // _LIBBACKTRACE_UNWINDSTACK_MAP_H
diff --git a/libbacktrace/backtrace_benchmarks.cpp b/libbacktrace/backtrace_benchmarks.cpp
index a23e3b4..a93a25e 100644
--- a/libbacktrace/backtrace_benchmarks.cpp
+++ b/libbacktrace/backtrace_benchmarks.cpp
@@ -27,6 +27,7 @@
 #include <string>
 
 #include <android-base/file.h>
+#include <android-base/threads.h>
 
 #include <benchmark/benchmark.h>
 
@@ -34,10 +35,6 @@
 #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) {
@@ -92,10 +89,11 @@
         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) {
+#if defined(PR_SET_VMA)
+      if (prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, memory, PAGE_SIZE, "test_map") == -1) {
         fprintf(stderr, "Failed: %s\n", strerror(errno));
       }
+#endif
       maps.push_back(memory);
     }
 
@@ -154,7 +152,7 @@
 
 static void CreateBacktrace(benchmark::State& state, BacktraceMap* map, BacktraceCreateFn fn) {
   while (state.KeepRunning()) {
-    std::unique_ptr<Backtrace> backtrace(fn(getpid(), gettid(), map));
+    std::unique_ptr<Backtrace> backtrace(fn(getpid(), android::base::GetThreadId(), map));
     backtrace->Unwind(0);
   }
 }
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
deleted file mode 100644
index 9877f29..0000000
--- a/libbacktrace/backtrace_offline_test.cpp
+++ /dev/null
@@ -1,404 +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 <inttypes.h>
-#include <pthread.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <functional>
-#include <memory>
-#include <string>
-#include <utility>
-#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 <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
-#include <cutils/threads.h>
-
-#include <gtest/gtest.h>
-
-extern "C" {
-// Prototypes for functions in the test library.
-int test_level_one(int, int, int, int, void (*)(void*), void*);
-int test_level_two(int, int, int, int, void (*)(void*), void*);
-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(void* context, volatile int* exit_flag);
-}
-
-struct FunctionSymbol {
-  std::string name;
-  uint64_t start;
-  uint64_t end;
-};
-
-static std::vector<FunctionSymbol> GetFunctionSymbols() {
-  std::vector<FunctionSymbol> symbols = {
-      {"unknown_start", 0, 0},
-      {"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(),
-      [](const FunctionSymbol& s1, const FunctionSymbol& s2) { return s1.start < s2.start; });
-  for (size_t i = 0; i + 1 < symbols.size(); ++i) {
-    symbols[i].end = symbols[i + 1].start;
-  }
-  return symbols;
-}
-
-static std::string RawDataToHexString(const void* data, size_t size) {
-  const uint8_t* p = static_cast<const uint8_t*>(data);
-  std::string s;
-  for (size_t i = 0; i < size; ++i) {
-    s += android::base::StringPrintf("%02x", p[i]);
-  }
-  return s;
-}
-
-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);
-    data->push_back(value);
-    s += 2;
-  }
-}
-
-struct OfflineThreadArg {
-  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->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);
-  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));
-  pthread_t thread;
-  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 context information.
-  sleep(1);
-  // Copy the stack information.
-  std::vector<uint8_t> stack_data(reinterpret_cast<uint8_t*>(stack),
-                                  reinterpret_cast<uint8_t*>(stack) + stack_size);
-  arg.exit_flag = 1;
-  ASSERT_EQ(0, pthread_join(thread, nullptr));
-  ASSERT_EQ(0, munmap(stack, stack_size));
-
-  std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid()));
-  ASSERT_TRUE(map != nullptr);
-
-  backtrace_stackinfo_t stack_info;
-  stack_info.start = stack_addr;
-  stack_info.end = stack_addr + stack_size;
-  stack_info.data = stack_data.data();
-
-  // Generate offline testdata.
-  std::string testdata;
-  // 1. Dump pid, tid
-  testdata += android::base::StringPrintf("pid: %d tid: %d\n", getpid(), arg.tid);
-  // 2. Dump maps
-  for (auto it = map->begin(); it != map->end(); ++it) {
-    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 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
-  testdata += android::base::StringPrintf(
-      "stack: start: %" PRIx64 " end: %" PRIx64 " size: %zu ",
-      stack_info.start, stack_info.end, stack_data.size());
-  testdata += RawDataToHexString(stack_data.data(), stack_data.size());
-  testdata.push_back('\n');
-
-  // 5. Dump function symbols
-  std::vector<FunctionSymbol> function_symbols = GetFunctionSymbols();
-  for (const auto& symbol : function_symbols) {
-    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"));
-}
-
-// 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(uint64_t addr,
-                                          const std::vector<FunctionSymbol>& symbols) {
-  for (auto& symbol : symbols) {
-    if (addr >= symbol.start && addr < symbol.end) {
-      return symbol.name;
-    }
-  }
-  return "";
-}
-
-struct OfflineTestData {
-  int pid;
-  int tid;
-  std::vector<backtrace_map_t> maps;
-  std::vector<uint8_t> ucontext;
-  backtrace_stackinfo_t stack_info;
-  std::vector<uint8_t> stack;
-  std::vector<FunctionSymbol> symbols;
-};
-
-bool ReadOfflineTestData(const std::string offline_testdata_path, OfflineTestData* testdata) {
-  std::string s;
-  if (!android::base::ReadFileToString(offline_testdata_path, &s)) {
-    return false;
-  }
-  // Parse offline_testdata.
-  std::vector<std::string> lines = android::base::Split(s, "\n");
-  for (const auto& line : lines) {
-    if (android::base::StartsWith(line, "pid:")) {
-      sscanf(line.c_str(), "pid: %d tid: %d", &testdata->pid, &testdata->tid);
-    } else if (android::base::StartsWith(line, "map:")) {
-      testdata->maps.resize(testdata->maps.size() + 1);
-      backtrace_map_t& map = testdata->maps.back();
-      int pos;
-      sscanf(line.c_str(),
-             "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, "ucontext:")) {
-      size_t size;
-      int pos;
-      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);
-      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: %" SCNx64 " end: %" SCNx64 " name: %n", &symbol.start,
-             &symbol.end, &pos);
-      symbol.name = line.substr(pos);
-    }
-  }
-  return true;
-}
-
-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)) << "Failed " << arch_str;
-
-  // Fix path of libbacktrace_testlib.so.
-  for (auto& map : testdata.maps) {
-    if (map.name.find("libbacktrace_test.so") != 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 {
-    abort();
-  }
-
-  std::unique_ptr<Backtrace> backtrace(Backtrace::CreateOffline(
-      arch, testdata.pid, testdata.tid, testdata.maps, testdata.stack_info));
-  ASSERT_TRUE(backtrace != nullptr) << "Failed " << arch_str;
-
-  ASSERT_TRUE(backtrace->Unwind(0, testdata.ucontext.data())) << "Failed " << arch_str;
-
-  // Collect pc values of the call stack frames.
-  std::vector<uint64_t> pc_values;
-  for (size_t i = 0; i < backtrace->NumFrames(); ++i) {
-    pc_values.push_back(backtrace->GetFrame(i)->pc);
-  }
-
-  size_t test_one_index = 0;
-  for (size_t i = 0; i < pc_values.size(); ++i) {
-    if (FunctionNameForAddress(pc_values[i], testdata.symbols) == "test_level_one") {
-      test_one_index = i;
-      break;
-    }
-  }
-
-  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("arm64", "libbacktrace_test_eh_frame.so");
-  BacktraceOfflineTest("x86_64", "libbacktrace_test_eh_frame.so");
-}
-
-TEST(libbacktrace, offline_debug_frame) {
-  BacktraceOfflineTest("arm", "libbacktrace_test_debug_frame.so");
-  BacktraceOfflineTest("x86", "libbacktrace_test_debug_frame.so");
-}
-
-TEST(libbacktrace, offline_gnu_debugdata) {
-  BacktraceOfflineTest("arm", "libbacktrace_test_gnu_debugdata.so");
-  BacktraceOfflineTest("x86", "libbacktrace_test_gnu_debugdata.so");
-}
-
-TEST(libbacktrace, offline_arm_exidx) {
-  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) {
-  LibUnwindingTest("arm", "offline_testdata_for_libart", "libart.so");
-}
-
-TEST(libbacktrace, offline_debug_frame_with_load_bias) {
-  LibUnwindingTest("arm", "offline_testdata_for_libandroid_runtime", "libandroid_runtime.so");
-}
-
-TEST(libbacktrace, offline_try_armexidx_after_debug_frame) {
-  LibUnwindingTest("arm", "offline_testdata_for_libGLESv2_adreno", "libGLESv2_adreno.so");
-}
-
-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");
-}
-
-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");
-}
-
-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_test.cpp b/libbacktrace/backtrace_test.cpp
index 1e3d379..f4191b9 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -20,6 +20,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <malloc.h>
 #include <pthread.h>
 #include <signal.h>
 #include <stdint.h>
@@ -46,16 +47,17 @@
 
 #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 "BacktraceTest.h"
 #include "backtrace_testlib.h"
-#include "thread_utils.h"
 
 // Number of microseconds per milliseconds.
 #define US_PER_MSEC             1000
@@ -95,6 +97,23 @@
 static void VerifyMaxDump(Backtrace* backtrace, create_func_t create_func = nullptr,
                           map_create_func_t map_func = nullptr);
 
+void* BacktraceTest::dl_handle_;
+int (*BacktraceTest::test_level_one_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_level_two_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_level_three_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_level_four_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_recursive_call_)(int, void (*)(void*), void*);
+void (*BacktraceTest::test_get_context_and_wait_)(void*, volatile int*);
+void (*BacktraceTest::test_signal_action_)(int, siginfo_t*, void*);
+void (*BacktraceTest::test_signal_handler_)(int);
+
+extern "C" bool GetInitialArgs(const char*** args, size_t* num_args) {
+  static const char* initial_args[] = {"--slow_threshold_ms=8000", "--deadline_threshold_ms=15000"};
+  *args = initial_args;
+  *num_args = 2;
+  return true;
+}
+
 static uint64_t NanoTime() {
   struct timespec t = { 0, 0 };
   clock_gettime(CLOCK_MONOTONIC, &t);
@@ -250,7 +269,7 @@
   return false;
 }
 
-TEST(libbacktrace, local_no_unwind_frames) {
+TEST_F(BacktraceTest, local_no_unwind_frames) {
   // Verify that a local unwind does not include any frames within
   // libunwind or libbacktrace.
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
@@ -270,7 +289,7 @@
   }
 }
 
-TEST(libbacktrace, local_unwind_frames) {
+TEST_F(BacktraceTest, 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()));
@@ -302,8 +321,8 @@
                                                << DumpFrames(backtrace.get());
 }
 
-TEST(libbacktrace, local_trace) {
-  ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelBacktrace, nullptr), 0);
+TEST_F(BacktraceTest, 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,
@@ -357,12 +376,12 @@
   VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), "VerifyLevelIgnoreFrames");
 }
 
-TEST(libbacktrace, local_trace_ignore_frames) {
-  ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelIgnoreFrames, nullptr), 0);
+TEST_F(BacktraceTest, local_trace_ignore_frames) {
+  ASSERT_NE(test_level_one_(1, 2, 3, 4, VerifyLevelIgnoreFrames, nullptr), 0);
 }
 
-TEST(libbacktrace, local_max_trace) {
-  ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, nullptr), 0);
+TEST_F(BacktraceTest, local_max_trace) {
+  ASSERT_NE(test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, VerifyMaxBacktrace, nullptr), 0);
 }
 
 static void VerifyProcTest(pid_t pid, pid_t tid, bool (*ReadyFunc)(Backtrace*),
@@ -402,10 +421,10 @@
   ASSERT_TRUE(verified) << "Last backtrace:\n" << last_dump;
 }
 
-TEST(libbacktrace, ptrace_trace) {
+TEST_F(BacktraceTest, ptrace_trace) {
   pid_t pid;
   if ((pid = fork()) == 0) {
-    ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+    ASSERT_NE(test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
     _exit(1);
   }
   VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyLevelDump,
@@ -416,10 +435,10 @@
   ASSERT_EQ(waitpid(pid, &status, 0), pid);
 }
 
-TEST(libbacktrace, ptrace_max_trace) {
+TEST_F(BacktraceTest, ptrace_max_trace) {
   pid_t pid;
   if ((pid = fork()) == 0) {
-    ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, nullptr, nullptr), 0);
+    ASSERT_NE(test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, nullptr, nullptr), 0);
     _exit(1);
   }
   VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyMaxBacktrace, VerifyMaxDump, Backtrace::Create,
@@ -446,10 +465,10 @@
   VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), nullptr);
 }
 
-TEST(libbacktrace, ptrace_ignore_frames) {
+TEST_F(BacktraceTest, ptrace_ignore_frames) {
   pid_t pid;
   if ((pid = fork()) == 0) {
-    ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+    ASSERT_NE(test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
     _exit(1);
   }
   VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyProcessIgnoreFrames,
@@ -462,7 +481,7 @@
 
 // Create a process with multiple threads and dump all of the threads.
 static void* PtraceThreadLevelRun(void*) {
-  EXPECT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+  EXPECT_NE(BacktraceTest::test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
   return nullptr;
 }
 
@@ -483,7 +502,7 @@
   }
 }
 
-TEST(libbacktrace, ptrace_threads) {
+TEST_F(BacktraceTest, ptrace_threads) {
   pid_t pid;
   if ((pid = fork()) == 0) {
     for (size_t i = 0; i < NUM_PTRACE_THREADS; i++) {
@@ -494,7 +513,7 @@
       pthread_t thread;
       ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, nullptr) == 0);
     }
-    ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+    ASSERT_NE(test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
     _exit(1);
   }
 
@@ -524,7 +543,7 @@
 }
 
 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));
   VERIFY_NO_ERROR(backtrace->GetError().error_code);
@@ -532,12 +551,12 @@
   VerifyLevelDump(backtrace.get());
 }
 
-TEST(libbacktrace, thread_current_level) {
-  ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelThread, nullptr), 0);
+TEST_F(BacktraceTest, thread_current_level) {
+  ASSERT_NE(test_level_one_(1, 2, 3, 4, VerifyLevelThread, nullptr), 0);
 }
 
 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_ERROR_EXCEED_MAX_FRAMES_LIMIT, backtrace->GetError().error_code);
@@ -545,19 +564,19 @@
   VerifyMaxDump(backtrace.get());
 }
 
-TEST(libbacktrace, thread_current_max) {
-  ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxThread, nullptr), 0);
+TEST_F(BacktraceTest, thread_current_max) {
+  ASSERT_NE(test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, VerifyMaxThread, nullptr), 0);
 }
 
 static void* ThreadLevelRun(void* data) {
   thread_t* thread = reinterpret_cast<thread_t*>(data);
 
-  thread->tid = gettid();
-  EXPECT_NE(test_level_one(1, 2, 3, 4, ThreadSetState, data), 0);
+  thread->tid = android::base::GetThreadId();
+  EXPECT_NE(BacktraceTest::test_level_one_(1, 2, 3, 4, ThreadSetState, data), 0);
   return nullptr;
 }
 
-TEST(libbacktrace, thread_level_trace) {
+TEST_F(BacktraceTest, thread_level_trace) {
   pthread_attr_t attr;
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -607,7 +626,7 @@
   EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags);
 }
 
-TEST(libbacktrace, thread_ignore_frames) {
+TEST_F(BacktraceTest, thread_ignore_frames) {
   pthread_attr_t attr;
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -643,12 +662,13 @@
 static void* ThreadMaxRun(void* data) {
   thread_t* thread = reinterpret_cast<thread_t*>(data);
 
-  thread->tid = gettid();
-  EXPECT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, ThreadSetState, data), 0);
+  thread->tid = android::base::GetThreadId();
+  EXPECT_NE(BacktraceTest::test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, ThreadSetState, data),
+            0);
   return nullptr;
 }
 
-TEST(libbacktrace, thread_max_trace) {
+TEST_F(BacktraceTest, thread_max_trace) {
   pthread_attr_t attr;
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -742,17 +762,17 @@
   }
 }
 
-TEST(libbacktrace, thread_multiple_dump) {
+TEST_F(BacktraceTest, thread_multiple_dump) {
   MultipleThreadDumpTest(false);
 }
 
-TEST(libbacktrace, thread_multiple_dump_same_map) {
+TEST_F(BacktraceTest, thread_multiple_dump_same_map) {
   MultipleThreadDumpTest(true);
 }
 
 // This test is for UnwindMaps that should share the same map cursor when
 // multiple maps are created for the current process at the same time.
-TEST(libbacktrace, simultaneous_maps) {
+TEST_F(BacktraceTest, simultaneous_maps) {
   BacktraceMap* map1 = BacktraceMap::Create(getpid());
   BacktraceMap* map2 = BacktraceMap::Create(getpid());
   BacktraceMap* map3 = BacktraceMap::Create(getpid());
@@ -779,7 +799,7 @@
   delete map3;
 }
 
-TEST(libbacktrace, fillin_erases) {
+TEST_F(BacktraceTest, fillin_erases) {
   BacktraceMap* back_map = BacktraceMap::Create(getpid());
 
   backtrace_map_t map;
@@ -798,7 +818,7 @@
   ASSERT_EQ("", map.name);
 }
 
-TEST(libbacktrace, format_test) {
+TEST_F(BacktraceTest, format_test) {
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(backtrace.get() != nullptr);
 
@@ -969,7 +989,7 @@
   ASSERT_TRUE(test_it == test_maps.end());
 }
 
-TEST(libbacktrace, verify_map_remote) {
+TEST_F(BacktraceTest, verify_map_remote) {
   pid_t pid;
   CreateRemoteProcess(&pid);
 
@@ -993,7 +1013,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.
@@ -1069,7 +1089,7 @@
   delete[] expected;
 }
 
-TEST(libbacktrace, thread_read) {
+TEST_F(BacktraceTest, thread_read) {
   pthread_attr_t attr;
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -1120,7 +1140,7 @@
   }
 }
 
-TEST(libbacktrace, process_read) {
+TEST_F(BacktraceTest, process_read) {
   g_ready = 0;
   pid_t pid;
   if ((pid = fork()) == 0) {
@@ -1186,49 +1206,39 @@
   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
-
-#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);
-#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;
+static void CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
+  std::string test_lib(testing::internal::GetArgvs()[0]);
+  auto const value = test_lib.find_last_of('/');
+  if (value == std::string::npos) {
+    test_lib = "../backtrace_test_libs/";
+  } else {
+    test_lib = test_lib.substr(0, value + 1) + "../backtrace_test_libs/";
   }
-  std::string cp_cmd = android::base::StringPrintf("cp %s/%s/libbacktrace_test.so %s",
-                                                   getenv("ANDROID_HOST_OUT"), lib_name,
-                                                   tmp_so_name);
-#endif
+  test_lib += "libbacktrace_test.so";
+
+  *tmp_so_name = std::string(tmp_dir) + "/libbacktrace_test.so";
+  std::string cp_cmd = android::base::StringPrintf("cp %s %s", test_lib.c_str(), tmp_dir);
 
   // Copy the shared so to a tempory directory.
-  system(cp_cmd.c_str());
-
-  return tmp_so_name;
+  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);
+TEST_F(BacktraceTest, check_unreadable_elf_local) {
+  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);
+  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(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,
@@ -1255,33 +1265,34 @@
   VerifyFunctionsFound(found_functions);
 }
 
-TEST(libbacktrace, check_unreadable_elf_remote) {
-  const char* tmp_so_name = CopySharedLibrary();
-  ASSERT_TRUE(tmp_so_name != nullptr);
+TEST_F(BacktraceTest, check_unreadable_elf_remote) {
+  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);
+  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(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);
     }
@@ -1393,12 +1404,14 @@
 
 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);
+TEST_F(BacktraceTest, unwind_through_unreadable_elf_local) {
+  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"));
@@ -1406,16 +1419,16 @@
 
   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);
+TEST_F(BacktraceTest, unwind_through_unreadable_elf_remote) {
+  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"));
@@ -1427,7 +1440,6 @@
     exit(0);
   }
   ASSERT_TRUE(pid > 0);
-  ASSERT_TRUE(dlclose(lib_handle) == 0);
 
   uint64_t start = NanoTime();
   bool done = false;
@@ -1444,7 +1456,8 @@
 
     size_t frame_num;
     if (FindFuncFrameInBacktrace(backtrace.get(), reinterpret_cast<uint64_t>(test_func),
-                                 &frame_num)) {
+                                 &frame_num) &&
+        frame_num != 0) {
       VerifyUnreadableElfFrame(backtrace.get(), reinterpret_cast<uint64_t>(test_func), frame_num);
       done = true;
     }
@@ -1463,7 +1476,7 @@
   ASSERT_TRUE(done) << "Test function never found in unwind.";
 }
 
-TEST(libbacktrace, unwind_thread_doesnt_exist) {
+TEST_F(BacktraceTest, unwind_thread_doesnt_exist) {
   std::unique_ptr<Backtrace> backtrace(
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 99999999));
   ASSERT_TRUE(backtrace.get() != nullptr);
@@ -1471,18 +1484,18 @@
   ASSERT_EQ(BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST, backtrace->GetError().error_code);
 }
 
-TEST(libbacktrace, local_get_function_name_before_unwind) {
+TEST_F(BacktraceTest, local_get_function_name_before_unwind) {
   std::unique_ptr<Backtrace> backtrace(
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(backtrace.get() != nullptr);
 
   // Verify that trying to get a function name before doing an unwind works.
-  uint64_t cur_func_offset = reinterpret_cast<uint64_t>(&test_level_one) + 1;
+  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));
 }
 
-TEST(libbacktrace, remote_get_function_name_before_unwind) {
+TEST_F(BacktraceTest, remote_get_function_name_before_unwind) {
   pid_t pid;
   CreateRemoteProcess(&pid);
 
@@ -1490,7 +1503,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
 
   // Verify that trying to get a function name before doing an unwind works.
-  uint64_t cur_func_offset = reinterpret_cast<uint64_t>(&test_level_one) + 1;
+  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));
 
@@ -1577,7 +1590,7 @@
   ASSERT_EQ(std::string(""), backtrace->GetFunctionName(device_map_uint, &offset, &map));
   ASSERT_EQ(std::string(""), backtrace->GetFunctionName(0, &offset));
 
-  uint64_t cur_func_offset = reinterpret_cast<uint64_t>(&test_level_one) + 1;
+  uint64_t cur_func_offset = reinterpret_cast<uint64_t>(BacktraceTest::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);
@@ -1626,7 +1639,7 @@
   ASSERT_EQ(0U, backtrace->NumFrames());
 }
 
-TEST(libbacktrace, unwind_disallow_device_map_local) {
+TEST_F(BacktraceTest, unwind_disallow_device_map_local) {
   void* device_map;
   SetupDeviceMap(&device_map);
 
@@ -1640,7 +1653,7 @@
   munmap(device_map, DEVICE_MAP_SIZE);
 }
 
-TEST(libbacktrace, unwind_disallow_device_map_remote) {
+TEST_F(BacktraceTest, unwind_disallow_device_map_remote) {
   void* device_map;
   SetupDeviceMap(&device_map);
 
@@ -1696,13 +1709,13 @@
   pid_t pid;
   if ((pid = fork()) == 0) {
     if (use_action) {
-      ScopedSignalHandler ssh(SIGUSR1, test_signal_action);
+      ScopedSignalHandler ssh(SIGUSR1, BacktraceTest::test_signal_action_);
 
-      test_level_one(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
+      BacktraceTest::test_level_one_(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
     } else {
-      ScopedSignalHandler ssh(SIGUSR1, test_signal_handler);
+      ScopedSignalHandler ssh(SIGUSR1, BacktraceTest::test_signal_handler_);
 
-      test_level_one(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
+      BacktraceTest::test_level_one_(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
     }
   }
   ASSERT_NE(-1, pid);
@@ -1803,65 +1816,58 @@
   FinishRemoteProcess(pid);
 }
 
-TEST(libbacktrace, unwind_remote_through_signal_using_handler) {
+TEST_F(BacktraceTest, unwind_remote_through_signal_using_handler) {
   UnwindThroughSignal(false, Backtrace::Create, BacktraceMap::Create);
 }
 
-TEST(libbacktrace, unwind_remote_through_signal_using_action) {
+TEST_F(BacktraceTest, 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(), gettid(), map.get()));
+  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) {
+TEST_F(BacktraceTest, unwind_frame_skip_numbering) {
   TestFrameSkipNumbering(Backtrace::Create, BacktraceMap::Create);
 }
 
-#if defined(ENABLE_PSS_TESTS)
-#include "GetPss.h"
-
 #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, map.get());
-    ASSERT_TRUE(backtrace != nullptr);
-    ASSERT_TRUE(backtrace->Unwind(0));
-    VERIFY_NO_ERROR(backtrace->GetError().error_code);
-    delete backtrace;
-  }
-  size_t stable_pss = GetPssBytes();
-  ASSERT_TRUE(stable_pss != 0);
-
   // Loop enough that even a small leak should be detectable.
+  size_t first_allocated_bytes = 0;
+  size_t last_allocated_bytes = 0;
   for (size_t i = 0; i < 4096; i++) {
     Backtrace* backtrace = Backtrace::Create(pid, tid, map.get());
     ASSERT_TRUE(backtrace != nullptr);
     ASSERT_TRUE(backtrace->Unwind(0));
     VERIFY_NO_ERROR(backtrace->GetError().error_code);
     delete backtrace;
-  }
-  size_t new_pss = GetPssBytes();
-  ASSERT_TRUE(new_pss != 0);
-  if (new_pss > stable_pss) {
-    ASSERT_LE(new_pss - stable_pss, MAX_LEAK_BYTES);
+
+    size_t allocated_bytes = mallinfo().uordblks;
+    if (first_allocated_bytes == 0) {
+      first_allocated_bytes = allocated_bytes;
+    } else if (last_allocated_bytes > first_allocated_bytes) {
+      // Check that the memory did not increase too much over the first loop.
+      ASSERT_LE(last_allocated_bytes - first_allocated_bytes, MAX_LEAK_BYTES);
+    }
+    last_allocated_bytes = allocated_bytes;
   }
 }
 
-TEST(libbacktrace, check_for_leak_local) {
+TEST_F(BacktraceTest, check_for_leak_local) {
   CheckForLeak(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD);
 }
 
-TEST(libbacktrace, check_for_leak_local_thread) {
+TEST_F(BacktraceTest, check_for_leak_local_thread) {
   thread_t thread_data = { 0, 0, 0, nullptr };
   pthread_t thread;
   ASSERT_TRUE(pthread_create(&thread, nullptr, ThreadLevelRun, &thread_data) == 0);
@@ -1877,7 +1883,7 @@
   ASSERT_TRUE(pthread_join(thread, nullptr) == 0);
 }
 
-TEST(libbacktrace, check_for_leak_remote) {
+TEST_F(BacktraceTest, check_for_leak_remote) {
   pid_t pid;
   CreateRemoteProcess(&pid);
 
@@ -1885,4 +1891,3 @@
 
   FinishRemoteProcess(pid);
 }
-#endif
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index 735a2f3..664b531 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -64,6 +64,8 @@
   BACKTRACE_UNWIND_ERROR_UNWIND_INFO,
   // Unwind information stopped due to sp/pc repeating.
   BACKTRACE_UNWIND_ERROR_REPEATED_FRAME,
+  // Unwind information stopped due to invalid elf.
+  BACKTRACE_UNWIND_ERROR_INVALID_ELF,
 };
 
 struct BacktraceUnwindError {
@@ -122,45 +124,22 @@
   // 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);
-
-  // 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
-  // for this call. The cached information survives until the calling process ends. This means
-  // that subsequent calls to create offline Backtrace objects will continue to use the same
-  // cache. It also assumes that the elf files used for each offline unwind are the same.
-  static Backtrace* CreateOffline(pid_t pid, pid_t tid, BacktraceMap* map,
-                                  const backtrace_stackinfo_t& stack, bool cache_file = false);
+  static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = nullptr);
 
   virtual ~Backtrace();
 
   // Get the current stack trace and store in the backtrace_ structure.
-  virtual bool Unwind(size_t num_ignore_frames, void* 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(uint64_t pc, uint64_t* offset,
-                                      const backtrace_map_t* map = NULL);
+                                      const backtrace_map_t* map = nullptr);
 
   // Fill in the map data associated with the given pc.
   virtual void FillInMap(uint64_t pc, backtrace_map_t* map);
@@ -185,7 +164,7 @@
 
   const backtrace_frame_data_t* GetFrame(size_t frame_num) {
     if (frame_num >= frames_.size()) {
-      return NULL;
+      return nullptr;
     }
     return &frames_[frame_num];
   }
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index 473d195..f8d5058 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -31,6 +31,7 @@
 
 #include <deque>
 #include <iterator>
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -40,6 +41,10 @@
 // Special flag to indicate a map is in /dev/. However, a map in
 // /dev/ashmem/... does not set this flag.
 static constexpr int PROT_DEVICE_MAP = 0x8000;
+// Special flag to indicate that this map represents an elf file
+// created by ART for use with the gdb jit debug interface.
+// This should only ever appear in offline maps data.
+static constexpr int PROT_JIT_SYMFILE_MAP = 0x4000;
 
 struct backtrace_map_t {
   uint64_t start = 0;
@@ -64,8 +69,6 @@
   // is unsupported.
   static BacktraceMap* Create(pid_t pid, bool uncached = false);
 
-  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*> {
@@ -165,8 +168,6 @@
 
   virtual uint64_t GetLoadBias(size_t /* index */) { return 0; }
 
-  virtual bool ParseLine(const char* line, backtrace_map_t* map);
-
   pid_t pid_;
   std::deque<backtrace_map_t> maps_;
   std::vector<std::string> suffixes_to_ignore_;
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 47de12a..e47560f 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -17,6 +17,7 @@
 cc_library {
     name: "libcrypto_utils",
     vendor_available: true,
+    recovery_available: true,
     vndk: {
         enabled: true,
     },
diff --git a/libcrypto_utils/tests/Android.bp b/libcrypto_utils/tests/Android.bp
new file mode 100644
index 0000000..5aadfe2
--- /dev/null
+++ b/libcrypto_utils/tests/Android.bp
@@ -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.
+//
+
+cc_test_host {
+    name: "libcrypto_utils_test",
+    srcs: ["android_pubkey_test.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+    shared_libs: [
+        "libcrypto_utils",
+        "libcrypto",
+    ],
+}
diff --git a/libcrypto_utils/tests/Android.mk b/libcrypto_utils/tests/Android.mk
deleted file mode 100644
index ef3d0cf..0000000
--- a/libcrypto_utils/tests/Android.mk
+++ /dev/null
@@ -1,24 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libcrypto_utils_test
-LOCAL_SRC_FILES := android_pubkey_test.cpp
-LOCAL_CFLAGS := -Wall -Werror -Wextra
-LOCAL_SHARED_LIBRARIES := libcrypto_utils libcrypto
-include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index bcc9b1c..319a73a 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -18,8 +18,8 @@
 // they correspond to features not used by our host development tools
 // which are also hard or even impossible to port to native Win32
 libcutils_nonwindows_sources = [
-    "android_get_control_file.cpp",
     "fs.cpp",
+    "hashmap.cpp",
     "multiuser.cpp",
     "socket_inaddr_any_server_unix.cpp",
     "socket_local_client_unix.cpp",
@@ -32,7 +32,9 @@
 cc_library_headers {
     name: "libcutils_headers",
     vendor_available: true,
+    recovery_available: true,
     host_supported: true,
+    native_bridge_supported: true,
     export_include_dirs: ["include"],
     target: {
         vendor: {
@@ -54,18 +56,16 @@
         enabled: true,
         support_system_process: true,
     },
+    recovery_available: true,
     host_supported: true,
+    native_bridge_supported: true,
     srcs: [
         "config_utils.cpp",
-        "fs_config.cpp",
         "canned_fs_config.cpp",
-        "hashmap.cpp",
         "iosched_policy.cpp",
         "load_file.cpp",
         "native_handle.cpp",
-        "open_memstream.c",
         "record_stream.cpp",
-        "sched_policy.cpp",
         "sockets.cpp",
         "strdup16to8.cpp",
         "strdup8to16.cpp",
@@ -80,20 +80,21 @@
         not_windows: {
             srcs: libcutils_nonwindows_sources + [
                 "ashmem-host.cpp",
+                "fs_config.cpp",
                 "trace-host.cpp",
             ],
         },
         windows: {
+            host_ldlibs: ["-lws2_32"],
+
             srcs: [
                 "socket_inaddr_any_server_windows.cpp",
                 "socket_network_client_windows.cpp",
                 "sockets_windows.cpp",
+                "trace-host.cpp",
             ],
 
             enabled: true,
-            shared: {
-                enabled: false,
-            },
             cflags: [
                 "-D_GNU_SOURCE",
             ],
@@ -101,8 +102,10 @@
 
         android: {
             srcs: libcutils_nonwindows_sources + [
+                "android_get_control_file.cpp",
                 "android_reboot.cpp",
                 "ashmem-dev.cpp",
+                "fs_config.cpp",
                 "klog.cpp",
                 "partition_utils.cpp",
                 "properties.cpp",
@@ -171,12 +174,20 @@
         }
     },
 
-    shared_libs: ["liblog"],
+    shared_libs: [
+        "liblog",
+        "libbase",
+    ],
     header_libs: [
+        "libbase_headers",
         "libcutils_headers",
         "libutils_headers",
+        "libprocessgroup_headers",
     ],
-    export_header_lib_headers: ["libcutils_headers"],
+    export_header_lib_headers: [
+        "libcutils_headers",
+        "libprocessgroup_headers",
+    ],
     local_include_dirs: ["include"],
 
     cflags: [
@@ -186,4 +197,75 @@
     ],
 }
 
-subdirs = ["tests"]
+cc_defaults {
+    name: "libcutils_test_default",
+    srcs: ["sockets_test.cpp"],
+
+    target: {
+        android: {
+            srcs: [
+                "android_get_control_file_test.cpp",
+                "android_get_control_socket_test.cpp",
+                "ashmem_test.cpp",
+                "fs_config_test.cpp",
+                "memset_test.cpp",
+                "multiuser_test.cpp",
+                "properties_test.cpp",
+                "sched_policy_test.cpp",
+                "str_parms_test.cpp",
+                "trace-dev_test.cpp",
+            ],
+        },
+
+        not_windows: {
+            srcs: [
+                "str_parms_test.cpp",
+            ],
+        },
+    },
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+}
+
+test_libraries = [
+    "libcutils",
+    "liblog",
+    "libbase",
+    "libjsoncpp",
+    "libprocessgroup",
+    "libcgrouprc",
+]
+
+cc_test {
+    name: "libcutils_test",
+    test_suites: ["device-tests"],
+    defaults: ["libcutils_test_default"],
+    host_supported: true,
+    shared_libs: test_libraries,
+}
+
+cc_test {
+    name: "libcutils_test_static",
+    test_suites: ["device-tests"],
+    defaults: ["libcutils_test_default"],
+    static_libs: [
+        "libc",
+        "libcgrouprc_format",
+    ] + test_libraries,
+    stl: "libc++_static",
+
+    target: {
+        android: {
+            static_executable: true,
+        },
+        windows: {
+            host_ldlibs: ["-lws2_32"],
+
+            enabled: true,
+        },
+    },
+}
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_env.h b/libcutils/android_get_control_env.h
index 638c831..a830269 100644
--- a/libcutils/android_get_control_env.h
+++ b/libcutils/android_get_control_env.h
@@ -14,20 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef __CUTILS_ANDROID_GET_CONTROL_ENV_H
-#define __CUTILS_ANDROID_GET_CONTROL_ENV_H
+#pragma once
 
-/* To declare library function hidden and internal */
-#define LIBCUTILS_HIDDEN __attribute__((visibility("hidden")))
+#include <sys/cdefs.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+__BEGIN_DECLS
 
-LIBCUTILS_HIDDEN int __android_get_control_from_env(const char* prefix,
-                                                    const char* name);
-#ifdef __cplusplus
-}
-#endif
+int __android_get_control_from_env(const char* prefix, const char* name)
+        __attribute__((visibility("hidden")));
 
-#endif /* __CUTILS_ANDROID_GET_CONTROL_ENV_H */
+__END_DECLS
diff --git a/libcutils/android_get_control_file.cpp b/libcutils/android_get_control_file.cpp
index d8121f5..d5b0894 100644
--- a/libcutils/android_get_control_file.cpp
+++ b/libcutils/android_get_control_file.cpp
@@ -39,14 +39,14 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+
 #include "android_get_control_env.h"
 
-#ifndef TEMP_FAILURE_RETRY
-#define TEMP_FAILURE_RETRY(exp) (exp) // KISS implementation
-#endif
-
-LIBCUTILS_HIDDEN int __android_get_control_from_env(const char* prefix,
-                                                    const char* name) {
+int __android_get_control_from_env(const char* prefix, const char* name) {
     if (!prefix || !name) return -1;
 
     char *key = NULL;
@@ -67,48 +67,33 @@
     long fd = strtol(val, NULL, 10);
     if (errno) return -1;
 
-    // validity checking
+    // Since we are inheriting an fd, it could legitimately exceed _SC_OPEN_MAX
     if ((fd < 0) || (fd > INT_MAX)) return -1;
 
-    // Since we are inheriting an fd, it could legitimately exceed _SC_OPEN_MAX
-
     // Still open?
-#if defined(F_GETFD) // Lowest overhead
     if (TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD)) < 0) return -1;
-#elif defined(F_GETFL) // Alternate lowest overhead
-    if (TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL)) < 0) return -1;
-#else // Hail Mary pass
-    struct stat s;
-    if (TEMP_FAILURE_RETRY(fstat(fd, &s)) < 0) return -1;
-#endif
 
     return static_cast<int>(fd);
 }
 
 int android_get_control_file(const char* path) {
-    int fd = __android_get_control_from_env(ANDROID_FILE_ENV_PREFIX, path);
+    std::string given_path;
+    if (!android::base::Realpath(path, &given_path)) return -1;
 
-#if defined(__linux__)
-    // Find file path from /proc and make sure it is correct
-    char *proc = NULL;
-    if (asprintf(&proc, "/proc/self/fd/%d", fd) < 0) return -1;
-    if (!proc) return -1;
-
-    size_t len = strlen(path);
-    // readlink() does not guarantee a nul byte, len+2 so we catch truncation.
-    char *buf = static_cast<char *>(calloc(1, len + 2));
-    if (!buf) {
-        free(proc);
-        return -1;
+    // Try path, then realpath(path), as keys to get the fd from env.
+    auto fd = __android_get_control_from_env(ANDROID_FILE_ENV_PREFIX, path);
+    if (fd < 0) {
+        fd = __android_get_control_from_env(ANDROID_FILE_ENV_PREFIX, given_path.c_str());
+        if (fd < 0) return fd;
     }
-    ssize_t ret = TEMP_FAILURE_RETRY(readlink(proc, buf, len + 1));
-    free(proc);
-    int cmp = (len != static_cast<size_t>(ret)) || strcmp(buf, path);
-    free(buf);
-    if (ret < 0) return -1;
-    if (cmp != 0) return -1;
+
+    // Find file path from /proc and make sure it is correct
+    auto proc = android::base::StringPrintf("/proc/self/fd/%d", fd);
+    std::string fd_path;
+    if (!android::base::Realpath(proc, &fd_path)) return -1;
+
+    if (given_path != fd_path) return -1;
     // It is what we think it is
-#endif
 
     return fd;
 }
diff --git a/libcutils/android_get_control_file_test.cpp b/libcutils/android_get_control_file_test.cpp
new file mode 100644
index 0000000..8de8530
--- /dev/null
+++ b/libcutils/android_get_control_file_test.cpp
@@ -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.
+ */
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <cutils/android_get_control_file.h>
+#include <gtest/gtest.h>
+
+TEST(FilesTest, android_get_control_file) {
+    TemporaryFile tf;
+    ASSERT_GE(tf.fd, 0);
+
+    std::string key(ANDROID_FILE_ENV_PREFIX);
+    key += tf.path;
+
+    std::for_each(key.begin(), key.end(), [] (char& c) { c = isalnum(c) ? c : '_'; });
+
+    EXPECT_EQ(unsetenv(key.c_str()), 0);
+    EXPECT_EQ(android_get_control_file(tf.path), -1);
+
+    EXPECT_EQ(setenv(key.c_str(), android::base::StringPrintf("%d", tf.fd).c_str(), true), 0);
+
+    EXPECT_EQ(android_get_control_file(tf.path), tf.fd);
+    close(tf.fd);
+    EXPECT_EQ(android_get_control_file(tf.path), -1);
+    EXPECT_EQ(unsetenv(key.c_str()), 0);
+    EXPECT_EQ(android_get_control_file(tf.path), -1);
+}
diff --git a/libcutils/tests/android_get_control_socket_test.cpp b/libcutils/android_get_control_socket_test.cpp
similarity index 100%
rename from libcutils/tests/android_get_control_socket_test.cpp
rename to libcutils/android_get_control_socket_test.cpp
diff --git a/libcutils/android_reboot.cpp b/libcutils/android_reboot.cpp
index ce41cd3..e0def71 100644
--- a/libcutils/android_reboot.cpp
+++ b/libcutils/android_reboot.cpp
@@ -23,12 +23,12 @@
 
 #define TAG "android_reboot"
 
-int android_reboot(int cmd, int /*flags*/, const char* arg) {
+int android_reboot(unsigned cmd, int /*flags*/, const char* arg) {
     int ret;
     const char* restart_cmd = NULL;
     char* prop_value;
 
-    switch (static_cast<unsigned>(cmd)) {
+    switch (cmd) {
         case ANDROID_RB_RESTART:  // deprecated
         case ANDROID_RB_RESTART2:
             restart_cmd = "reboot";
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index 15ace0e..e67b458 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -23,20 +23,40 @@
  */
 #define LOG_TAG "ashmem"
 
+#ifndef __ANDROID_VNDK__
+#include <dlfcn.h>
+#endif
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/ashmem.h>
+#include <linux/memfd.h>
+#include <log/log.h>
 #include <pthread.h>
+#include <stdio.h>
 #include <string.h>
 #include <sys/ioctl.h>
+#include <sys/mman.h>
 #include <sys/stat.h>
+#include <sys/syscall.h>
 #include <sys/sysmacros.h>
 #include <sys/types.h>
 #include <unistd.h>
-#include <log/log.h>
+
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
 
 #define ASHMEM_DEVICE "/dev/ashmem"
 
+/* Will be added to UAPI once upstream change is merged */
+#define F_SEAL_FUTURE_WRITE 0x0010
+
+/*
+ * The minimum vendor API level at and after which it is safe to use memfd.
+ * This is to facilitate deprecation of ashmem.
+ */
+#define MIN_MEMFD_VENDOR_API_LEVEL 29
+#define MIN_MEMFD_VENDOR_API_LEVEL_CHAR 'Q'
+
 /* ashmem identity */
 static dev_t __ashmem_rdev;
 /*
@@ -45,13 +65,175 @@
  */
 static pthread_mutex_t __ashmem_lock = PTHREAD_MUTEX_INITIALIZER;
 
+/*
+ * We use ashmemd to enforce that apps don't open /dev/ashmem directly. Vendor
+ * code can't access system aidl services per Treble requirements. So we limit
+ * ashmemd access to the system variant of libcutils.
+ */
+#ifndef __ANDROID_VNDK__
+using openFdType = int (*)();
+
+static openFdType openFd;
+
+openFdType initOpenAshmemFd() {
+    openFdType openFd = nullptr;
+    void* handle = dlopen("libashmemd_client.so", RTLD_NOW);
+    if (!handle) {
+        ALOGE("Failed to dlopen() libashmemd_client.so: %s", dlerror());
+        return openFd;
+    }
+
+    openFd = reinterpret_cast<openFdType>(dlsym(handle, "openAshmemdFd"));
+    if (!openFd) {
+        ALOGE("Failed to dlsym() openAshmemdFd() function: %s", dlerror());
+    }
+    return openFd;
+}
+#endif
+
+/*
+ * has_memfd_support() determines if the device can use memfd. memfd support
+ * has been there for long time, but certain things in it may be missing.  We
+ * check for needed support in it. Also we check if the VNDK version of
+ * libcutils being used is new enough, if its not, then we cannot use memfd
+ * since the older copies may be using ashmem so we just use ashmem. Once all
+ * Android devices that are getting updates are new enough (ex, they were
+ * originally shipped with Android release > P), then we can just use memfd and
+ * delete all ashmem code from libcutils (while preserving the interface).
+ *
+ * NOTE:
+ * The sys.use_memfd property is set by default to false in Android
+ * to temporarily disable memfd, till vendor and apps are ready for it.
+ * The main issue: either apps or vendor processes can directly make ashmem
+ * IOCTLs on FDs they receive by assuming they are ashmem, without going
+ * through libcutils. Such fds could have very well be originally created with
+ * libcutils hence they could be memfd. Thus the IOCTLs will break.
+ *
+ * Set default value of sys.use_memfd property to true once the issue is
+ * resolved, so that the code can then self-detect if kernel support is present
+ * on the device. The property can also set to true from adb shell, for
+ * debugging.
+ */
+
+static bool debug_log = false;            /* set to true for verbose logging and other debug  */
+static bool pin_deprecation_warn = true; /* Log the pin deprecation warning only once */
+
+/* Determine if vendor processes would be ok with memfd in the system:
+ *
+ * If VNDK is using older libcutils, don't use memfd. This is so that the
+ * same shared memory mechanism is used across binder transactions between
+ * vendor partition processes and system partition processes.
+ */
+static bool check_vendor_memfd_allowed() {
+    std::string vndk_version = android::base::GetProperty("ro.vndk.version", "");
+
+    if (vndk_version == "") {
+        ALOGE("memfd: ro.vndk.version not defined or invalid (%s), this is mandated since P.\n",
+              vndk_version.c_str());
+        return false;
+    }
+
+    /* No issues if vendor is targetting current Dessert */
+    if (vndk_version == "current") {
+        return false;
+    }
+
+    /* Check if VNDK version is a number and act on it */
+    char* p;
+    long int vers = strtol(vndk_version.c_str(), &p, 10);
+    if (*p == 0) {
+        if (vers < MIN_MEMFD_VENDOR_API_LEVEL) {
+            ALOGI("memfd: device VNDK version (%s) is < Q so using ashmem.\n",
+                  vndk_version.c_str());
+            return false;
+        }
+
+        return true;
+    }
+
+    /* If its not a number, assume string, but check if its a sane string */
+    if (tolower(vndk_version[0]) < 'a' || tolower(vndk_version[0]) > 'z') {
+        ALOGE("memfd: ro.vndk.version not defined or invalid (%s), this is mandated since P.\n",
+              vndk_version.c_str());
+        return false;
+    }
+
+    if (tolower(vndk_version[0]) < tolower(MIN_MEMFD_VENDOR_API_LEVEL_CHAR)) {
+        ALOGI("memfd: device is using VNDK version (%s) which is less than Q. Use ashmem only.\n",
+              vndk_version.c_str());
+        return false;
+    }
+
+    return true;
+}
+
+
+/* Determine if memfd can be supported. This is just one-time hardwork
+ * which will be cached by the caller.
+ */
+static bool __has_memfd_support() {
+    if (check_vendor_memfd_allowed() == false) {
+        return false;
+    }
+
+    /* Used to turn on/off the detection at runtime, in the future this
+     * property will be removed once we switch everything over to ashmem.
+     * Currently it is used only for debugging to switch the system over.
+     */
+    if (!android::base::GetBoolProperty("sys.use_memfd", false)) {
+        if (debug_log) {
+            ALOGD("sys.use_memfd=false so memfd disabled\n");
+        }
+        return false;
+    }
+
+    /* Check if kernel support exists, otherwise fall back to ashmem */
+    android::base::unique_fd fd(
+            syscall(__NR_memfd_create, "test_android_memfd", MFD_ALLOW_SEALING));
+    if (fd == -1) {
+        ALOGE("memfd_create failed: %s, no memfd support.\n", strerror(errno));
+        return false;
+    }
+
+    if (fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE) == -1) {
+        ALOGE("fcntl(F_ADD_SEALS) failed: %s, no memfd support.\n", strerror(errno));
+        return false;
+    }
+
+    if (debug_log) {
+        ALOGD("memfd: device has memfd support, using it\n");
+    }
+    return true;
+}
+
+static bool has_memfd_support() {
+    /* memfd_supported is the initial global per-process state of what is known
+     * about memfd.
+     */
+    static bool memfd_supported = __has_memfd_support();
+
+    return memfd_supported;
+}
+
 /* 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));
+    int fd = -1;
+#ifndef __ANDROID_VNDK__
+    if (!openFd) {
+        openFd = initOpenAshmemFd();
+    }
+
+    if (openFd) {
+        fd = openFd();
+    }
+#endif
+    if (fd < 0) {
+        fd = TEMP_FAILURE_RETRY(open(ASHMEM_DEVICE, O_RDWR | O_CLOEXEC));
+    }
     if (fd < 0) {
         return fd;
     }
@@ -90,7 +272,7 @@
     dev_t rdev;
     struct stat st;
 
-    if (TEMP_FAILURE_RETRY(fstat(fd, &st)) < 0) {
+    if (fstat(fd, &st) < 0) {
         return -1;
     }
 
@@ -135,11 +317,55 @@
     return -1;
 }
 
+static int __ashmem_check_failure(int fd, int result)
+{
+    if (result == -1 && errno == ENOTTY) __ashmem_is_ashmem(fd, 1);
+    return result;
+}
+
+static bool memfd_is_ashmem(int fd) {
+    static bool fd_check_error_once = false;
+
+    if (__ashmem_is_ashmem(fd, 0) == 0) {
+        if (!fd_check_error_once) {
+            ALOGE("memfd: memfd expected but ashmem fd used - please use libcutils.\n");
+            fd_check_error_once = true;
+        }
+
+        return true;
+    }
+
+    return false;
+}
+
 int ashmem_valid(int fd)
 {
+    if (has_memfd_support() && !memfd_is_ashmem(fd)) {
+        return 1;
+    }
+
     return __ashmem_is_ashmem(fd, 0) >= 0;
 }
 
+static int memfd_create_region(const char* name, size_t size) {
+    android::base::unique_fd fd(syscall(__NR_memfd_create, name, MFD_ALLOW_SEALING));
+
+    if (fd == -1) {
+        ALOGE("memfd_create(%s, %zd) failed: %s\n", name, size, strerror(errno));
+        return -1;
+    }
+
+    if (ftruncate(fd, size) == -1) {
+        ALOGE("ftruncate(%s, %zd) failed for memfd creation: %s\n", name, size, strerror(errno));
+        return -1;
+    }
+
+    if (debug_log) {
+        ALOGE("memfd_create(%s, %zd) success. fd=%d\n", name, size, fd.get());
+    }
+    return fd.release();
+}
+
 /*
  * ashmem_create_region - creates a new ashmem region and returns the file
  * descriptor, or <0 on error
@@ -151,6 +377,10 @@
 {
     int ret, save_errno;
 
+    if (has_memfd_support()) {
+        return memfd_create_region(name ? name : "none", size);
+    }
+
     int fd = __ashmem_open();
     if (fd < 0) {
         return fd;
@@ -180,48 +410,86 @@
     return ret;
 }
 
-int ashmem_set_prot_region(int fd, int prot)
-{
-    int ret = __ashmem_is_ashmem(fd, 1);
-    if (ret < 0) {
-        return ret;
+static int memfd_set_prot_region(int fd, int prot) {
+    /* Only proceed if an fd needs to be write-protected */
+    if (prot & PROT_WRITE) {
+        return 0;
     }
 
-    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot));
+    if (fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE) == -1) {
+        ALOGE("memfd_set_prot_region(%d, %d): F_SEAL_FUTURE_WRITE seal failed: %s\n", fd, prot,
+              strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+int ashmem_set_prot_region(int fd, int prot)
+{
+    if (has_memfd_support() && !memfd_is_ashmem(fd)) {
+        return memfd_set_prot_region(fd, 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) };
-
-    int ret = __ashmem_is_ashmem(fd, 1);
-    if (ret < 0) {
-        return ret;
+    if (!pin_deprecation_warn || debug_log) {
+        ALOGE("Pinning is deprecated since Android Q. Please use trim or other methods.\n");
+        pin_deprecation_warn = true;
     }
 
-    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_PIN, &pin));
+    if (has_memfd_support() && !memfd_is_ashmem(fd)) {
+        return 0;
+    }
+
+    // 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) };
-
-    int ret = __ashmem_is_ashmem(fd, 1);
-    if (ret < 0) {
-        return ret;
+    if (!pin_deprecation_warn || debug_log) {
+        ALOGE("Pinning is deprecated since Android Q. Please use trim or other methods.\n");
+        pin_deprecation_warn = true;
     }
 
-    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_UNPIN, &pin));
+    if (has_memfd_support() && !memfd_is_ashmem(fd)) {
+        return 0;
+    }
+
+    // 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)
 {
-    int ret = __ashmem_is_ashmem(fd, 1);
-    if (ret < 0) {
-        return ret;
+    if (has_memfd_support() && !memfd_is_ashmem(fd)) {
+        struct stat sb;
+
+        if (fstat(fd, &sb) == -1) {
+            ALOGE("ashmem_get_size_region(%d): fstat failed: %s\n", fd, strerror(errno));
+            return -1;
+        }
+
+        if (debug_log) {
+            ALOGD("ashmem_get_size_region(%d): %d\n", fd, static_cast<int>(sb.st_size));
+        }
+
+        return sb.st_size;
     }
 
-    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL));
+    return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL)));
+}
+
+void ashmem_init() {
+#ifndef __ANDROID_VNDK__
+    pthread_mutex_lock(&__ashmem_lock);
+    openFd = initOpenAshmemFd();
+    pthread_mutex_unlock(&__ashmem_lock);
+#endif  //__ANDROID_VNDK__
 }
diff --git a/libcutils/ashmem-host.cpp b/libcutils/ashmem-host.cpp
index b2bec99..32446d4 100644
--- a/libcutils/ashmem-host.cpp
+++ b/libcutils/ashmem-host.cpp
@@ -24,7 +24,6 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -83,3 +82,5 @@
 
     return buf.st_size;
 }
+
+void ashmem_init() {}
diff --git a/libcutils/tests/AshmemTest.cpp b/libcutils/ashmem_test.cpp
similarity index 100%
rename from libcutils/tests/AshmemTest.cpp
rename to libcutils/ashmem_test.cpp
diff --git a/libcutils/canned_fs_config.cpp b/libcutils/canned_fs_config.cpp
index 6b5763b..2772ef0 100644
--- a/libcutils/canned_fs_config.cpp
+++ b/libcutils/canned_fs_config.cpp
@@ -21,7 +21,6 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <limits.h>
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 5b79b1d..897a169 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -24,6 +24,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <fnmatch.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -31,28 +32,24 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include <string>
+
+#include <android-base/strings.h>
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
 #include <utils/Compat.h>
 
+#include "fs_config.h"
+
 #ifndef O_BINARY
 #define O_BINARY 0
 #endif
 
-// My kingdom for <endian.h>
-static inline uint16_t get2LE(const uint8_t* src) {
-    return src[0] | (src[1] << 8);
-}
-
-static inline uint64_t get8LE(const uint8_t* src) {
-    uint32_t low, high;
-
-    low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
-    high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
-    return ((uint64_t)high << 32) | (uint64_t)low;
-}
+using android::base::EndsWith;
+using android::base::StartsWith;
 
 #define ALIGN(x, alignment) (((x) + ((alignment)-1)) & ~((alignment)-1))
+#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name))
 
 // Rules for directories.
 // These rules are applied based on "first match", so they
@@ -60,7 +57,7 @@
 // way up to the root.
 
 static const struct fs_path_config android_dirs[] = {
-    // clang-format off
+        // clang-format off
     { 00770, AID_SYSTEM,       AID_CACHE,        0, "cache" },
     { 00555, AID_ROOT,         AID_ROOT,         0, "config" },
     { 00771, AID_SYSTEM,       AID_SYSTEM,       0, "data/app" },
@@ -80,17 +77,18 @@
     { 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_SHELL,        0, "product/bin" },
-    { 00750, AID_ROOT,         AID_SHELL,        0, "sbin" },
+    { 00751, AID_ROOT,         AID_SHELL,        0, "product/bin" },
     { 00777, AID_ROOT,         AID_ROOT,         0, "sdcard" },
     { 00751, AID_ROOT,         AID_SDCARD_R,     0, "storage" },
-    { 00755, AID_ROOT,         AID_SHELL,        0, "system/bin" },
+    { 00751, AID_ROOT,         AID_SHELL,        0, "system/bin" },
     { 00755, AID_ROOT,         AID_ROOT,         0, "system/etc/ppp" },
     { 00755, AID_ROOT,         AID_SHELL,        0, "system/vendor" },
-    { 00755, AID_ROOT,         AID_SHELL,        0, "system/xbin" },
+    { 00751, AID_ROOT,         AID_SHELL,        0, "system/xbin" },
+    { 00755, AID_ROOT,         AID_SHELL,        0, "system/apex/*/bin" },
+    { 00751, AID_ROOT,         AID_SHELL,        0, "vendor/bin" },
     { 00755, AID_ROOT,         AID_SHELL,        0, "vendor" },
     { 00755, AID_ROOT,         AID_ROOT,         0, 0 },
-    // clang-format on
+        // clang-format on
 };
 #ifndef __ANDROID_VNDK__
 auto __for_testing_only__android_dirs = android_dirs;
@@ -107,7 +105,9 @@
 // although the developer is advised to restrict the scope to the /vendor or
 // oem/ file-system since the intent is to provide support for customized
 // portions of a separate vendor.img or oem.img.  Has to remain open so that
-// customization can also land on /system/vendor, /system/oem or /system/odm.
+// customization can also land on /system/vendor, /system/oem, /system/odm,
+// /system/product or /system/product_services.
+//
 // We expect build-time checking or filtering when constructing the associated
 // fs_config_* files (see build/tools/fs_config/fs_config_generate.c)
 static const char ven_conf_dir[] = "/vendor/etc/fs_config_dirs";
@@ -116,11 +116,17 @@
 static const char oem_conf_file[] = "/oem/etc/fs_config_files";
 static const char odm_conf_dir[] = "/odm/etc/fs_config_dirs";
 static const char odm_conf_file[] = "/odm/etc/fs_config_files";
+static const char product_conf_dir[] = "/product/etc/fs_config_dirs";
+static const char product_conf_file[] = "/product/etc/fs_config_files";
+static const char product_services_conf_dir[] = "/product_services/etc/fs_config_dirs";
+static const char product_services_conf_file[] = "/product_services/etc/fs_config_files";
 static const char* conf[][2] = {
-    {sys_conf_file, sys_conf_dir},
-    {ven_conf_file, ven_conf_dir},
-    {oem_conf_file, oem_conf_dir},
-    {odm_conf_file, odm_conf_dir},
+        {sys_conf_file, sys_conf_dir},
+        {ven_conf_file, ven_conf_dir},
+        {oem_conf_file, oem_conf_dir},
+        {odm_conf_file, odm_conf_dir},
+        {product_conf_file, product_conf_dir},
+        {product_services_conf_file, product_services_conf_dir},
 };
 
 // Do not use android_files to grant Linux capabilities.  Use ambient capabilities in their
@@ -130,7 +136,7 @@
 // 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/*" },
@@ -142,18 +148,24 @@
     { 00750, AID_ROOT,      AID_SHELL,     0, "data/nativetest64/*" },
     { 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" },
+    { 00600, AID_ROOT,      AID_ROOT,      0, "odm/build.prop" }, // legacy; only for P release
+    { 00600, AID_ROOT,      AID_ROOT,      0, "odm/default.prop" }, // legacy; only for P release
+    { 00600, AID_ROOT,      AID_ROOT,      0, "odm/etc/build.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" },
-    { 00750, AID_ROOT,      AID_SHELL,     0, "sbin/fs_mgr" },
+    { 00444, AID_ROOT,      AID_ROOT,      0, product_conf_dir + 1 },
+    { 00444, AID_ROOT,      AID_ROOT,      0, product_conf_file + 1 },
+    { 00600, AID_ROOT,      AID_ROOT,      0, "product_services/build.prop" },
+    { 00444, AID_ROOT,      AID_ROOT,      0, product_services_conf_dir + 1 },
+    { 00444, AID_ROOT,      AID_ROOT,      0, product_services_conf_file + 1 },
     { 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" },
@@ -179,15 +191,12 @@
     // 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" },
-    { 00550, AID_SYSTEM,    AID_LOG,      CAP_MASK_LONG(CAP_SYSLOG),
-                                              "system/bin/bootstat" },
     { 00750, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) |
                                            CAP_MASK_LONG(CAP_SETGID),
                                               "system/bin/run-as" },
+    { 00750, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) |
+                                           CAP_MASK_LONG(CAP_SETGID),
+                                              "system/bin/simpleperf_app_runner" },
 
     // Support FIFO scheduling mode in SurfaceFlinger.
     { 00755, AID_SYSTEM,    AID_GRAPHICS,  CAP_MASK_LONG(CAP_SYS_NICE),
@@ -196,16 +205,15 @@
     { 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, "odm/bin/*" },
     { 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/*" },
-    { 00755, AID_ROOT,      AID_ROOT,      0, "system/lib64/valgrind/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/xbin/*" },
+    { 00755, AID_ROOT,      AID_SHELL,     0, "system/apex/*/bin/*" },
     { 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;
@@ -239,48 +247,58 @@
     return fd;
 }
 
-// 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[] = {"odm/", "oem/", "product/", "vendor/"};
+// if path is "odm/<stuff>", "oem/<stuff>", "product/<stuff>",
+// "product_services/<stuff>" or "vendor/<stuff>"
+static bool is_partition(const std::string& path) {
+    static const char* partitions[] = {"odm/", "oem/", "product/", "product_services/", "vendor/"};
     for (size_t i = 0; i < (sizeof(partitions) / sizeof(partitions[0])); ++i) {
-        size_t plen = strlen(partitions[i]);
-        if (len <= plen) continue;
-        if (!strncmp(path, partitions[i], plen)) return true;
+        if (StartsWith(path, partitions[i])) return true;
     }
     return false;
 }
 
-static inline bool prefix_cmp(bool partial, const char* prefix, size_t len, const char* path,
-                              size_t plen) {
-    return ((partial && plen >= len) || (plen == len)) && !strncmp(prefix, path, len);
-}
-
 // alias prefixes of "<partition>/<stuff>" to "system/<partition>/<stuff>" or
 // "system/<partition>/<stuff>" to "<partition>/<stuff>"
-static bool fs_config_cmp(bool partial, const char* prefix, size_t len, const char* path,
-                          size_t plen) {
-    // If name ends in * then allow partial matches.
-    if (!partial && prefix[len - 1] == '*') {
-        len--;
-        partial = true;
+static bool fs_config_cmp(bool dir, const char* prefix, size_t len, const char* path, size_t plen) {
+    std::string pattern(prefix, len);
+    std::string input(path, plen);
+
+    // Massage pattern and input so that they can be used by fnmatch where
+    // directories have to end with /.
+    if (dir) {
+        if (!EndsWith(input, "/")) {
+            input.append("/");
+        }
+
+        if (!EndsWith(pattern, "/*")) {
+            if (EndsWith(pattern, "/")) {
+                pattern.append("*");
+            } else {
+                pattern.append("/*");
+            }
+        }
     }
 
-    if (prefix_cmp(partial, prefix, len, path, plen)) return true;
+    // no FNM_PATHNAME is set in order to match a/b/c/d with a/*
+    // FNM_ESCAPE is set in order to prevent using \\? and \\* and maintenance issues.
+    const int fnm_flags = FNM_NOESCAPE;
+    if (fnmatch(pattern.c_str(), input.c_str(), fnm_flags) == 0) return true;
 
-    static const char system[] = "system/";
-    if (!strncmp(path, system, strlen(system))) {
-        path += strlen(system);
-        plen -= strlen(system);
-    } else if (len <= strlen(system)) {
-        return false;
-    } else if (strncmp(prefix, system, strlen(system))) {
-        return false;
-    } else {
-        prefix += strlen(system);
-        len -= strlen(system);
+    // Check match between logical partition's files and patterns.
+    static constexpr const char* kLogicalPartitions[] = {"system/product/",
+                                                         "system/product_services/",
+                                                         "system/vendor/",
+                                                         "vendor/odm/"};
+    for (auto& logical_partition : kLogicalPartitions) {
+        if (StartsWith(input, logical_partition)) {
+            std::string input_in_partition = input.substr(input.find('/') + 1);
+            if (!is_partition(input_in_partition)) continue;
+            if (fnmatch(pattern.c_str(), input_in_partition.c_str(), fnm_flags) == 0) {
+                return true;
+            }
+        }
     }
-    return is_partition(prefix, len) && prefix_cmp(partial, prefix, len, path, plen);
+    return false;
 }
 #ifndef __ANDROID_VNDK__
 auto __for_testing_only__fs_config_cmp = fs_config_cmp;
@@ -305,7 +323,7 @@
 
         while (TEMP_FAILURE_RETRY(read(fd, &header, sizeof(header))) == sizeof(header)) {
             char* prefix;
-            uint16_t host_len = get2LE((const uint8_t*)&header.len);
+            uint16_t host_len = header.len;
             ssize_t len, remainder = host_len - sizeof(header);
             if (remainder <= 0) {
                 ALOGE("%s len is corrupted", conf[which][dir]);
@@ -330,10 +348,10 @@
             if (fs_config_cmp(dir, prefix, len, path, plen)) {
                 free(prefix);
                 close(fd);
-                *uid = get2LE((const uint8_t*)&(header.uid));
-                *gid = get2LE((const uint8_t*)&(header.gid));
-                *mode = (*mode & (~07777)) | get2LE((const uint8_t*)&(header.mode));
-                *capabilities = get8LE((const uint8_t*)&(header.capabilities));
+                *uid = header.uid;
+                *gid = header.gid;
+                *mode = (*mode & (~07777)) | header.mode;
+                *capabilities = header.capabilities;
                 return;
             }
             free(prefix);
@@ -351,21 +369,3 @@
     *mode = (*mode & (~07777)) | pc->mode;
     *capabilities = pc->capabilities;
 }
-
-ssize_t fs_config_generate(char* buffer, size_t length, const struct fs_path_config* pc) {
-    struct fs_path_config_from_file* p = (struct fs_path_config_from_file*)buffer;
-    size_t len = ALIGN(sizeof(*p) + strlen(pc->prefix) + 1, sizeof(uint64_t));
-
-    if ((length < len) || (len > UINT16_MAX)) {
-        return -ENOSPC;
-    }
-    memset(p, 0, len);
-    uint16_t host_len = len;
-    p->len = get2LE((const uint8_t*)&host_len);
-    p->mode = get2LE((const uint8_t*)&(pc->mode));
-    p->uid = get2LE((const uint8_t*)&(pc->uid));
-    p->gid = get2LE((const uint8_t*)&(pc->gid));
-    p->capabilities = get8LE((const uint8_t*)&(pc->capabilities));
-    strcpy(p->prefix, pc->prefix);
-    return len;
-}
diff --git a/libcutils/fs_config.h b/libcutils/fs_config.h
new file mode 100644
index 0000000..66ad48b
--- /dev/null
+++ b/libcutils/fs_config.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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>
+
+// Binary format for the runtime <partition>/etc/fs_config_(dirs|files) filesystem override files.
+struct fs_path_config_from_file {
+    uint16_t len;
+    uint16_t mode;
+    uint16_t uid;
+    uint16_t gid;
+    uint64_t capabilities;
+    char prefix[];
+} __attribute__((__aligned__(sizeof(uint64_t))));
+
+struct fs_path_config {
+    unsigned mode;
+    unsigned uid;
+    unsigned gid;
+    uint64_t capabilities;
+    const char* prefix;
+};
diff --git a/libcutils/fs_config_test.cpp b/libcutils/fs_config_test.cpp
new file mode 100644
index 0000000..9627152
--- /dev/null
+++ b/libcutils/fs_config_test.cpp
@@ -0,0 +1,259 @@
+/*
+ * 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 <string>
+
+#include <gtest/gtest.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "fs_config.h"
+
+extern const fs_path_config* __for_testing_only__android_dirs;
+extern const fs_path_config* __for_testing_only__android_files;
+extern bool (*__for_testing_only__fs_config_cmp)(bool, const char*, size_t, const char*, size_t);
+
+// Maximum entries in system/core/libcutils/fs_config.cpp:android_* before we
+// hit a nullptr termination, before we declare the list is just too big or
+// could be missing the nullptr.
+static constexpr size_t max_idx = 4096;
+
+static const struct fs_config_cmp_test {
+    bool dir;
+    const char* prefix;
+    const char* path;
+    bool match;
+} fs_config_cmp_tests[] = {
+        // clang-format off
+    { true,  "system/lib",             "system/lib/hw",           true  },
+    { true,  "vendor/lib",             "system/vendor/lib/hw",    true  },
+    { true,  "system/vendor/lib",      "vendor/lib/hw",           true  },
+    { true,  "system/vendor/lib",      "system/vendor/lib/hw",    true  },
+    { true,  "foo/*/bar/*",            "foo/1/bar/2",             true  },
+    { true,  "foo/*/bar/*",            "foo/1/bar",               true  },
+    { true,  "foo/*/bar/*",            "foo/1/bar/2/3",           true  },
+    { true,  "foo/*/bar/*",            "foo/1/bar/2/3/",          true  },
+    { false, "vendor/bin/wifi",        "system/vendor/bin/w",     false },
+    { false, "vendor/bin/wifi",        "system/vendor/bin/wifi",  true  },
+    { false, "vendor/bin/wifi",        "system/vendor/bin/wifi2", false },
+    { false, "system/vendor/bin/wifi", "system/vendor/bin/wifi",  true, },
+    { false, "odm/bin/wifi",           "system/odm/bin/wifi",     true  },
+    { false, "oem/bin/wifi",           "system/oem/bin/wifi",     true  },
+    { false, "data/bin/wifi",          "system/data/bin/wifi",    false },
+    { false, "system/bin/*",           "system/bin/wifi",         true  },
+    { false, "vendor/bin/*",           "system/vendor/bin/wifi",  true  },
+    { false, "system/bin/*",           "system/bin",              false },
+    { false, "system/vendor/bin/*",    "vendor/bin/wifi",         true  },
+    { false, "foo/*/bar/*",            "foo/1/bar/2",             true  },
+    { false, "foo/*/bar/*",            "foo/1/bar",               false },
+    { false, "foo/*/bar/*",            "foo/1/bar/2/3",           true  },
+    { false, "foo/*/bar/*.so",         "foo/1/bar/2/3",           false },
+    { false, "foo/*/bar/*.so",         "foo/1/bar/2.so",          true  },
+    { false, "foo/*/bar/*.so",         "foo/1/bar/2/3.so",        true  },
+    { false, NULL,                     NULL,                      false },
+        // clang-format on
+};
+
+static bool check_unique(std::vector<const char*>& paths, const std::string& config_name,
+                         const std::string& prefix) {
+    bool retval = false;
+
+    std::string alternate = "system/" + prefix;
+
+    for (size_t idx = 0; idx < paths.size(); ++idx) {
+        size_t second;
+        std::string path(paths[idx]);
+        // check if there are multiple identical paths
+        for (second = idx + 1; second < paths.size(); ++second) {
+            if (path == paths[second]) {
+                GTEST_LOG_(ERROR) << "duplicate paths in " << config_name << ": " << paths[idx];
+                retval = true;
+                break;
+            }
+        }
+
+        // check if path is <partition>/
+        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) {
+                if (path == paths[second]) {
+                    GTEST_LOG_(ERROR) << "duplicate alias paths in " << config_name << ": "
+                                      << paths[idx] << " and " << paths[second]
+                                      << " (remove latter)";
+                    retval = true;
+                    break;
+                }
+            }
+            continue;
+        }
+
+        // check if path is system/<partition>/
+        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) {
+                if (path == paths[second]) break;
+            }
+            if (second >= paths.size()) {
+                GTEST_LOG_(ERROR) << "replace path in " << config_name << ": " << paths[idx]
+                                  << " with " << path;
+                retval = true;
+            }
+        }
+    }
+    return retval;
+}
+
+static bool check_unique(const fs_path_config* paths, const char* type_name,
+                         const std::string& prefix) {
+    std::string config("system/core/libcutils/fs_config.cpp:android_");
+    config += type_name;
+    config += "[]";
+
+    bool retval = false;
+    std::vector<const char*> paths_tmp;
+    for (size_t idx = 0; paths[idx].prefix; ++idx) {
+        if (idx > max_idx) {
+            GTEST_LOG_(WARNING) << config << ": has no end (missing null prefix)";
+            retval = true;
+            break;
+        }
+        paths_tmp.push_back(paths[idx].prefix);
+    }
+
+    return check_unique(paths_tmp, config, prefix) || retval;
+}
+
+static bool check_fs_config_cmp(const fs_config_cmp_test* tests) {
+    bool match, retval = false;
+    for (size_t idx = 0; tests[idx].prefix; ++idx) {
+        match = __for_testing_only__fs_config_cmp(tests[idx].dir, tests[idx].prefix,
+                                                  strlen(tests[idx].prefix), tests[idx].path,
+                                                  strlen(tests[idx].path));
+        if (match != tests[idx].match) {
+            GTEST_LOG_(ERROR) << tests[idx].path << (match ? " matched " : " didn't match ")
+                              << tests[idx].prefix;
+            retval = true;
+            break;
+        }
+    }
+    return retval;
+}
+
+#define endof(pointer, field) (offsetof(typeof(*(pointer)), field) + sizeof((pointer)->field))
+
+static bool check_unique(const std::string& config, const std::string& prefix) {
+    int retval = false;
+
+    std::string data;
+    if (!android::base::ReadFileToString(config, &data)) return retval;
+
+    const fs_path_config_from_file* pc =
+        reinterpret_cast<const fs_path_config_from_file*>(data.c_str());
+    size_t len = data.size();
+
+    std::vector<const char*> paths_tmp;
+    size_t entry_number = 0;
+    while (len > 0) {
+        uint16_t host_len = (len >= endof(pc, len)) ? pc->len : INT16_MAX;
+        if (host_len > len) {
+            GTEST_LOG_(WARNING) << config << ": truncated at entry " << entry_number << " ("
+                                << host_len << " > " << len << ")";
+            const std::string unknown("?");
+            GTEST_LOG_(WARNING)
+                << config << ": entry[" << entry_number << "]={ "
+                << "len=" << ((len >= endof(pc, len))
+                                  ? android::base::StringPrintf("%" PRIu16, pc->len)
+                                  : unknown)
+                << ", mode=" << ((len >= endof(pc, mode))
+                                     ? android::base::StringPrintf("0%" PRIo16, pc->mode)
+                                     : unknown)
+                << ", uid=" << ((len >= endof(pc, uid))
+                                    ? android::base::StringPrintf("%" PRIu16, pc->uid)
+                                    : unknown)
+                << ", gid=" << ((len >= endof(pc, gid))
+                                    ? android::base::StringPrintf("%" PRIu16, pc->gid)
+                                    : unknown)
+                << ", capabilities="
+                << ((len >= endof(pc, capabilities))
+                        ? android::base::StringPrintf("0x%" PRIx64, pc->capabilities)
+                        : unknown)
+                << ", prefix="
+                << ((len >= offsetof(fs_path_config_from_file, prefix))
+                        ? android::base::StringPrintf(
+                              "\"%.*s...", (int)(len - offsetof(fs_path_config_from_file, prefix)),
+                              pc->prefix)
+                        : unknown)
+                << " }";
+            retval = true;
+            break;
+        }
+        paths_tmp.push_back(pc->prefix);
+
+        pc = reinterpret_cast<const fs_path_config_from_file*>(reinterpret_cast<const char*>(pc) +
+                                                               host_len);
+        len -= host_len;
+        ++entry_number;
+    }
+
+    return check_unique(paths_tmp, config, prefix) || retval;
+}
+
+void check_two(const fs_path_config* paths, const char* type_name, const char* prefix) {
+    ASSERT_FALSE(paths == nullptr);
+    ASSERT_FALSE(type_name == nullptr);
+    ASSERT_FALSE(prefix == nullptr);
+    bool check_internal = check_unique(paths, type_name, prefix);
+    EXPECT_FALSE(check_internal);
+    bool check_overrides =
+        check_unique(std::string("/") + prefix + "etc/fs_config_" + type_name, prefix);
+    EXPECT_FALSE(check_overrides);
+}
+
+TEST(fs_config, vendor_dirs_alias) {
+    check_two(__for_testing_only__android_dirs, "dirs", "vendor/");
+}
+
+TEST(fs_config, vendor_files_alias) {
+    check_two(__for_testing_only__android_files, "files", "vendor/");
+}
+
+TEST(fs_config, oem_dirs_alias) {
+    check_two(__for_testing_only__android_dirs, "dirs", "oem/");
+}
+
+TEST(fs_config, oem_files_alias) {
+    check_two(__for_testing_only__android_files, "files", "oem/");
+}
+
+TEST(fs_config, odm_dirs_alias) {
+    check_two(__for_testing_only__android_dirs, "dirs", "odm/");
+}
+
+TEST(fs_config, odm_files_alias) {
+    check_two(__for_testing_only__android_files, "files", "odm/");
+}
+
+TEST(fs_config, system_alias) {
+    EXPECT_FALSE(check_fs_config_cmp(fs_config_cmp_tests));
+}
diff --git a/libcutils/hashmap.cpp b/libcutils/hashmap.cpp
index 65b6ab1..57d6006 100644
--- a/libcutils/hashmap.cpp
+++ b/libcutils/hashmap.cpp
@@ -18,10 +18,9 @@
 
 #include <assert.h>
 #include <errno.h>
-#include <cutils/threads.h>
+#include <pthread.h>
 #include <stdlib.h>
 #include <string.h>
-#include <stdbool.h>
 #include <sys/types.h>
 
 typedef struct Entry Entry;
@@ -37,7 +36,7 @@
     size_t bucketCount;
     int (*hash)(void* key);
     bool (*equals)(void* keyA, void* keyB);
-    mutex_t lock; 
+    pthread_mutex_t lock;
     size_t size;
 };
 
@@ -45,18 +44,18 @@
         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->bucketCount <<= 1;
     }
 
     map->buckets = static_cast<Entry**>(calloc(map->bucketCount, sizeof(Entry*)));
@@ -64,14 +63,14 @@
         free(map);
         return NULL;
     }
-    
+
     map->size = 0;
 
     map->hash = hash;
     map->equals = equals;
-    
-    mutex_init(&map->lock);
-    
+
+    pthread_mutex_init(&map->lock, nullptr);
+
     return map;
 }
 
@@ -90,12 +89,8 @@
     h ^= (((unsigned int) h) >> 14);
     h += (h << 4);
     h ^= (((unsigned int) h) >> 10);
-       
-    return h;
-}
 
-size_t hashmapSize(Hashmap* map) {
-    return map->size;
+    return h;
 }
 
 static inline size_t calculateIndex(size_t bucketCount, int hash) {
@@ -112,7 +107,7 @@
             // Abort expansion.
             return;
         }
-        
+
         // Move over existing entries.
         size_t i;
         for (i = 0; i < map->bucketCount; i++) {
@@ -134,11 +129,11 @@
 }
 
 void hashmapLock(Hashmap* map) {
-    mutex_lock(&map->lock);
+    pthread_mutex_lock(&map->lock);
 }
 
 void hashmapUnlock(Hashmap* map) {
-    mutex_unlock(&map->lock);
+    pthread_mutex_unlock(&map->lock);
 }
 
 void hashmapFree(Hashmap* map) {
@@ -152,7 +147,7 @@
         }
     }
     free(map->buckets);
-    mutex_destroy(&map->lock);
+    pthread_mutex_destroy(&map->lock);
     free(map);
 }
 
@@ -241,54 +236,6 @@
     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);
@@ -311,9 +258,8 @@
     return NULL;
 }
 
-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) {
     size_t i;
     for (i = 0; i < map->bucketCount; i++) {
         Entry* entry = map->buckets[i];
@@ -326,34 +272,3 @@
         }
     }
 }
-
-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/include/cutils/android_reboot.h b/libcutils/include/cutils/android_reboot.h
index 99030ed..cd27eef 100644
--- a/libcutils/include/cutils/android_reboot.h
+++ b/libcutils/include/cutils/android_reboot.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef __CUTILS_ANDROID_REBOOT_H__
-#define __CUTILS_ANDROID_REBOOT_H__
+#pragma once
 
 #include <sys/cdefs.h>
 
@@ -36,10 +35,8 @@
 /* Reboot or shutdown the system.
  * This call uses ANDROID_RB_PROPERTY to request reboot to init process.
  * Due to that, process calling this should have proper selinux permission
- * to write to the property. Otherwise, the call will fail.
+ * to write to the property or the call will fail.
  */
-int android_reboot(int cmd, int flags, const char *arg);
+int android_reboot(unsigned cmd, int flags, const char* arg);
 
 __END_DECLS
-
-#endif /* __CUTILS_ANDROID_REBOOT_H__ */
diff --git a/libcutils/include/cutils/ashmem.h b/libcutils/include/cutils/ashmem.h
index d80caa6..abc5068 100644
--- a/libcutils/include/cutils/ashmem.h
+++ b/libcutils/include/cutils/ashmem.h
@@ -26,6 +26,7 @@
 int ashmem_pin_region(int fd, size_t offset, size_t len);
 int ashmem_unpin_region(int fd, size_t offset, size_t len);
 int ashmem_get_size_region(int fd);
+void ashmem_init();
 
 #ifdef __cplusplus
 }
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/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 7518559..8bc9b48 100644
--- a/libcutils/include/cutils/partition_utils.h
+++ b/libcutils/include/cutils/partition_utils.h
@@ -21,7 +21,7 @@
 
 __BEGIN_DECLS
 
-int partition_wiped(char *source);
+int partition_wiped(const char* source);
 
 __END_DECLS
 
diff --git a/libcutils/include/cutils/sched_policy.h b/libcutils/include/cutils/sched_policy.h
index cf91b76..538ff6b 100644
--- a/libcutils/include/cutils/sched_policy.h
+++ b/libcutils/include/cutils/sched_policy.h
@@ -17,67 +17,10 @@
 #ifndef __CUTILS_SCHED_POLICY_H
 #define __CUTILS_SCHED_POLICY_H
 
-#include <stdbool.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /*
- * Check if Linux kernel enables CPUSETS feature.
- *
- * Return value: 1 if Linux kernel CONFIG_CPUSETS=y; 0 otherwise.
+ * For backwards compatibility only
+ * New users should include processgroup/sched_policy.h directly
  */
-extern bool cpusets_enabled();
-
-/*
- * Check if Linux kernel enables SCHEDTUNE feature (only available in Android
- * common kernel or Linaro LSK, not in mainline Linux as of v4.9)
- *
- * Return value: 1 if Linux kernel CONFIG_CGROUP_SCHEDTUNE=y; 0 otherwise.
- */
-extern bool schedboost_enabled();
-
-/* Keep in sync with THREAD_GROUP_* in frameworks/base/core/java/android/os/Process.java */
-typedef enum {
-    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_RT_APP = 6,
-    SP_RESTRICTED = 7,
-    SP_CNT,
-    SP_MAX = SP_CNT - 1,
-    SP_SYSTEM_DEFAULT = SP_FOREGROUND,
-} SchedPolicy;
-
-extern int set_cpuset_policy(int tid, SchedPolicy policy);
-
-/* Assign thread tid to the cgroup associated with the specified policy.
- * If the thread is a thread group leader, that is it's gettid() == getpid(),
- * then the other threads in the same thread group are _not_ affected.
- * On platforms which support gettid(), zero tid means current thread.
- * Return value: 0 for success, or -errno for error.
- */
-extern int set_sched_policy(int tid, SchedPolicy policy);
-
-/* Return the policy associated with the cgroup of thread tid via policy pointer.
- * On platforms which support gettid(), zero tid means current thread.
- * Return value: 0 for success, or -1 for error and set errno.
- */
-extern int get_sched_policy(int tid, SchedPolicy *policy);
-
-/* Return a displayable string corresponding to policy.
- * Return value: non-NULL NUL-terminated name of unspecified length;
- * the caller is responsible for displaying the useful part of the string.
- */
-extern const char *get_sched_policy_name(SchedPolicy policy);
-
-#ifdef __cplusplus
-}
-#endif
+#include <processgroup/sched_policy.h>
 
 #endif /* __CUTILS_SCHED_POLICY_H */ 
diff --git a/libcutils/include/cutils/sockets.h b/libcutils/include/cutils/sockets.h
index b24468b..285f150 100644
--- a/libcutils/include/cutils/sockets.h
+++ b/libcutils/include/cutils/sockets.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef __CUTILS_SOCKETS_H
-#define __CUTILS_SOCKETS_H
+#pragma once
 
 #include <errno.h>
 #include <limits.h>
@@ -141,19 +140,6 @@
                             const cutils_socket_buffer_t* buffers,
                             size_t num_buffers);
 
-/*
- * socket_peer_is_trusted - Takes a socket which is presumed to be a
- * connected local socket (e.g. AF_LOCAL) and returns whether the peer
- * (the userid that owns the process on the other end of that socket)
- * is one of the two trusted userids, root or shell.
- *
- * Note: This only works as advertised on the Android OS and always
- * just returns true when called on other operating systems.
- */
-extern bool socket_peer_is_trusted(int fd);
-
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* __CUTILS_SOCKETS_H */
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 bbb150d..79b4b35 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -74,7 +74,9 @@
 #define ATRACE_TAG_ADB              (1<<22)
 #define ATRACE_TAG_VIBRATOR         (1<<23)
 #define ATRACE_TAG_AIDL             (1<<24)
-#define ATRACE_TAG_LAST             ATRACE_TAG_AIDL
+#define ATRACE_TAG_NNAPI            (1<<25)
+#define ATRACE_TAG_RRO              (1<<26)
+#define ATRACE_TAG_LAST             ATRACE_TAG_RRO
 
 // 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 5d17698..63c3793 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -129,6 +129,12 @@
 #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 */
+#define AID_IORAPD 1071          /* input/output readahead and pin daemon */
+#define AID_GPU_SERVICE 1072     /* GPU service daemon */
+#define AID_NETWORK_STACK 1073   /* network stack service */
+#define AID_GSID 1074            /* GSI service daemon */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
@@ -185,7 +191,8 @@
  */
 #define AID_OVERFLOWUID 65534 /* unmapped user in the user namespace */
 
-#define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */
+/* use the ranges below to determine whether a process is isolated */
+#define AID_ISOLATED_START 90000 /* start of uids for fully isolated sandboxed processes */
 #define AID_ISOLATED_END 99999   /* end of uids for fully isolated sandboxed processes */
 
 #define AID_USER 100000        /* TODO: switch users over to AID_USER_OFFSET */
diff --git a/libcutils/include/private/fs_config.h b/libcutils/include/private/fs_config.h
index 8926491..8a9a1ff 100644
--- a/libcutils/include/private/fs_config.h
+++ b/libcutils/include/private/fs_config.h
@@ -19,44 +19,17 @@
 ** by the device side of adb.
 */
 
-#ifndef _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
-#define _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
+#pragma once
 
 #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"
+#include <private/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)
- * filesystem override files.
- */
-
-/* The following structure is stored little endian */
-struct fs_path_config_from_file {
-    uint16_t len;
-    uint16_t mode;
-    uint16_t uid;
-    uint16_t gid;
-    uint64_t capabilities;
-    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
@@ -74,8 +47,4 @@
 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/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/tests/MemsetTest.cpp b/libcutils/memset_test.cpp
similarity index 100%
rename from libcutils/tests/MemsetTest.cpp
rename to libcutils/memset_test.cpp
diff --git a/libcutils/tests/multiuser_test.cpp b/libcutils/multiuser_test.cpp
similarity index 100%
rename from libcutils/tests/multiuser_test.cpp
rename to libcutils/multiuser_test.cpp
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.cpp b/libcutils/partition_utils.cpp
index 6735d6c..b840559 100644
--- a/libcutils/partition_utils.cpp
+++ b/libcutils/partition_utils.cpp
@@ -25,7 +25,7 @@
 
 #include <cutils/properties.h>
 
-static int only_one_char(char *buf, int len, char c)
+static int only_one_char(uint8_t *buf, int len, uint8_t c)
 {
     int i, ret;
 
@@ -39,9 +39,8 @@
     return ret;
 }
 
-int partition_wiped(char *source)
-{
-    char buf[4096];
+int partition_wiped(const char* source) {
+    uint8_t buf[4096];
     int fd, ret;
 
     if ((fd = open(source, O_RDONLY)) < 0) {
@@ -67,4 +66,3 @@
 
     return 0;
 }
-
diff --git a/libcutils/tests/PropertiesTest.cpp b/libcutils/properties_test.cpp
similarity index 100%
rename from libcutils/tests/PropertiesTest.cpp
rename to libcutils/properties_test.cpp
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
deleted file mode 100644
index f72ec52..0000000
--- a/libcutils/sched_policy.cpp
+++ /dev/null
@@ -1,475 +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 <cutils/sched_policy.h>
-
-#define LOG_TAG "SchedPolicy"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <log/log.h>
-
-/* 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.
- * Returns the possibly re-mapped policy.
- */
-static inline SchedPolicy _policy(SchedPolicy p)
-{
-   return p == SP_DEFAULT ? SP_SYSTEM_DEFAULT : p;
-}
-
-#if defined(__ANDROID__)
-
-#include <pthread.h>
-#include <sched.h>
-#include <sys/prctl.h>
-
-#define POLICY_DEBUG 0
-
-// timer slack value in nS enforced when the thread moves to background
-#define TIMER_SLACK_BG 40000000
-#define TIMER_SLACK_FG 50000
-
-static pthread_once_t the_once = PTHREAD_ONCE_INIT;
-
-static int __sys_supports_timerslack = -1;
-
-// File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error
-static int system_bg_cpuset_fd = -1;
-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)
-{
-    if (fd < 0) {
-        SLOGE("add_tid_to_cgroup failed; fd=%d\n", fd);
-        errno = EINVAL;
-        return -1;
-    }
-
-    // specialized itoa -- works for tid > 0
-    char text[22];
-    char *end = text + sizeof(text) - 1;
-    char *ptr = end;
-    *ptr = '\0';
-    while (tid > 0) {
-        *--ptr = '0' + (tid % 10);
-        tid = tid / 10;
-    }
-
-    if (write(fd, ptr, end - ptr) < 0) {
-        /*
-         * If the thread is in the process of exiting,
-         * don't flag an error
-         */
-        if (errno == ESRCH)
-                return 0;
-        SLOGW("add_tid_to_cgroup failed to write '%s' (%s); fd=%d\n",
-              ptr, strerror(errno), fd);
-        errno = EINVAL;
-        return -1;
-    }
-
-    return 0;
-}
-
-/*
-    If CONFIG_CPUSETS for Linux kernel is set, "tasks" can be found under
-    /dev/cpuset mounted in init.rc; otherwise, that file does not exist
-    even though the directory, /dev/cpuset, is still created (by init.rc).
-
-    A couple of other candidates (under cpuset mount directory):
-        notify_on_release
-        release_agent
-
-    Yet another way to decide if cpuset is enabled is to parse
-    /proc/self/status and search for lines begin with "Mems_allowed".
-
-    If CONFIG_PROC_PID_CPUSET is set, the existence "/proc/self/cpuset" can
-    be used to decide if CONFIG_CPUSETS is set, so we don't have a dependency
-    on where init.rc mounts cpuset. That's why we'd better require this
-    configuration be set if CONFIG_CPUSETS is set.
-
-    In older releases, this was controlled by build-time configuration.
- */
-bool cpusets_enabled() {
-    static bool enabled = (access("/dev/cpuset/tasks", F_OK) == 0);
-
-    return enabled;
-}
-
-/*
-    Similar to CONFIG_CPUSETS above, but with a different configuration
-    CONFIG_CGROUP_SCHEDTUNE that's in Android common Linux kernel and Linaro
-    Stable Kernel (LSK), but not in mainline Linux as of v4.9.
-
-    In older releases, this was controlled by build-time configuration.
- */
-bool schedboost_enabled() {
-    static bool enabled = (access("/dev/stune/tasks", F_OK) == 0);
-
-    return enabled;
-}
-
-static void __initialize() {
-    const char* filename;
-
-    if (cpusets_enabled()) {
-        if (!access("/dev/cpuset/tasks", W_OK)) {
-
-            filename = "/dev/cpuset/foreground/tasks";
-            fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
-            filename = "/dev/cpuset/background/tasks";
-            bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
-            filename = "/dev/cpuset/system-background/tasks";
-            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";
-                ta_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
-                filename = "/dev/stune/foreground/tasks";
-                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);
-            }
-        }
-    }
-
-    char buf[64];
-    snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", getpid());
-    __sys_supports_timerslack = !access(buf, W_OK);
-}
-
-/*
- * Returns the path under the requested cgroup subsystem (if it exists)
- *
- * The data from /proc/<pid>/cgroup looks (something) like:
- *  2:cpu:/bg_non_interactive
- *  1:cpuacct:/
- *
- * We return the part after the "/", which will be an empty string for
- * the default cgroup.  If the string is longer than "bufLen", the string
- * will be truncated.
- */
-static int getCGroupSubsys(int tid, const char* subsys, char* buf, size_t bufLen)
-{
-#if defined(__ANDROID__)
-    char pathBuf[32];
-    char lineBuf[256];
-    FILE *fp;
-
-    snprintf(pathBuf, sizeof(pathBuf), "/proc/%d/cgroup", tid);
-    if (!(fp = fopen(pathBuf, "re"))) {
-        return -1;
-    }
-
-    while(fgets(lineBuf, sizeof(lineBuf) -1, fp)) {
-        char *next = lineBuf;
-        char *found_subsys;
-        char *grp;
-        size_t len;
-
-        /* Junk the first field */
-        if (!strsep(&next, ":")) {
-            goto out_bad_data;
-        }
-
-        if (!(found_subsys = strsep(&next, ":"))) {
-            goto out_bad_data;
-        }
-
-        if (strcmp(found_subsys, subsys)) {
-            /* Not the subsys we're looking for */
-            continue;
-        }
-
-        if (!(grp = strsep(&next, ":"))) {
-            goto out_bad_data;
-        }
-        grp++; /* Drop the leading '/' */
-        len = strlen(grp);
-        grp[len-1] = '\0'; /* Drop the trailing '\n' */
-
-        if (bufLen <= len) {
-            len = bufLen - 1;
-        }
-        strncpy(buf, grp, len);
-        buf[len] = '\0';
-        fclose(fp);
-        return 0;
-    }
-
-    SLOGE("Failed to find subsys %s", subsys);
-    fclose(fp);
-    return -1;
- out_bad_data:
-    SLOGE("Bad cgroup data {%s}", lineBuf);
-    fclose(fp);
-    return -1;
-#else
-    errno = ENOSYS;
-    return -1;
-#endif
-}
-
-int get_sched_policy(int tid, SchedPolicy *policy)
-{
-    if (tid == 0) {
-        tid = gettid();
-    }
-    pthread_once(&the_once, __initialize);
-
-    char grpBuf[32];
-
-    grpBuf[0] = '\0';
-    if (schedboost_enabled()) {
-        if (getCGroupSubsys(tid, "schedtune", grpBuf, sizeof(grpBuf)) < 0) return -1;
-    }
-    if ((grpBuf[0] == '\0') && cpusets_enabled()) {
-        if (getCGroupSubsys(tid, "cpuset", grpBuf, sizeof(grpBuf)) < 0) return -1;
-    }
-    if (grpBuf[0] == '\0') {
-        *policy = SP_FOREGROUND;
-    } else if (!strcmp(grpBuf, "foreground")) {
-        *policy = SP_FOREGROUND;
-    } else if (!strcmp(grpBuf, "system-background")) {
-        *policy = SP_SYSTEM;
-    } else if (!strcmp(grpBuf, "background")) {
-        *policy = SP_BACKGROUND;
-    } else if (!strcmp(grpBuf, "top-app")) {
-        *policy = SP_TOP_APP;
-    } else {
-        errno = ERANGE;
-        return -1;
-    }
-    return 0;
-}
-
-int set_cpuset_policy(int tid, SchedPolicy policy)
-{
-    // in the absence of cpusets, use the old sched policy
-    if (!cpusets_enabled()) {
-        return set_sched_policy(tid, policy);
-    }
-
-    if (tid == 0) {
-        tid = gettid();
-    }
-    policy = _policy(policy);
-    pthread_once(&the_once, __initialize);
-
-    int fd = -1;
-    int boost_fd = -1;
-    switch (policy) {
-    case SP_BACKGROUND:
-        fd = bg_cpuset_fd;
-        boost_fd = bg_schedboost_fd;
-        break;
-    case SP_FOREGROUND:
-    case SP_AUDIO_APP:
-    case SP_AUDIO_SYS:
-        fd = fg_cpuset_fd;
-        boost_fd = fg_schedboost_fd;
-        break;
-    case SP_TOP_APP :
-        fd = ta_cpuset_fd;
-        boost_fd = ta_schedboost_fd;
-        break;
-    case SP_SYSTEM:
-        fd = system_bg_cpuset_fd;
-        break;
-    case SP_RESTRICTED:
-        fd = rs_cpuset_fd;
-        break;
-    default:
-        boost_fd = fd = -1;
-        break;
-    }
-
-    if (add_tid_to_cgroup(tid, fd) != 0) {
-        if (errno != ESRCH && errno != ENOENT)
-            return -errno;
-    }
-
-    if (schedboost_enabled()) {
-        if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
-            if (errno != ESRCH && errno != ENOENT)
-                return -errno;
-        }
-    }
-
-    return 0;
-}
-
-static void set_timerslack_ns(int tid, unsigned long slack) {
-    // v4.6+ kernels support the /proc/<tid>/timerslack_ns interface.
-    // TODO: once we've backported this, log if the open(2) fails.
-    if (__sys_supports_timerslack) {
-        char buf[64];
-        snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", tid);
-        int fd = open(buf, O_WRONLY | O_CLOEXEC);
-        if (fd != -1) {
-            int len = snprintf(buf, sizeof(buf), "%lu", slack);
-            if (write(fd, buf, len) != len) {
-                SLOGE("set_timerslack_ns write failed: %s\n", strerror(errno));
-            }
-            close(fd);
-            return;
-        }
-    }
-
-    // TODO: Remove when /proc/<tid>/timerslack_ns interface is backported.
-    if ((tid == 0) || (tid == gettid())) {
-        if (prctl(PR_SET_TIMERSLACK, slack) == -1) {
-            SLOGE("set_timerslack_ns prctl failed: %s\n", strerror(errno));
-        }
-    }
-}
-
-int set_sched_policy(int tid, SchedPolicy policy)
-{
-    if (tid == 0) {
-        tid = gettid();
-    }
-    policy = _policy(policy);
-    pthread_once(&the_once, __initialize);
-
-#if POLICY_DEBUG
-    char statfile[64];
-    char statline[1024];
-    char thread_name[255];
-
-    snprintf(statfile, sizeof(statfile), "/proc/%d/stat", tid);
-    memset(thread_name, 0, sizeof(thread_name));
-
-    int fd = open(statfile, O_RDONLY | O_CLOEXEC);
-    if (fd >= 0) {
-        int rc = read(fd, statline, 1023);
-        close(fd);
-        statline[rc] = 0;
-        char *p = statline;
-        char *q;
-
-        for (p = statline; *p != '('; p++);
-        p++;
-        for (q = p; *q != ')'; q++);
-
-        strncpy(thread_name, p, (q-p));
-    }
-    switch (policy) {
-    case SP_BACKGROUND:
-        SLOGD("vvv tid %d (%s)", tid, thread_name);
-        break;
-    case SP_FOREGROUND:
-    case SP_AUDIO_APP:
-    case SP_AUDIO_SYS:
-    case SP_TOP_APP:
-        SLOGD("^^^ tid %d (%s)", tid, thread_name);
-        break;
-    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;
-    }
-#endif
-
-    if (schedboost_enabled()) {
-        int boost_fd = -1;
-        switch (policy) {
-        case SP_BACKGROUND:
-            boost_fd = bg_schedboost_fd;
-            break;
-        case SP_FOREGROUND:
-        case SP_AUDIO_APP:
-        case SP_AUDIO_SYS:
-            boost_fd = fg_schedboost_fd;
-            break;
-        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;
-        }
-
-        if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
-            if (errno != ESRCH && errno != ENOENT)
-                return -errno;
-        }
-
-    }
-
-    set_timerslack_ns(tid, policy == SP_BACKGROUND ? TIMER_SLACK_BG : TIMER_SLACK_FG);
-
-    return 0;
-}
-
-#else
-
-/* Stubs for non-Android targets. */
-
-int set_sched_policy(int /*tid*/, SchedPolicy /*policy*/) {
-    return 0;
-}
-
-int get_sched_policy(int /*tid*/, SchedPolicy* policy) {
-    *policy = SP_SYSTEM_DEFAULT;
-    return 0;
-}
-
-#endif
-
-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",
-            [SP_RT_APP] = "rt",     [SP_RESTRICTED] = "rs",
-    };
-    if ((policy < SP_CNT) && (strings[policy] != NULL))
-        return strings[policy];
-    else
-        return "error";
-}
diff --git a/libcutils/sched_policy_test.cpp b/libcutils/sched_policy_test.cpp
new file mode 100644
index 0000000..a321c90
--- /dev/null
+++ b/libcutils/sched_policy_test.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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 <algorithm>
+#include <chrono>
+#include <thread>
+#include <vector>
+
+#include <sys/capability.h>
+
+#include <cutils/sched_policy.h>
+
+#include <gtest/gtest.h>
+
+bool hasCapSysNice() {
+    __user_cap_header_struct header;
+    memset(&header, 0, sizeof(header));
+    header.version = _LINUX_CAPABILITY_VERSION_3;
+
+    __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3];
+    if (capget(&header, &caps[0])) {
+        GTEST_LOG_(WARNING) << "failed to get process capabilities";
+        return false;
+    }
+
+    auto nice_idx = CAP_TO_INDEX(CAP_SYS_NICE);
+    auto nice_mask = CAP_TO_MASK(CAP_SYS_NICE);
+    return caps[nice_idx].effective & nice_mask;
+}
+
+long long medianSleepTime() {
+    std::vector<long long> sleepTimes;
+    constexpr size_t numSamples = 100;
+
+    for (size_t i = 0; i < numSamples; i++) {
+        auto start = std::chrono::steady_clock::now();
+        std::this_thread::sleep_for(std::chrono::nanoseconds(1));
+        auto end = std::chrono::steady_clock::now();
+
+        auto diff = end - start;
+        sleepTimes.push_back(diff.count());
+    }
+
+    constexpr auto median = numSamples / 2;
+    std::nth_element(sleepTimes.begin(), sleepTimes.begin() + median,
+            sleepTimes.end());
+    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 (!schedboost_enabled()) {
+        // schedboost_enabled() (i.e. CONFIG_CGROUP_SCHEDTUNE) is optional;
+        // it's only needed on devices using energy-aware scheduler.
+        GTEST_LOG_(INFO) << "skipping test that requires CONFIG_CGROUP_SCHEDTUNE";
+        return;
+    }
+
+    ASSERT_EQ(0, set_sched_policy(0, SP_BACKGROUND));
+    AssertPolicy(SP_BACKGROUND);
+
+    ASSERT_EQ(0, set_sched_policy(0, SP_FOREGROUND));
+    AssertPolicy(SP_FOREGROUND);
+}
+
+TEST(SchedPolicy, set_sched_policy_timerslack) {
+    if (!hasCapSysNice()) {
+        GTEST_LOG_(INFO) << "skipping test that requires CAP_SYS_NICE";
+        return;
+    }
+
+    // A measureable effect of scheduling policy is that the kernel has 800x
+    // greater slack time in waking up a sleeping background thread.
+    //
+    // Look for 10x difference in how long FB and BG threads actually sleep
+    // when trying to sleep for 1 ns.  This difference is large enough not
+    // to happen by chance, but small enough (compared to 800x) to keep inherent
+    // fuzziness in scheduler behavior from causing false negatives.
+    const unsigned int BG_FG_SLACK_FACTOR = 10;
+
+    ASSERT_EQ(0, set_sched_policy(0, SP_BACKGROUND));
+    auto bgSleepTime = medianSleepTime();
+
+    ASSERT_EQ(0, set_sched_policy(0, SP_FOREGROUND));
+    auto fgSleepTime = medianSleepTime();
+
+    ASSERT_GT(bgSleepTime, fgSleepTime * BG_FG_SLACK_FACTOR);
+}
+
+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/sockets_test.cpp b/libcutils/sockets_test.cpp
similarity index 100%
rename from libcutils/tests/sockets_test.cpp
rename to libcutils/sockets_test.cpp
diff --git a/libcutils/sockets_unix.cpp b/libcutils/sockets_unix.cpp
index 2849aa8..6acdcd8 100644
--- a/libcutils/sockets_unix.cpp
+++ b/libcutils/sockets_unix.cpp
@@ -16,8 +16,6 @@
 
 #include <cutils/sockets.h>
 
-#define LOG_TAG "socket-unix"
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -27,43 +25,8 @@
 #include <time.h>
 #include <unistd.h>
 
-#include <cutils/android_get_control_file.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>
-#define __android_unused
-#else
-#define __android_unused __attribute__((__unused__))
-#endif
-
-bool socket_peer_is_trusted(int fd __android_unused) {
-#if defined(__ANDROID__)
-    ucred cr;
-    socklen_t len = sizeof(cr);
-    int n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
-
-    if (n != 0) {
-        ALOGE("could not get socket credentials: %s\n", strerror(errno));
-        return false;
-    }
-
-    if ((cr.uid != AID_ROOT) && (cr.uid != AID_SHELL)) {
-        ALOGE("untrusted userid on other end of socket: userid %d\n", cr.uid);
-        return false;
-    }
-#endif
-
-    return true;
-}
-
 int socket_close(int sock) {
     return close(sock);
 }
@@ -94,6 +57,7 @@
     return writev(sock, iovec_buffers, num_buffers);
 }
 
+#if defined(__ANDROID__)
 int android_get_control_socket(const char* name) {
     int fd = __android_get_control_from_env(ANDROID_SOCKET_ENV_PREFIX, name);
 
@@ -102,15 +66,20 @@
     // 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;
 }
+#else
+int android_get_control_socket(const char*) {
+    return -1;
+}
+#endif
diff --git a/libcutils/str_parms.cpp b/libcutils/str_parms.cpp
index f5a52a7..d818c51 100644
--- a/libcutils/str_parms.cpp
+++ b/libcutils/str_parms.cpp
@@ -354,12 +354,8 @@
 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;
+    hashmapForEach(str_parms->map, combine_strings, &str);
+    return (str != NULL) ? str : strdup("");
 }
 
 static bool dump_entry(void* key, void* value, void* /*context*/) {
diff --git a/libcutils/tests/test_str_parms.cpp b/libcutils/str_parms_test.cpp
similarity index 100%
rename from libcutils/tests/test_str_parms.cpp
rename to libcutils/str_parms_test.cpp
diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
deleted file mode 100644
index 7884190..0000000
--- a/libcutils/tests/Android.bp
+++ /dev/null
@@ -1,89 +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.
-
-cc_defaults {
-    name: "libcutils_test_default",
-    srcs: ["sockets_test.cpp"],
-
-    target: {
-        android: {
-            srcs: [
-                "AshmemTest.cpp",
-                "MemsetTest.cpp",
-                "PropertiesTest.cpp",
-                "sched_policy_test.cpp",
-                "trace-dev_test.cpp",
-                "test_str_parms.cpp",
-                "android_get_control_socket_test.cpp",
-                "android_get_control_file_test.cpp",
-                "multiuser_test.cpp",
-                "fs_config.cpp",
-            ],
-        },
-
-        not_windows: {
-            srcs: [
-                "test_str_parms.cpp",
-            ],
-        },
-    },
-
-    multilib: {
-        lib32: {
-            suffix: "32",
-        },
-        lib64: {
-            suffix: "64",
-        },
-    },
-
-    cflags: [
-        "-Wall",
-        "-Wextra",
-        "-Werror",
-    ],
-}
-
-test_libraries = [
-    "libcutils",
-    "liblog",
-    "libbase",
-]
-
-cc_test {
-    name: "libcutils_test",
-    test_suites: ["device-tests"],
-    defaults: ["libcutils_test_default"],
-    host_supported: true,
-    shared_libs: test_libraries,
-}
-
-cc_test {
-    name: "libcutils_test_static",
-    test_suites: ["device-tests"],
-    defaults: ["libcutils_test_default"],
-    static_libs: ["libc"] + test_libraries,
-    stl: "libc++_static",
-
-    target: {
-        android: {
-            static_executable: true,
-        },
-        windows: {
-            host_ldlibs: ["-lws2_32"],
-
-            enabled: true,
-        },
-    },
-}
diff --git a/libcutils/tests/AndroidTest.xml b/libcutils/tests/AndroidTest.xml
deleted file mode 100644
index dd7aca2..0000000
--- a/libcutils/tests/AndroidTest.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<configuration description="Config for libcutils_test">
-    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
-        <option name="cleanup" value="true" />
-        <option name="push" value="libcutils_test->/data/local/tmp/libcutils_test" />
-    </target_preparer>
-    <option name="test-suite-tag" value="apct" />
-    <test class="com.android.tradefed.testtype.GTest" >
-        <option name="native-test-device-path" value="/data/local/tmp" />
-        <option name="module-name" value="libcutils_test" />
-    </test>
-</configuration>
\ No newline at end of file
diff --git a/libcutils/tests/android_get_control_file_test.cpp b/libcutils/tests/android_get_control_file_test.cpp
deleted file mode 100644
index 6c6fd2a..0000000
--- a/libcutils/tests/android_get_control_file_test.cpp
+++ /dev/null
@@ -1,50 +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 <ctype.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <time.h>
-
-#include <string>
-
-#include <android-base/stringprintf.h>
-#include <android-base/test_utils.h>
-#include <cutils/android_get_control_file.h>
-#include <gtest/gtest.h>
-
-TEST(FilesTest, android_get_control_file) {
-    TemporaryFile tf;
-    ASSERT_GE(tf.fd, 0);
-
-    std::string key(ANDROID_FILE_ENV_PREFIX);
-    key += tf.path;
-
-    std::for_each(key.begin(), key.end(), [] (char& c) { c = isalnum(c) ? c : '_'; });
-
-    EXPECT_EQ(unsetenv(key.c_str()), 0);
-    EXPECT_EQ(android_get_control_file(tf.path), -1);
-
-    EXPECT_EQ(setenv(key.c_str(), android::base::StringPrintf("%d", tf.fd).c_str(), true), 0);
-
-    EXPECT_EQ(android_get_control_file(tf.path), tf.fd);
-    close(tf.fd);
-    EXPECT_EQ(android_get_control_file(tf.path), -1);
-    EXPECT_EQ(unsetenv(key.c_str()), 0);
-    EXPECT_EQ(android_get_control_file(tf.path), -1);
-}
diff --git a/libcutils/tests/fs_config.cpp b/libcutils/tests/fs_config.cpp
deleted file mode 100644
index d5dc66a..0000000
--- a/libcutils/tests/fs_config.cpp
+++ /dev/null
@@ -1,248 +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 <inttypes.h>
-
-#include <string>
-
-#include <gtest/gtest.h>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-
-#include <private/android_filesystem_config.h>
-#include <private/fs_config.h>
-
-extern const fs_path_config* __for_testing_only__android_dirs;
-extern const fs_path_config* __for_testing_only__android_files;
-extern bool (*__for_testing_only__fs_config_cmp)(bool, const char*, size_t, const char*, size_t);
-
-// Maximum entries in system/core/libcutils/fs_config.cpp:android_* before we
-// hit a nullptr termination, before we declare the list is just too big or
-// could be missing the nullptr.
-static constexpr size_t max_idx = 4096;
-
-static const struct fs_config_cmp_test {
-    bool dir;
-    const char* prefix;
-    const char* path;
-    bool match;
-} fs_config_cmp_tests[] = {
-    // clang-format off
-    { true,  "system/lib",             "system/lib/hw",           true  },
-    { true,  "vendor/lib",             "system/vendor/lib/hw",    true  },
-    { true,  "system/vendor/lib",      "vendor/lib/hw",           true  },
-    { true,  "system/vendor/lib",      "system/vendor/lib/hw",    true  },
-    { false, "vendor/bin/wifi",        "system/vendor/bin/w",     false },
-    { false, "vendor/bin/wifi",        "system/vendor/bin/wifi",  true  },
-    { false, "vendor/bin/wifi",        "system/vendor/bin/wifi2", false },
-    { false, "system/vendor/bin/wifi", "system/vendor/bin/wifi",  true, },
-    { false, "odm/bin/wifi",           "system/odm/bin/wifi",     true  },
-    { false, "oem/bin/wifi",           "system/oem/bin/wifi",     true  },
-    { false, "data/bin/wifi",          "system/data/bin/wifi",    false },
-    { false, "system/bin/*",           "system/bin/wifi",         true  },
-    { false, "vendor/bin/*",           "system/vendor/bin/wifi",  true  },
-    { false, "system/bin/*",           "system/bin",              false },
-    { false, "system/vendor/bin/*",    "vendor/bin/wifi",         true  },
-    { false, NULL,                     NULL,                      false },
-    // clang-format on
-};
-
-static bool check_unique(std::vector<const char*>& paths, const std::string& config_name,
-                         const std::string& prefix) {
-    bool retval = false;
-
-    std::string alternate = "system/" + prefix;
-
-    for (size_t idx = 0; idx < paths.size(); ++idx) {
-        size_t second;
-        std::string path(paths[idx]);
-        // check if there are multiple identical paths
-        for (second = idx + 1; second < paths.size(); ++second) {
-            if (path == paths[second]) {
-                GTEST_LOG_(ERROR) << "duplicate paths in " << config_name << ": " << paths[idx];
-                retval = true;
-                break;
-            }
-        }
-
-        // check if path is <partition>/
-        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) {
-                if (path == paths[second]) {
-                    GTEST_LOG_(ERROR) << "duplicate alias paths in " << config_name << ": "
-                                      << paths[idx] << " and " << paths[second]
-                                      << " (remove latter)";
-                    retval = true;
-                    break;
-                }
-            }
-            continue;
-        }
-
-        // check if path is system/<partition>/
-        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) {
-                if (path == paths[second]) break;
-            }
-            if (second >= paths.size()) {
-                GTEST_LOG_(ERROR) << "replace path in " << config_name << ": " << paths[idx]
-                                  << " with " << path;
-                retval = true;
-            }
-        }
-    }
-    return retval;
-}
-
-static bool check_unique(const fs_path_config* paths, const char* type_name,
-                         const std::string& prefix) {
-    std::string config("system/core/libcutils/fs_config.cpp:android_");
-    config += type_name;
-    config += "[]";
-
-    bool retval = false;
-    std::vector<const char*> paths_tmp;
-    for (size_t idx = 0; paths[idx].prefix; ++idx) {
-        if (idx > max_idx) {
-            GTEST_LOG_(WARNING) << config << ": has no end (missing null prefix)";
-            retval = true;
-            break;
-        }
-        paths_tmp.push_back(paths[idx].prefix);
-    }
-
-    return check_unique(paths_tmp, config, prefix) || retval;
-}
-
-static bool check_fs_config_cmp(const fs_config_cmp_test* tests) {
-    bool match, retval = false;
-    for (size_t idx = 0; tests[idx].prefix; ++idx) {
-        match = __for_testing_only__fs_config_cmp(tests[idx].dir, tests[idx].prefix,
-                                                  strlen(tests[idx].prefix), tests[idx].path,
-                                                  strlen(tests[idx].path));
-        if (match != tests[idx].match) {
-            GTEST_LOG_(ERROR) << tests[idx].path << (match ? " matched " : " didn't match ")
-                              << tests[idx].prefix;
-            retval = true;
-            break;
-        }
-    }
-    return retval;
-}
-
-#define endof(pointer, field) (offsetof(typeof(*(pointer)), field) + sizeof((pointer)->field))
-
-static bool check_unique(const std::string& config, const std::string& prefix) {
-    int retval = false;
-
-    std::string data;
-    if (!android::base::ReadFileToString(config, &data)) return retval;
-
-    const fs_path_config_from_file* pc =
-        reinterpret_cast<const fs_path_config_from_file*>(data.c_str());
-    size_t len = data.size();
-
-    std::vector<const char*> paths_tmp;
-    size_t entry_number = 0;
-    while (len > 0) {
-        uint16_t host_len = (len >= endof(pc, len)) ? pc->len : INT16_MAX;
-        if (host_len > len) {
-            GTEST_LOG_(WARNING) << config << ": truncated at entry " << entry_number << " ("
-                                << host_len << " > " << len << ")";
-            const std::string unknown("?");
-            GTEST_LOG_(WARNING)
-                << config << ": entry[" << entry_number << "]={ "
-                << "len=" << ((len >= endof(pc, len))
-                                  ? android::base::StringPrintf("%" PRIu16, pc->len)
-                                  : unknown)
-                << ", mode=" << ((len >= endof(pc, mode))
-                                     ? android::base::StringPrintf("0%" PRIo16, pc->mode)
-                                     : unknown)
-                << ", uid=" << ((len >= endof(pc, uid))
-                                    ? android::base::StringPrintf("%" PRIu16, pc->uid)
-                                    : unknown)
-                << ", gid=" << ((len >= endof(pc, gid))
-                                    ? android::base::StringPrintf("%" PRIu16, pc->gid)
-                                    : unknown)
-                << ", capabilities="
-                << ((len >= endof(pc, capabilities))
-                        ? android::base::StringPrintf("0x%" PRIx64, pc->capabilities)
-                        : unknown)
-                << ", prefix="
-                << ((len >= offsetof(fs_path_config_from_file, prefix))
-                        ? android::base::StringPrintf(
-                              "\"%.*s...", (int)(len - offsetof(fs_path_config_from_file, prefix)),
-                              pc->prefix)
-                        : unknown)
-                << " }";
-            retval = true;
-            break;
-        }
-        paths_tmp.push_back(pc->prefix);
-
-        pc = reinterpret_cast<const fs_path_config_from_file*>(reinterpret_cast<const char*>(pc) +
-                                                               host_len);
-        len -= host_len;
-        ++entry_number;
-    }
-
-    return check_unique(paths_tmp, config, prefix) || retval;
-}
-
-void check_two(const fs_path_config* paths, const char* type_name, const char* prefix) {
-    ASSERT_FALSE(paths == nullptr);
-    ASSERT_FALSE(type_name == nullptr);
-    ASSERT_FALSE(prefix == nullptr);
-    bool check_internal = check_unique(paths, type_name, prefix);
-    EXPECT_FALSE(check_internal);
-    bool check_overrides =
-        check_unique(std::string("/") + prefix + "etc/fs_config_" + type_name, prefix);
-    EXPECT_FALSE(check_overrides);
-}
-
-TEST(fs_config, vendor_dirs_alias) {
-    check_two(__for_testing_only__android_dirs, "dirs", "vendor/");
-}
-
-TEST(fs_config, vendor_files_alias) {
-    check_two(__for_testing_only__android_files, "files", "vendor/");
-}
-
-TEST(fs_config, oem_dirs_alias) {
-    check_two(__for_testing_only__android_dirs, "dirs", "oem/");
-}
-
-TEST(fs_config, oem_files_alias) {
-    check_two(__for_testing_only__android_files, "files", "oem/");
-}
-
-TEST(fs_config, odm_dirs_alias) {
-    check_two(__for_testing_only__android_dirs, "dirs", "odm/");
-}
-
-TEST(fs_config, odm_files_alias) {
-    check_two(__for_testing_only__android_files, "files", "odm/");
-}
-
-TEST(fs_config, system_alias) {
-    EXPECT_FALSE(check_fs_config_cmp(fs_config_cmp_tests));
-}
diff --git a/libcutils/tests/sched_policy_test.cpp b/libcutils/tests/sched_policy_test.cpp
deleted file mode 100644
index 173174a..0000000
--- a/libcutils/tests/sched_policy_test.cpp
+++ /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.
- */
-
-#include <algorithm>
-#include <chrono>
-#include <thread>
-#include <vector>
-
-#include <sys/capability.h>
-
-#include <cutils/sched_policy.h>
-
-#include <gtest/gtest.h>
-
-bool hasCapSysNice() {
-    __user_cap_header_struct header;
-    memset(&header, 0, sizeof(header));
-    header.version = _LINUX_CAPABILITY_VERSION_3;
-
-    __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3];
-    if (capget(&header, &caps[0])) {
-        GTEST_LOG_(WARNING) << "failed to get process capabilities";
-        return false;
-    }
-
-    auto nice_idx = CAP_TO_INDEX(CAP_SYS_NICE);
-    auto nice_mask = CAP_TO_MASK(CAP_SYS_NICE);
-    return caps[nice_idx].effective & nice_mask;
-}
-
-long long medianSleepTime() {
-    std::vector<long long> sleepTimes;
-    constexpr size_t numSamples = 100;
-
-    for (size_t i = 0; i < numSamples; i++) {
-        auto start = std::chrono::steady_clock::now();
-        std::this_thread::sleep_for(std::chrono::nanoseconds(1));
-        auto end = std::chrono::steady_clock::now();
-
-        auto diff = end - start;
-        sleepTimes.push_back(diff.count());
-    }
-
-    constexpr auto median = numSamples / 2;
-    std::nth_element(sleepTimes.begin(), sleepTimes.begin() + median,
-            sleepTimes.end());
-    return sleepTimes[median];
-}
-
-TEST(SchedPolicy, set_sched_policy) {
-    if (!hasCapSysNice()) {
-        GTEST_LOG_(INFO) << "skipping test that requires CAP_SYS_NICE";
-        return;
-    }
-
-    // A measureable effect of scheduling policy is that the kernel has 800x
-    // greater slack time in waking up a sleeping background thread.
-    //
-    // Look for 100x difference in how long FB and BG threads actually sleep
-    // when trying to sleep for 1 ns.  This difference is large enough not
-    // to happen by chance, but small enough (compared to 800x) to keep inherent
-    // fuzziness in scheduler behavior from causing false negatives.
-    const unsigned int BG_FG_SLACK_FACTOR = 100;
-
-    ASSERT_EQ(0, set_sched_policy(0, SP_BACKGROUND));
-    auto bgSleepTime = medianSleepTime();
-
-    ASSERT_EQ(0, set_sched_policy(0, 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);
-}
diff --git a/libcutils/tests/trace-dev_test.cpp b/libcutils/tests/trace-dev_test.cpp
deleted file mode 100644
index f8d4f00..0000000
--- a/libcutils/tests/trace-dev_test.cpp
+++ /dev/null
@@ -1,295 +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 <sys/types.h>
-#include <unistd.h>
-
-#include <memory>
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <android-base/test_utils.h>
-#include <gtest/gtest.h>
-
-#include "../trace-dev.cpp"
-
-class TraceDevTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    lseek(tmp_file_.fd, 0, SEEK_SET);
-    atrace_marker_fd = tmp_file_.fd;
-  }
-
-  void TearDown() override {
-    atrace_marker_fd = -1;
-  }
-
-  TemporaryFile tmp_file_;
-
-  static std::string MakeName(size_t length) {
-    std::string name;
-    for (size_t i = 0; i < length; i++) {
-      name += '0' + (i % 10);
-    }
-    return name;
-  }
-};
-
-TEST_F(TraceDevTest, atrace_begin_body_normal) {
-  atrace_begin_body("fake_name");
-
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-  std::string actual;
-  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-  std::string expected = android::base::StringPrintf("B|%d|fake_name", getpid());
-  ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_begin_body_exact) {
-  std::string expected = android::base::StringPrintf("B|%d|", getpid());
-  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1);
-  atrace_begin_body(name.c_str());
-
-  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-  std::string actual;
-  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-  expected += name;
-  ASSERT_STREQ(expected.c_str(), actual.c_str());
-
-  // Add a single character and verify we get the exact same value as before.
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-  name += '*';
-  atrace_begin_body(name.c_str());
-  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-  ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_begin_body_truncated) {
-  std::string expected = android::base::StringPrintf("B|%d|", getpid());
-  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-  atrace_begin_body(name.c_str());
-
-  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-  std::string actual;
-  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1;
-  expected += android::base::StringPrintf("%.*s", expected_len, name.c_str());
-  ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_begin_body_normal) {
-  atrace_async_begin_body("fake_name", 12345);
-
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-  std::string actual;
-  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-  std::string expected = android::base::StringPrintf("S|%d|fake_name|12345", getpid());
-  ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_begin_body_exact) {
-  std::string expected = android::base::StringPrintf("S|%d|", getpid());
-  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
-  atrace_async_begin_body(name.c_str(), 12345);
-
-  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-  std::string actual;
-  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-  expected += name + "|12345";
-  ASSERT_STREQ(expected.c_str(), actual.c_str());
-
-  // Add a single character and verify we get the exact same value as before.
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-  name += '*';
-  atrace_async_begin_body(name.c_str(), 12345);
-  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-  ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_begin_body_truncated) {
-  std::string expected = android::base::StringPrintf("S|%d|", getpid());
-  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-  atrace_async_begin_body(name.c_str(), 12345);
-
-  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-  std::string actual;
-  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
-  expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
-  ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_end_body_normal) {
-  atrace_async_end_body("fake_name", 12345);
-
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-  std::string actual;
-  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-  std::string expected = android::base::StringPrintf("F|%d|fake_name|12345", getpid());
-  ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_end_body_exact) {
-  std::string expected = android::base::StringPrintf("F|%d|", getpid());
-  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
-  atrace_async_end_body(name.c_str(), 12345);
-
-  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-  std::string actual;
-  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-  expected += name + "|12345";
-  ASSERT_STREQ(expected.c_str(), actual.c_str());
-
-  // Add a single character and verify we get the exact same value as before.
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-  name += '*';
-  atrace_async_end_body(name.c_str(), 12345);
-  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-  ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_end_body_truncated) {
-  std::string expected = android::base::StringPrintf("F|%d|", getpid());
-  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-  atrace_async_end_body(name.c_str(), 12345);
-
-  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-  std::string actual;
-  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
-  expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
-  ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_int_body_normal) {
-  atrace_int_body("fake_name", 12345);
-
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-  std::string actual;
-  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-  std::string expected = android::base::StringPrintf("C|%d|fake_name|12345", getpid());
-  ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_int_body_exact) {
-  std::string expected = android::base::StringPrintf("C|%d|", getpid());
-  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
-  atrace_int_body(name.c_str(), 12345);
-
-  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-  std::string actual;
-  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-  expected += name + "|12345";
-  ASSERT_STREQ(expected.c_str(), actual.c_str());
-
-  // Add a single character and verify we get the exact same value as before.
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-  name += '*';
-  atrace_int_body(name.c_str(), 12345);
-  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-  ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_int_body_truncated) {
-  std::string expected = android::base::StringPrintf("C|%d|", getpid());
-  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-  atrace_int_body(name.c_str(), 12345);
-
-  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-  std::string actual;
-  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
-  expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
-  ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_int64_body_normal) {
-  atrace_int64_body("fake_name", 17179869183L);
-
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-  std::string actual;
-  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-  std::string expected = android::base::StringPrintf("C|%d|fake_name|17179869183", getpid());
-  ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_int64_body_exact) {
-  std::string expected = android::base::StringPrintf("C|%d|", getpid());
-  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 13);
-  atrace_int64_body(name.c_str(), 17179869183L);
-
-  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-  std::string actual;
-  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-  expected += name + "|17179869183";
-  ASSERT_STREQ(expected.c_str(), actual.c_str());
-
-  // Add a single character and verify we get the exact same value as before.
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-  name += '*';
-  atrace_int64_body(name.c_str(), 17179869183L);
-  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-  ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_int64_body_truncated) {
-  std::string expected = android::base::StringPrintf("C|%d|", getpid());
-  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-  atrace_int64_body(name.c_str(), 17179869183L);
-
-  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-  std::string actual;
-  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 13;
-  expected += android::base::StringPrintf("%.*s|17179869183", expected_len, name.c_str());
-  ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
index f95c6c5..e3da77b 100644
--- a/libcutils/trace-dev.inc
+++ b/libcutils/trace-dev.inc
@@ -24,7 +24,6 @@
 #include <limits.h>
 #include <pthread.h>
 #include <stdatomic.h>
-#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
@@ -81,7 +80,7 @@
 // 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 sys_debuggable = property_get_bool("ro.debuggable", 0);
     bool result = false;
 
     if (sys_debuggable || atrace_is_debuggable) {
diff --git a/libcutils/trace-dev_test.cpp b/libcutils/trace-dev_test.cpp
new file mode 100644
index 0000000..832b36a
--- /dev/null
+++ b/libcutils/trace-dev_test.cpp
@@ -0,0 +1,294 @@
+/*
+ * 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 <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include "../trace-dev.cpp"
+
+class TraceDevTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    lseek(tmp_file_.fd, 0, SEEK_SET);
+    atrace_marker_fd = tmp_file_.fd;
+  }
+
+  void TearDown() override {
+    atrace_marker_fd = -1;
+  }
+
+  TemporaryFile tmp_file_;
+
+  static std::string MakeName(size_t length) {
+    std::string name;
+    for (size_t i = 0; i < length; i++) {
+      name += '0' + (i % 10);
+    }
+    return name;
+  }
+};
+
+TEST_F(TraceDevTest, atrace_begin_body_normal) {
+  atrace_begin_body("fake_name");
+
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  std::string expected = android::base::StringPrintf("B|%d|fake_name", getpid());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_begin_body_exact) {
+  std::string expected = android::base::StringPrintf("B|%d|", getpid());
+  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1);
+  atrace_begin_body(name.c_str());
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  expected += name;
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+  // Add a single character and verify we get the exact same value as before.
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  name += '*';
+  atrace_begin_body(name.c_str());
+  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_begin_body_truncated) {
+  std::string expected = android::base::StringPrintf("B|%d|", getpid());
+  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+  atrace_begin_body(name.c_str());
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1;
+  expected += android::base::StringPrintf("%.*s", expected_len, name.c_str());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_begin_body_normal) {
+  atrace_async_begin_body("fake_name", 12345);
+
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  std::string expected = android::base::StringPrintf("S|%d|fake_name|12345", getpid());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_begin_body_exact) {
+  std::string expected = android::base::StringPrintf("S|%d|", getpid());
+  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
+  atrace_async_begin_body(name.c_str(), 12345);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  expected += name + "|12345";
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+  // Add a single character and verify we get the exact same value as before.
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  name += '*';
+  atrace_async_begin_body(name.c_str(), 12345);
+  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_begin_body_truncated) {
+  std::string expected = android::base::StringPrintf("S|%d|", getpid());
+  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+  atrace_async_begin_body(name.c_str(), 12345);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
+  expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_end_body_normal) {
+  atrace_async_end_body("fake_name", 12345);
+
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  std::string expected = android::base::StringPrintf("F|%d|fake_name|12345", getpid());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_end_body_exact) {
+  std::string expected = android::base::StringPrintf("F|%d|", getpid());
+  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
+  atrace_async_end_body(name.c_str(), 12345);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  expected += name + "|12345";
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+  // Add a single character and verify we get the exact same value as before.
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  name += '*';
+  atrace_async_end_body(name.c_str(), 12345);
+  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_end_body_truncated) {
+  std::string expected = android::base::StringPrintf("F|%d|", getpid());
+  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+  atrace_async_end_body(name.c_str(), 12345);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
+  expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int_body_normal) {
+  atrace_int_body("fake_name", 12345);
+
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  std::string expected = android::base::StringPrintf("C|%d|fake_name|12345", getpid());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int_body_exact) {
+  std::string expected = android::base::StringPrintf("C|%d|", getpid());
+  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
+  atrace_int_body(name.c_str(), 12345);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  expected += name + "|12345";
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+  // Add a single character and verify we get the exact same value as before.
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  name += '*';
+  atrace_int_body(name.c_str(), 12345);
+  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int_body_truncated) {
+  std::string expected = android::base::StringPrintf("C|%d|", getpid());
+  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+  atrace_int_body(name.c_str(), 12345);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
+  expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int64_body_normal) {
+  atrace_int64_body("fake_name", 17179869183L);
+
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  std::string expected = android::base::StringPrintf("C|%d|fake_name|17179869183", getpid());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int64_body_exact) {
+  std::string expected = android::base::StringPrintf("C|%d|", getpid());
+  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 13);
+  atrace_int64_body(name.c_str(), 17179869183L);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  expected += name + "|17179869183";
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+  // Add a single character and verify we get the exact same value as before.
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  name += '*';
+  atrace_int64_body(name.c_str(), 17179869183L);
+  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int64_body_truncated) {
+  std::string expected = android::base::StringPrintf("C|%d|", getpid());
+  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+  atrace_int64_body(name.c_str(), 17179869183L);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 13;
+  expected += android::base::StringPrintf("%.*s|17179869183", expected_len, name.c_str());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
diff --git a/libcutils/uevent.cpp b/libcutils/uevent.cpp
index 2dfceed..721de7c 100644
--- a/libcutils/uevent.cpp
+++ b/libcutils/uevent.cpp
@@ -95,6 +95,8 @@
 int uevent_open_socket(int buf_sz, bool passcred) {
     struct sockaddr_nl addr;
     int on = passcred;
+    int buf_sz_readback = 0;
+    socklen_t optlen = sizeof(buf_sz_readback);
     int s;
 
     memset(&addr, 0, sizeof(addr));
@@ -105,11 +107,21 @@
     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) {
+    if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0 ||
+          getsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz_readback, &optlen) < 0) {
         close(s);
         return -1;
     }
+    /* Only if SO_RCVBUF was not effective, try SO_RCVBUFFORCE. Generally, we
+     * want to avoid SO_RCVBUFFORCE, because it generates SELinux denials in
+     * case we don't have CAP_NET_ADMIN. This is the case, for example, for
+     * healthd. */
+    if (buf_sz_readback < 2 * buf_sz) {
+        if (setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz)) < 0) {
+            close(s);
+            return -1;
+        }
+    }
 
     setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
 
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index bcc0616..d27feb9 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_library_static {
+cc_library {
     name: "libgrallocusage",
     vendor_available: true,
     cflags: [
diff --git a/libgrallocusage/OWNERS b/libgrallocusage/OWNERS
new file mode 100644
index 0000000..154dc6d
--- /dev/null
+++ b/libgrallocusage/OWNERS
@@ -0,0 +1,3 @@
+jessehall@google.com
+olv@google.com
+stoza@google.com
diff --git a/libion/ion.c b/libion/ion.c
index 5836128..b8de5a4 100644
--- a/libion/ion.c
+++ b/libion/ion.c
@@ -55,7 +55,7 @@
 
 int ion_open() {
     int fd = open("/dev/ion", O_RDONLY | O_CLOEXEC);
-    if (fd < 0) ALOGE("open /dev/ion failed!\n");
+    if (fd < 0) ALOGE("open /dev/ion failed: %s", strerror(errno));
 
     return fd;
 }
@@ -69,7 +69,7 @@
 static int ion_ioctl(int fd, int req, void* arg) {
     int ret = ioctl(fd, req, arg);
     if (ret < 0) {
-        ALOGE("ioctl %x failed with code %d: %s\n", req, ret, strerror(errno));
+        ALOGE("ioctl %x failed with code %d: %s", req, ret, strerror(errno));
         return -errno;
     }
     return ret;
@@ -115,12 +115,12 @@
     ret = ion_ioctl(fd, ION_IOC_MAP, &data);
     if (ret < 0) return ret;
     if (data.fd < 0) {
-        ALOGE("map ioctl returned negative fd\n");
+        ALOGE("map ioctl returned negative fd");
         return -EINVAL;
     }
     tmp_ptr = mmap(NULL, length, prot, flags, data.fd, offset);
     if (tmp_ptr == MAP_FAILED) {
-        ALOGE("mmap failed: %s\n", strerror(errno));
+        ALOGE("mmap failed: %s", strerror(errno));
         return -errno;
     }
     *map_fd = data.fd;
@@ -140,7 +140,7 @@
     ret = ion_ioctl(fd, ION_IOC_SHARE, &data);
     if (ret < 0) return ret;
     if (data.fd < 0) {
-        ALOGE("share ioctl returned negative fd\n");
+        ALOGE("share ioctl returned negative fd");
         return -EINVAL;
     }
     *share_fd = data.fd;
diff --git a/libion/ion_4.12.h b/libion/ion_4.12.h
index 6ae79d4..614510c 100644
--- a/libion/ion_4.12.h
+++ b/libion/ion_4.12.h
@@ -1,125 +1,50 @@
-/*
- * 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.
- *
- */
-
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   To edit the content of this header, modify the corresponding
+ ***   source file (e.g. under external/kernel-headers/original/) then
+ ***   run bionic/libc/kernel/tools/update_all.py
+ ***
+ ***   Any manual change here will be lost the next time this script will
+ ***   be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
 #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;
+  __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;
+  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;
+  __u32 cnt;
+  __u32 reserved0;
+  __u64 heaps;
+  __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 */
+#endif
diff --git a/libion/original-kernel-headers/linux/ion_4.12.h b/libion/original-kernel-headers/linux/ion_4.12.h
new file mode 100644
index 0000000..6ae79d4
--- /dev/null
+++ b/libion/original-kernel-headers/linux/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/libkeyutils/Android.bp b/libkeyutils/Android.bp
index 0285259..dda491a 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,19 @@
     cflags: ["-Werror"],
     shared_libs: ["libkeyutils"],
     srcs: ["keyutils_test.cpp"],
+    test_suites: ["device-tests"],
+}
+
+cc_binary {
+    name: "mini-keyctl",
+    srcs: [
+        "mini_keyctl.cpp",
+        "mini_keyctl_utils.cpp"
+    ],
+    shared_libs: [
+        "libbase",
+        "libkeyutils",
+        "liblog",
+    ],
+    cflags: ["-Werror", "-Wall", "-Wextra", "-fexceptions"],
 }
diff --git a/libkeyutils/include/keyutils.h b/libkeyutils/include/keyutils.h
index 585767d..c508f27 100644
--- a/libkeyutils/include/keyutils.h
+++ b/libkeyutils/include/keyutils.h
@@ -51,6 +51,10 @@
 
 long keyctl_unlink(key_serial_t key, key_serial_t keyring);
 
+long keyctl_restrict_keyring(key_serial_t keyring, const char* type, const char* restriction);
+
+long keyctl_get_security(key_serial_t key, char* buffer, size_t buflen);
+
 __END_DECLS
 
 #endif
diff --git a/libkeyutils/keyutils.cpp b/libkeyutils/keyutils.cpp
index 58a2a17..8f63f70 100644
--- a/libkeyutils/keyutils.cpp
+++ b/libkeyutils/keyutils.cpp
@@ -69,3 +69,11 @@
 long keyctl_unlink(key_serial_t key, key_serial_t keyring) {
   return keyctl(KEYCTL_UNLINK, key, keyring);
 }
+
+long keyctl_restrict_keyring(key_serial_t keyring, const char* type, const char* restriction) {
+  return keyctl(KEYCTL_RESTRICT_KEYRING, keyring, type, restriction);
+}
+
+long keyctl_get_security(key_serial_t id, char* buffer, size_t buflen) {
+  return keyctl(KEYCTL_GET_SECURITY, id, buffer, buflen);
+}
diff --git a/libkeyutils/mini_keyctl.cpp b/libkeyutils/mini_keyctl.cpp
new file mode 100644
index 0000000..fe89e62
--- /dev/null
+++ b/libkeyutils/mini_keyctl.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * A tool loads keys to keyring.
+ */
+
+#include "mini_keyctl_utils.h"
+
+#include <error.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <android-base/parseint.h>
+
+static void Usage(int exit_code) {
+  fprintf(stderr, "usage: mini-keyctl <action> [args,]\n");
+  fprintf(stderr, "       mini-keyctl add <type> <desc> <data> <keyring>\n");
+  fprintf(stderr, "       mini-keyctl padd <type> <desc> <keyring>\n");
+  fprintf(stderr, "       mini-keyctl unlink <key> <keyring>\n");
+  fprintf(stderr, "       mini-keyctl restrict_keyring <keyring>\n");
+  fprintf(stderr, "       mini-keyctl security <key>\n");
+  _exit(exit_code);
+}
+
+static key_serial_t parseKeyOrDie(const char* str) {
+  key_serial_t key;
+  if (!android::base::ParseInt(str, &key)) {
+    error(1 /* exit code */, 0 /* errno */, "Unparsable key: '%s'\n", str);
+  }
+  return key;
+}
+
+int main(int argc, const char** argv) {
+  if (argc < 2) Usage(1);
+  const std::string action = argv[1];
+
+  if (action == "add") {
+    if (argc != 6) Usage(1);
+    std::string type = argv[2];
+    std::string desc = argv[3];
+    std::string data = argv[4];
+    std::string keyring = argv[5];
+    return Add(type, desc, data, keyring);
+  } else if (action == "padd") {
+    if (argc != 5) Usage(1);
+    std::string type = argv[2];
+    std::string desc = argv[3];
+    std::string keyring = argv[4];
+    return Padd(type, desc, keyring);
+  } else if (action == "restrict_keyring") {
+    if (argc != 3) Usage(1);
+    std::string keyring = argv[2];
+    return RestrictKeyring(keyring);
+  } else if (action == "unlink") {
+    if (argc != 4) Usage(1);
+    key_serial_t key = parseKeyOrDie(argv[2]);
+    const std::string keyring = argv[3];
+    return Unlink(key, keyring);
+  } else if (action == "security") {
+    if (argc != 3) Usage(1);
+    const char* key_str = argv[2];
+    key_serial_t key = parseKeyOrDie(key_str);
+    std::string context = RetrieveSecurityContext(key);
+    if (context.empty()) {
+      perror(key_str);
+      return 1;
+    }
+    fprintf(stderr, "%s\n", context.c_str());
+    return 0;
+  } else {
+    fprintf(stderr, "Unrecognized action: %s\n", action.c_str());
+    Usage(1);
+  }
+
+  return 0;
+}
diff --git a/libkeyutils/mini_keyctl_utils.cpp b/libkeyutils/mini_keyctl_utils.cpp
new file mode 100644
index 0000000..79b4680
--- /dev/null
+++ b/libkeyutils/mini_keyctl_utils.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <mini_keyctl_utils.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <error.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <iostream>
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <keyutils.h>
+
+static constexpr int kMaxCertSize = 4096;
+
+static std::vector<std::string> SplitBySpace(const std::string& s) {
+  std::istringstream iss(s);
+  return std::vector<std::string>{std::istream_iterator<std::string>{iss},
+                                  std::istream_iterator<std::string>{}};
+}
+
+// Find the keyring id. Because request_key(2) syscall is not available or the key is
+// kernel keyring, the id is looked up from /proc/keys. The keyring description may contain other
+// information in the descritption section depending on the key type, only the first word in the
+// keyring description is used for searching.
+static key_serial_t GetKeyringIdOrDie(const std::string& keyring_desc) {
+  // If the keyring id is already a hex number, directly convert it to keyring id
+  key_serial_t keyring_id;
+  if (android::base::ParseInt(keyring_desc.c_str(), &keyring_id)) {
+    return keyring_id;
+  }
+
+  // Only keys allowed by SELinux rules will be shown here.
+  std::ifstream proc_keys_file("/proc/keys");
+  if (!proc_keys_file.is_open()) {
+    error(1, errno, "Failed to open /proc/keys");
+    return -1;
+  }
+
+  std::string line;
+  while (getline(proc_keys_file, line)) {
+    std::vector<std::string> tokens = SplitBySpace(line);
+    if (tokens.size() < 9) {
+      continue;
+    }
+    std::string key_id = "0x" + tokens[0];
+    std::string key_type = tokens[7];
+    // The key description may contain space.
+    std::string key_desc_prefix = tokens[8];
+    // The prefix has a ":" at the end
+    std::string key_desc_pattern = keyring_desc + ":";
+    if (key_type != "keyring" || key_desc_prefix != key_desc_pattern) {
+      continue;
+    }
+    if (!android::base::ParseInt(key_id.c_str(), &keyring_id)) {
+      error(1, 0, "Unexpected key format in /proc/keys: %s", key_id.c_str());
+      return -1;
+    }
+    return keyring_id;
+  }
+  return -1;
+}
+
+int Unlink(key_serial_t key, const std::string& keyring) {
+  key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
+  if (keyctl_unlink(key, keyring_id) < 0) {
+    error(1, errno, "Failed to unlink key %x from keyring %s", key, keyring.c_str());
+    return 1;
+  }
+  return 0;
+}
+
+int Add(const std::string& type, const std::string& desc, const std::string& data,
+        const std::string& keyring) {
+  if (data.size() > kMaxCertSize) {
+    error(1, 0, "Certificate too large");
+    return 1;
+  }
+
+  key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
+  key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
+
+  if (key < 0) {
+    error(1, errno, "Failed to add key");
+    return 1;
+  }
+
+  std::cout << key << std::endl;
+  return 0;
+}
+
+int Padd(const std::string& type, const std::string& desc, const std::string& keyring) {
+  key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
+
+  // read from stdin to get the certificates
+  std::istreambuf_iterator<char> begin(std::cin), end;
+  std::string data(begin, end);
+
+  if (data.size() > kMaxCertSize) {
+    error(1, 0, "Certificate too large");
+    return 1;
+  }
+
+  key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
+
+  if (key < 0) {
+    error(1, errno, "Failed to add key");
+    return 1;
+  }
+
+  std::cout << key << std::endl;
+  return 0;
+}
+
+int RestrictKeyring(const std::string& keyring) {
+  key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
+  if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
+    error(1, errno, "Cannot restrict keyring '%s'", keyring.c_str());
+    return 1;
+  }
+  return 0;
+}
+
+std::string RetrieveSecurityContext(key_serial_t key) {
+  // Simply assume this size is enough in practice.
+  const int kMaxSupportedSize = 256;
+  std::string context;
+  context.resize(kMaxSupportedSize);
+  long retval = keyctl_get_security(key, context.data(), kMaxSupportedSize);
+  if (retval < 0) {
+    error(1, errno, "Cannot get security context of key %x", key);
+    return std::string();
+  }
+  if (retval > kMaxSupportedSize) {
+    error(1, 0, "The key has unexpectedly long security context than %d", kMaxSupportedSize);
+    return std::string();
+  }
+  context.resize(retval);
+  return context;
+}
diff --git a/libkeyutils/mini_keyctl_utils.h b/libkeyutils/mini_keyctl_utils.h
new file mode 100644
index 0000000..3616831
--- /dev/null
+++ b/libkeyutils/mini_keyctl_utils.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/keyutils.h"
+
+#include <string>
+
+// Add key to a keyring. Returns non-zero if error happens.
+int Add(const std::string& type, const std::string& desc, const std::string& data,
+        const std::string& keyring);
+
+// Add key from stdin to a keyring. Returns non-zero if error happens.
+int Padd(const std::string& type, const std::string& desc, const std::string& keyring);
+
+// Removes the link from a keyring to a key if exists. Return non-zero if error happens.
+int Unlink(key_serial_t key, const std::string& keyring);
+
+// Apply key-linking to a keyring. Return non-zero if error happens.
+int RestrictKeyring(const std::string& keyring);
+
+// Retrieves a key's security context. Return the context string, or empty string on error.
+std::string RetrieveSecurityContext(key_serial_t key);
diff --git a/liblog/.clang-format b/liblog/.clang-format
deleted file mode 100644
index 9db87a8..0000000
--- a/liblog/.clang-format
+++ /dev/null
@@ -1,9 +0,0 @@
-BasedOnStyle: Google
-AllowShortFunctionsOnASingleLine: false
-
-CommentPragmas: NOLINT:.*
-DerivePointerAlignment: false
-PointerAlignment: Left
-PenaltyExcessCharacter: 32
-
-Cpp11BracedListStyle: false
diff --git a/liblog/.clang-format b/liblog/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/liblog/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 7d9e306..53d3ab3 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -15,38 +15,40 @@
 //
 
 liblog_sources = [
-    "config_read.c",
-    "config_write.c",
-    "local_logger.c",
-    "log_event_list.c",
-    "log_event_write.c",
-    "log_ratelimit.cpp",
-    "logger_lock.c",
-    "logger_name.c",
-    "logger_read.c",
-    "logger_write.c",
-    "logprint.c",
-    "stderr_write.c",
+    "config_read.cpp",
+    "config_write.cpp",
+    "log_event_list.cpp",
+    "log_event_write.cpp",
+    "logger_lock.cpp",
+    "logger_name.cpp",
+    "logger_read.cpp",
+    "logger_write.cpp",
+    "logprint.cpp",
+    "stderr_write.cpp",
 ]
 liblog_host_sources = [
-    "fake_log_device.c",
-    "fake_writer.c",
+    "fake_log_device.cpp",
+    "fake_writer.cpp",
 ]
 liblog_target_sources = [
     "event_tag_map.cpp",
     "log_time.cpp",
-    "properties.c",
-    "pmsg_reader.c",
-    "pmsg_writer.c",
-    "logd_reader.c",
-    "logd_writer.c",
+    "properties.cpp",
+    "pmsg_reader.cpp",
+    "pmsg_writer.cpp",
+    "logd_reader.cpp",
+    "logd_writer.cpp",
 ]
 
 cc_library_headers {
     name: "liblog_headers",
     host_supported: true,
     vendor_available: true,
+    recovery_available: true,
+    native_bridge_supported: true,
     export_include_dirs: ["include"],
+    system_shared_libs: [],
+    stl: "none",
     target: {
         windows: {
             enabled: true,
@@ -65,7 +67,8 @@
 cc_library {
     name: "liblog",
     host_supported: true,
-
+    recovery_available: true,
+    native_bridge_supported: true,
     srcs: liblog_sources,
 
     target: {
@@ -74,6 +77,7 @@
             cflags: ["-DFAKE_LOG_DEVICE=1"],
         },
         android: {
+            version_script: "liblog.map.txt",
             srcs: liblog_target_sources,
             // AddressSanitizer runtime library depends on liblog.
             sanitize: {
@@ -82,10 +86,10 @@
         },
         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: {
-            srcs: ["uio.c"],
             enabled: true,
         },
         not_windows: {
@@ -99,9 +103,13 @@
     header_libs: ["liblog_headers"],
     export_header_lib_headers: ["liblog_headers"],
 
+    stubs: {
+        symbol_file: "liblog.map.txt",
+        versions: ["10000"],
+    },
+
     cflags: [
         "-Werror",
-        "-fvisibility=hidden",
         // This is what we want to do:
         //  liblog_cflags := $(shell \
         //   sed -n \
@@ -132,7 +140,7 @@
 
 llndk_library {
     name: "liblog",
+    native_bridge_supported: true,
     symbol_file: "liblog.map.txt",
-    unversioned: true,
     export_include_dirs: ["include_vndk"],
 }
diff --git a/liblog/Android.mk b/liblog/Android.mk
deleted file mode 100644
index 6c4dff5..0000000
--- a/liblog/Android.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-LOCAL_PATH := $(my-dir)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/liblog/README b/liblog/README
deleted file mode 100644
index 5a845be..0000000
--- a/liblog/README
+++ /dev/null
@@ -1,209 +0,0 @@
-LIBLOG(3)          Android Internal NDK Programming Manual           LIBLOG(3)
-
-
-
-NAME
-       liblog - Android Internal NDK logger interfaces
-
-SYNOPSIS
-       /*
-        * Please limit to 24 characters for runtime is loggable,
-        * 16 characters for persist is loggable, and logcat pretty
-        * alignment with limit of 7 characters.
-        */
-       #define LOG_TAG "yourtag"
-       #include <log/log.h>
-
-       ALOG(android_priority, tag, format, ...)
-       IF_ALOG(android_priority, tag)
-       LOG_PRI(priority, tag, format, ...)
-       LOG_PRI_VA(priority, tag, format, args)
-       #define LOG_TAG NULL
-       ALOGV(format, ...)
-       SLOGV(format, ...)
-       RLOGV(format, ...)
-       ALOGV_IF(cond, format, ...)
-       SLOGV_IF(cond, format, ...)
-       RLOGV_IF(cond, format, ...)
-       IF_ALOGC()
-       ALOGD(format, ...)
-       SLOGD(format, ...)
-       RLOGD(format, ...)
-       ALOGD_IF(cond, format, ...)
-       SLOGD_IF(cond, format, ...)
-       RLOGD_IF(cond, format, ...)
-       IF_ALOGD()
-       ALOGI(format, ...)
-       SLOGI(format, ...)
-       RLOGI(format, ...)
-       ALOGI_IF(cond, format, ...)
-       SLOGI_IF(cond, format, ...)
-       RLOGI_IF(cond, format, ...)
-       IF_ALOGI()
-       ALOGW(format, ...)
-       SLOGW(format, ...)
-       RLOGW(format, ...)
-       ALOGW_IF(cond, format, ...)
-       SLOGW_IF(cond, format, ...)
-       RLOGW_IF(cond, format, ...)
-       IF_ALOGW()
-       ALOGE(format, ...)
-       SLOGE(format, ...)
-       RLOGE(format, ...)
-       ALOGE_IF(cond, format, ...)
-       SLOGE_IF(cond, format, ...)
-       RLOGE_IF(cond, format, ...)
-       IF_ALOGE()
-       LOG_FATAL(format, ...)
-       LOG_ALWAYS_FATAL(format, ...)
-       LOG_FATAL_IF(cond, format, ...)
-       LOG_ALWAYS_FATAL_IF(cond, format, ...)
-       ALOG_ASSERT(cond, format, ...)
-       LOG_EVENT_INT(tag, value)
-       LOG_EVENT_LONG(tag, value)
-
-       clockid_t android_log_clockid()
-
-       log_id_t android_logger_get_id(struct logger *logger)
-       int android_logger_clear(struct logger *logger)
-       int android_logger_get_log_size(struct logger *logger)
-       int android_logger_get_log_readable_size(struct logger *logger)
-       int android_logger_get_log_version(struct logger *logger)
-
-       struct logger_list *android_logger_list_alloc(int mode,
-                                                     unsigned int tail,
-                                                     pid_t pid)
-       struct logger *android_logger_open(struct logger_list *logger_list,
-                                          log_id_t id)
-       struct logger_list *android_logger_list_open(log_id_t id, int mode,
-                                                    unsigned int tail,
-                                                    pid_t pid)
-       int android_logger_list_read(struct logger_list *logger_list,
-                                    struct log_msg *log_msg)
-       void android_logger_list_free(struct logger_list *logger_list)
-
-       log_id_t android_name_to_log_id(const char *logName)
-       const char *android_log_id_to_name(log_id_t log_id)
-
-       android_log_context create_android_logger(uint32_t tag)
-
-       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)
-
-       int android_log_write_list(android_log_context ctx,
-                                  log_id_t id = LOG_ID_EVENTS)
-
-       android_log_context create_android_log_parser(const char *msg,
-                                                     size_t len)
-       android_log_list_element android_log_read_next(android_log_context ctx)
-       android_log_list_element android_log_peek_next(android_log_context ctx)
-
-       int android_log_destroy(android_log_context *ctx)
-
-       #include <log/log_transport.h>
-
-       int android_set_log_transport(int transport_flag)
-       int android_get_log_transport()
-
-       Link with -llog
-
-DESCRIPTION
-       liblog  represents  an interface to the volatile Android Logging system
-       for NDK (Native) applications  and  libraries.  Interfaces  for  either
-       writing  or reading logs.  The log buffers are divided up in Main, Sys‐
-       tem, Radio and Events sub-logs.
-
-       The logging interfaces are a series of macros,  all  of  which  can  be
-       overridden individually in order to control the verbosity of the appli‐
-       cation or library.  [ASR]LOG[VDIWE] calls are used  to  log  to  BAsic,
-       System or Radio sub-logs in either the Verbose, Debug, Info, Warning or
-       Error priorities.  [ASR]LOG[VDIWE]_IF calls are used  to  perform  thus
-       based  on a condition being true.  IF_ALOG[VDIWE] calls are true if the
-       current LOG_TAG is enabled at the specified priority.  LOG_ALWAYS_FATAL
-       is  used to ALOG a message, then kill the process.  LOG_FATAL call is a
-       variant of LOG_ALWAYS_FATAL,  only  enabled  in  engineering,  and  not
-       release builds.  ALOG_ASSERT is used to ALOG a message if the condition
-       is  false;   the   condition   is   part   of   the   logged   message.
-       LOG_EVENT_(INT|LONG)  is  used  to  drop binary content into the Events
-       sub-log.
-
-       The log reading interfaces permit opening the  logs  either  singly  or
-       multiply,  retrieving  a  log  entry  at  a  time in time sorted order,
-       optionally limited to a specific pid and tail of the log(s) and finally
-       a  call closing the logs.  A single log can be opened with android_log‐
-       ger_list_open;  or  multiple  logs  can  be  opened  with  android_log‐
-       ger_list_alloc,  calling  in  turn the android_logger_open for each log
-       id.  Each entry can be retrieved  with  android_logger_list_read.   The
-       log(s) can be closed with android_logger_list_free.  The logs should be
-       opened  with an  ANDROID_LOG_RDONLY  mode.   ANDROID_LOG_NONBLOCK  mode
-       will report when the  log reading is done with an  EAGAIN  error return
-       code,  otherwise the  android_logger_list_read  call will block for new
-       entries.
-
-       The  ANDROID_LOG_WRAP  mode flag to the  android_logger_list_alloc_time
-       signals  logd to quiesce  the reader until the buffer is about to prune
-       at the start time then proceed to dumping content.
-
-       The  ANDROID_LOG_PSTORE mode flag to the android_logger_open is used to
-       switch from the active logs to the persistent logs from before the last
-       reboot.
-
-       The value returned by android_logger_open can be used as a parameter to
-       the  android_logger_clear  function to empty the sub-log.  It is recom‐
-       mended to only open log ANDROID_LOG_WRONLY in that case.
-
-       The value returned by android_logger_open can be used as a parameter to
-       the android_logger_get_log_(size|readable_size|version) to retrieve the
-       sub-log maximum size, readable size and log buffer format protocol ver‐
-       sion  respectively.  android_logger_get_id returns the id that was used
-       when  opening  the  sub-log.    It  is  recommended  to  open  the  log
-       ANDROID_LOG_RDONLY in these cases.
-
-       android_set_log_transport()  selects  transport  filters.  Argument  is
-       either LOGGER_DEFAULT, LOGGER_LOGD, LOGGER_NULL or LOGGER_LOCAL. Log to
-       logger daemon for default or logd, drop contents on floor,  or log into
-       local   memory   respectively.       Both   android_set_log_transport()
-       and android_get_log_transport() return the current  transport mask,  or
-       a negative errno for any problems.
-
-ERRORS
-       If messages fail, a negative error code will be returned to the caller.
-
-       The -ENOTCONN return code indicates that the logger daemon is stopped.
-
-       The  -EBADF return code indicates that the log access point can not be
-       opened, or the log buffer id is out of range.
-
-       For the  -EAGAIN  return code,  this means that the logging message was
-       temporarily backed-up either because of Denial Of Service (DOS) logging
-       pressure from some chatty application or service in the Android system,
-       or if too small of a value is set in /proc/sys/net/unix/max_dgram_qlen.
-       To aid in diagnosing the occurence of this,  a binary event from liblog
-       will be sent to the  log  daemon  once a  new  message  can get through
-       indicating how many  messages were  dropped  as a result.   Please take
-       action to resolve the structural problems at the source.
-
-       It is generally not advised for the caller to retry the  -EAGAIN return
-       code as  this  will  only  make the  problem(s)  worse  and  cause your
-       application to temporarily drop to the  logger daemon  priority,  BATCH
-       scheduling policy and background task cgroup. If you require a group of
-       messages to be passed atomically,  merge  them  into  one  message with
-       embedded newlines to the maximum length LOGGER_ENTRY_MAX_PAYLOAD.
-
-       Other return codes  from  writing operation can be returned.  Since the
-       library retries on EINTR, -EINTR should never be returned.
-
-SEE ALSO
-       syslogd(8), klogd, auditd(8)
-
-
-
-                                  08 Feb 2017                        LIBLOG(3)
diff --git a/liblog/README.md b/liblog/README.md
new file mode 100644
index 0000000..98bee9f
--- /dev/null
+++ b/liblog/README.md
@@ -0,0 +1,176 @@
+Android liblog
+--------------
+
+Public Functions and Macros
+---------------------------
+
+    /*
+     * Please limit to 24 characters for runtime is loggable,
+     * 16 characters for persist is loggable, and logcat pretty
+     * alignment with limit of 7 characters.
+    */
+    #define LOG_TAG "yourtag"
+    #include <log/log.h>
+
+    ALOG(android_priority, tag, format, ...)
+    IF_ALOG(android_priority, tag)
+    LOG_PRI(priority, tag, format, ...)
+    LOG_PRI_VA(priority, tag, format, args)
+    #define LOG_TAG NULL
+    ALOGV(format, ...)
+    SLOGV(format, ...)
+    RLOGV(format, ...)
+    ALOGV_IF(cond, format, ...)
+    SLOGV_IF(cond, format, ...)
+    RLOGV_IF(cond, format, ...)
+    IF_ALOGC()
+    ALOGD(format, ...)
+    SLOGD(format, ...)
+    RLOGD(format, ...)
+    ALOGD_IF(cond, format, ...)
+    SLOGD_IF(cond, format, ...)
+    RLOGD_IF(cond, format, ...)
+    IF_ALOGD()
+    ALOGI(format, ...)
+    SLOGI(format, ...)
+    RLOGI(format, ...)
+    ALOGI_IF(cond, format, ...)
+    SLOGI_IF(cond, format, ...)
+    RLOGI_IF(cond, format, ...)
+    IF_ALOGI()
+    ALOGW(format, ...)
+    SLOGW(format, ...)
+    RLOGW(format, ...)
+    ALOGW_IF(cond, format, ...)
+    SLOGW_IF(cond, format, ...)
+    RLOGW_IF(cond, format, ...)
+    IF_ALOGW()
+    ALOGE(format, ...)
+    SLOGE(format, ...)
+    RLOGE(format, ...)
+    ALOGE_IF(cond, format, ...)
+    SLOGE_IF(cond, format, ...)
+    RLOGE_IF(cond, format, ...)
+    IF_ALOGE()
+    LOG_FATAL(format, ...)
+    LOG_ALWAYS_FATAL(format, ...)
+    LOG_FATAL_IF(cond, format, ...)
+    LOG_ALWAYS_FATAL_IF(cond, format, ...)
+    ALOG_ASSERT(cond, format, ...)
+    LOG_EVENT_INT(tag, value)
+    LOG_EVENT_LONG(tag, value)
+
+    clockid_t android_log_clockid()
+
+    log_id_t android_logger_get_id(struct logger *logger)
+    int android_logger_clear(struct logger *logger)
+    int android_logger_get_log_size(struct logger *logger)
+    int android_logger_get_log_readable_size(struct logger *logger)
+    int android_logger_get_log_version(struct logger *logger)
+
+    struct logger_list *android_logger_list_alloc(int mode, unsigned int tail, pid_t pid)
+    struct logger *android_logger_open(struct logger_list *logger_list, log_id_t id)
+    struct logger_list *android_logger_list_open(log_id_t id, int mode, unsigned int tail, pid_t pid)
+    int android_logger_list_read(struct logger_list *logger_list, struct log_msg *log_msg)
+    void android_logger_list_free(struct logger_list *logger_list)
+
+    log_id_t android_name_to_log_id(const char *logName)
+    const char *android_log_id_to_name(log_id_t log_id)
+
+    android_log_context create_android_logger(uint32_t tag)
+
+    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)
+
+    int android_log_write_list(android_log_context ctx, log_id_t id = LOG_ID_EVENTS)
+
+    android_log_context create_android_log_parser(const char *msg, size_t len)
+    android_log_list_element android_log_read_next(android_log_context ctx)
+    android_log_list_element android_log_peek_next(android_log_context ctx)
+
+    int android_log_destroy(android_log_context *ctx)
+
+    #include <log/log_transport.h>
+
+    int android_set_log_transport(int transport_flag)
+    int android_get_log_transport()
+
+Description
+-----------
+
+liblog represents an interface to the volatile Android Logging system for NDK (Native) applications
+and libraries.  Interfaces for either writing or reading logs.  The log buffers are divided up in
+Main, System, Radio and Events sub-logs.
+
+The logging interfaces are a series of macros, all of which can be overridden individually in order
+to control the verbosity of the application or library.  `[ASR]LOG[VDIWE]` calls are used to log to
+BAsic, System or Radio sub-logs in either the Verbose, Debug, Info, Warning or Error priorities.
+`[ASR]LOG[VDIWE]_IF` calls are used to perform thus based on a condition being true.
+`IF_ALOG[VDIWE]` calls are true if the current `LOG_TAG` is enabled at the specified priority.
+`LOG_ALWAYS_FATAL` is used to `ALOG` a message, then kill the process.  `LOG_FATAL` call is a
+variant of `LOG_ALWAYS_FATAL`, only enabled in engineering, and not release builds.  `ALOG_ASSERT`
+is used to `ALOG` a message if the condition is false; the condition is part of the logged message.
+`LOG_EVENT_(INT|LONG)` is used to drop binary content into the Events sub-log.
+
+The log reading interfaces permit opening the logs either singly or multiply, retrieving a log entry
+at a time in time sorted order, optionally limited to a specific pid and tail of the log(s) and
+finally a call closing the logs.  A single log can be opened with `android_logger_list_open()`; or
+multiple logs can be opened with `android_logger_list_alloc()`, calling in turn the
+`android_logger_open()` for each log id.  Each entry can be retrieved with
+`android_logger_list_read()`.  The log(s) can be closed with `android_logger_list_free()`.  The logs
+should be opened with an `ANDROID_LOG_RDONLY` mode.  `ANDROID_LOG_NONBLOCK` mode will report when
+the log reading is done with an `EAGAIN` error return code, otherwise the
+`android_logger_list_read()` call will block for new entries.
+
+The `ANDROID_LOG_WRAP` mode flag to the `android_logger_list_alloc_time()` signals logd to quiesce
+the reader until the buffer is about to prune at the start time then proceed to dumping content.
+
+The `ANDROID_LOG_PSTORE` mode flag to the `android_logger_open()` is used to switch from the active
+logs to the persistent logs from before the last reboot.
+
+The value returned by `android_logger_open()` can be used as a parameter to the
+`android_logger_clear()` function to empty the sub-log.  It is recommended to only open log
+`ANDROID_LOG_WRONLY` in that case.
+
+The value returned by `android_logger_open()` can be used as a parameter to the
+`android_logger_get_log_(size|readable_size|version)` to retrieve the sub-log maximum size, readable
+size and log buffer format protocol version respectively.  `android_logger_get_id()` returns the id
+that was used when opening the sub-log.  It is recommended to open the log `ANDROID_LOG_RDONLY` in
+these cases.
+
+`android_set_log_transport()` selects transport filters.  Argument is either `LOGGER_DEFAULT`,
+`LOGGER_LOGD`, or `LOGGER_NULL`. Log to logger daemon for default or logd, or drop contents on floor
+respectively.  `Both android_set_log_transport()` and `android_get_log_transport()` return the
+current transport mask, or a negative errno for any problems.
+
+Errors
+------
+
+If messages fail, a negative error code will be returned to the caller.
+
+The `-ENOTCONN` return code indicates that the logger daemon is stopped.
+
+The `-EBADF` return code indicates that the log access point can not be opened, or the log buffer id
+is out of range.
+
+For the `-EAGAIN` return code, this means that the logging message was temporarily backed-up either
+because of Denial Of Service (DOS) logging pressure from some chatty application or service in the
+Android system, or if too small of a value is set in /proc/sys/net/unix/max_dgram_qlen.  To aid in
+diagnosing the occurence of this, a binary event from liblog will be sent to the log daemon once a
+new message can get through indicating how many messages were dropped as a result.  Please take
+action to resolve the structural problems at the source.
+
+It is generally not advised for the caller to retry the `-EAGAIN` return code as this will only make
+the problem(s) worse and cause your application to temporarily drop to the logger daemon priority,
+BATCH scheduling policy and background task cgroup. If you require a group of messages to be passed
+atomically, merge them into one message with embedded newlines to the maximum length
+`LOGGER_ENTRY_MAX_PAYLOAD`.
+
+Other return codes from writing operation can be returned.  Since the library retries on `EINTR`,
+`-EINTR` should never be returned.
diff --git a/liblog/config_read.c b/liblog/config_read.c
deleted file mode 100644
index ca80c80..0000000
--- a/liblog/config_read.c
+++ /dev/null
@@ -1,86 +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 <log/log_transport.h>
-
-#include "config_read.h"
-#include "logger.h"
-
-LIBLOG_HIDDEN struct listnode __android_log_transport_read = {
-  &__android_log_transport_read, &__android_log_transport_read
-};
-LIBLOG_HIDDEN struct listnode __android_log_persist_read = {
-  &__android_log_persist_read, &__android_log_persist_read
-};
-
-static void __android_log_add_transport(
-    struct listnode* list, struct android_log_transport_read* transport) {
-  size_t i;
-
-  /* Try to keep one functioning transport for each log buffer id */
-  for (i = LOG_ID_MIN; i < LOG_ID_MAX; i++) {
-    struct android_log_transport_read* transp;
-
-    if (list_empty(list)) {
-      if (!transport->available || ((*transport->available)(i) >= 0)) {
-        list_add_tail(list, &transport->node);
-        return;
-      }
-    } else {
-      read_transport_for_each(transp, list) {
-        if (!transp->available) {
-          return;
-        }
-        if (((*transp->available)(i) < 0) &&
-            (!transport->available || ((*transport->available)(i) >= 0))) {
-          list_add_tail(list, &transport->node);
-          return;
-        }
-      }
-    }
-  }
-}
-
-LIBLOG_HIDDEN void __android_log_config_read() {
-  if (__android_log_transport & LOGGER_LOCAL) {
-    extern struct android_log_transport_read localLoggerRead;
-
-    __android_log_add_transport(&__android_log_transport_read, &localLoggerRead);
-  }
-
-#if (FAKE_LOG_DEVICE == 0)
-  if ((__android_log_transport == LOGGER_DEFAULT) ||
-      (__android_log_transport & LOGGER_LOGD)) {
-    extern struct android_log_transport_read logdLoggerRead;
-    extern struct android_log_transport_read pmsgLoggerRead;
-
-    __android_log_add_transport(&__android_log_transport_read, &logdLoggerRead);
-    __android_log_add_transport(&__android_log_persist_read, &pmsgLoggerRead);
-  }
-#endif
-}
-
-LIBLOG_HIDDEN void __android_log_config_read_close() {
-  struct android_log_transport_read* transport;
-  struct listnode* n;
-
-  read_transport_for_each_safe(transport, n, &__android_log_transport_read) {
-    list_remove(&transport->node);
-  }
-  read_transport_for_each_safe(transport, n, &__android_log_persist_read) {
-    list_remove(&transport->node);
-  }
-}
diff --git a/liblog/config_read.cpp b/liblog/config_read.cpp
new file mode 100644
index 0000000..3139f78
--- /dev/null
+++ b/liblog/config_read.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 <log/log_transport.h>
+
+#include "config_read.h"
+#include "logger.h"
+
+struct listnode __android_log_transport_read = {&__android_log_transport_read,
+                                                &__android_log_transport_read};
+struct listnode __android_log_persist_read = {&__android_log_persist_read,
+                                              &__android_log_persist_read};
+
+static void __android_log_add_transport(struct listnode* list,
+                                        struct android_log_transport_read* transport) {
+  uint32_t i;
+
+  /* Try to keep one functioning transport for each log buffer id */
+  for (i = LOG_ID_MIN; i < LOG_ID_MAX; i++) {
+    struct android_log_transport_read* transp;
+
+    if (list_empty(list)) {
+      if (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0)) {
+        list_add_tail(list, &transport->node);
+        return;
+      }
+    } else {
+      read_transport_for_each(transp, list) {
+        if (!transp->available) {
+          return;
+        }
+        if (((*transp->available)(static_cast<log_id_t>(i)) < 0) &&
+            (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0))) {
+          list_add_tail(list, &transport->node);
+          return;
+        }
+      }
+    }
+  }
+}
+
+void __android_log_config_read() {
+#if (FAKE_LOG_DEVICE == 0)
+  if ((__android_log_transport == LOGGER_DEFAULT) || (__android_log_transport & LOGGER_LOGD)) {
+    extern struct android_log_transport_read logdLoggerRead;
+    extern struct android_log_transport_read pmsgLoggerRead;
+
+    __android_log_add_transport(&__android_log_transport_read, &logdLoggerRead);
+    __android_log_add_transport(&__android_log_persist_read, &pmsgLoggerRead);
+  }
+#endif
+}
+
+void __android_log_config_read_close() {
+  struct android_log_transport_read* transport;
+  struct listnode* n;
+
+  read_transport_for_each_safe(transport, n, &__android_log_transport_read) {
+    list_remove(&transport->node);
+  }
+  read_transport_for_each_safe(transport, n, &__android_log_persist_read) {
+    list_remove(&transport->node);
+  }
+}
diff --git a/liblog/config_read.h b/liblog/config_read.h
index 7b29fa4..212b8a0 100644
--- a/liblog/config_read.h
+++ b/liblog/config_read.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBLOG_CONFIG_READ_H__
-#define _LIBLOG_CONFIG_READ_H__
+#pragma once
 
 #include <cutils/list.h>
 
@@ -23,8 +22,8 @@
 
 __BEGIN_DECLS
 
-extern LIBLOG_HIDDEN struct listnode __android_log_transport_read;
-extern LIBLOG_HIDDEN struct listnode __android_log_persist_read;
+extern struct listnode __android_log_transport_read;
+extern struct listnode __android_log_persist_read;
 
 #define read_transport_for_each(transp, transports)                           \
   for ((transp) = node_to_item((transports)->next,                            \
@@ -47,9 +46,7 @@
        (transp) = node_to_item((n), struct android_log_transport_read, node), \
       (n) = (transp)->node.next)
 
-LIBLOG_HIDDEN void __android_log_config_read();
-LIBLOG_HIDDEN void __android_log_config_read_close();
+void __android_log_config_read();
+void __android_log_config_read_close();
 
 __END_DECLS
-
-#endif /* _LIBLOG_CONFIG_READ_H__ */
diff --git a/liblog/config_write.c b/liblog/config_write.c
deleted file mode 100644
index 0a8b52f..0000000
--- a/liblog/config_write.c
+++ /dev/null
@@ -1,118 +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 <log/log_transport.h>
-
-#include "config_write.h"
-#include "logger.h"
-
-LIBLOG_HIDDEN struct listnode __android_log_transport_write = {
-  &__android_log_transport_write, &__android_log_transport_write
-};
-LIBLOG_HIDDEN struct listnode __android_log_persist_write = {
-  &__android_log_persist_write, &__android_log_persist_write
-};
-
-static void __android_log_add_transport(
-    struct listnode* list, struct android_log_transport_write* transport) {
-  size_t i;
-
-  /* Try to keep one functioning transport for each log buffer id */
-  for (i = LOG_ID_MIN; i < LOG_ID_MAX; i++) {
-    struct android_log_transport_write* transp;
-
-    if (list_empty(list)) {
-      if (!transport->available || ((*transport->available)(i) >= 0)) {
-        list_add_tail(list, &transport->node);
-        return;
-      }
-    } else {
-      write_transport_for_each(transp, list) {
-        if (!transp->available) {
-          return;
-        }
-        if (((*transp->available)(i) < 0) &&
-            (!transport->available || ((*transport->available)(i) >= 0))) {
-          list_add_tail(list, &transport->node);
-          return;
-        }
-      }
-    }
-  }
-}
-
-LIBLOG_HIDDEN void __android_log_config_write() {
-  if (__android_log_transport & LOGGER_LOCAL) {
-    extern struct android_log_transport_write localLoggerWrite;
-
-    __android_log_add_transport(&__android_log_transport_write,
-                                &localLoggerWrite);
-  }
-
-  if ((__android_log_transport == LOGGER_DEFAULT) ||
-      (__android_log_transport & LOGGER_LOGD)) {
-#if (FAKE_LOG_DEVICE == 0)
-    extern struct android_log_transport_write logdLoggerWrite;
-    extern struct android_log_transport_write pmsgLoggerWrite;
-
-    __android_log_add_transport(&__android_log_transport_write,
-                                &logdLoggerWrite);
-    __android_log_add_transport(&__android_log_persist_write, &pmsgLoggerWrite);
-#else
-    extern struct android_log_transport_write fakeLoggerWrite;
-
-    __android_log_add_transport(&__android_log_transport_write,
-                                &fakeLoggerWrite);
-#endif
-  }
-
-  if (__android_log_transport & LOGGER_STDERR) {
-    extern struct android_log_transport_write stderrLoggerWrite;
-
-    /*
-     * stderr logger should be primary if we can be the only one, or if
-     * already in the primary list.  Otherwise land in the persist list.
-     * Remember we can be called here if we are already initialized.
-     */
-    if (list_empty(&__android_log_transport_write)) {
-      __android_log_add_transport(&__android_log_transport_write,
-                                  &stderrLoggerWrite);
-    } else {
-      struct android_log_transport_write* transp;
-      write_transport_for_each(transp, &__android_log_transport_write) {
-        if (transp == &stderrLoggerWrite) {
-          return;
-        }
-      }
-      __android_log_add_transport(&__android_log_persist_write,
-                                  &stderrLoggerWrite);
-    }
-  }
-}
-
-LIBLOG_HIDDEN void __android_log_config_write_close() {
-  struct android_log_transport_write* transport;
-  struct listnode* n;
-
-  write_transport_for_each_safe(transport, n, &__android_log_transport_write) {
-    transport->logMask = 0;
-    list_remove(&transport->node);
-  }
-  write_transport_for_each_safe(transport, n, &__android_log_persist_write) {
-    transport->logMask = 0;
-    list_remove(&transport->node);
-  }
-}
diff --git a/liblog/config_write.cpp b/liblog/config_write.cpp
new file mode 100644
index 0000000..d454379
--- /dev/null
+++ b/liblog/config_write.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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 <log/log_transport.h>
+
+#include "config_write.h"
+#include "logger.h"
+
+struct listnode __android_log_transport_write = {&__android_log_transport_write,
+                                                 &__android_log_transport_write};
+struct listnode __android_log_persist_write = {&__android_log_persist_write,
+                                               &__android_log_persist_write};
+
+static void __android_log_add_transport(struct listnode* list,
+                                        struct android_log_transport_write* transport) {
+  uint32_t i;
+
+  /* Try to keep one functioning transport for each log buffer id */
+  for (i = LOG_ID_MIN; i < LOG_ID_MAX; i++) {
+    struct android_log_transport_write* transp;
+
+    if (list_empty(list)) {
+      if (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0)) {
+        list_add_tail(list, &transport->node);
+        return;
+      }
+    } else {
+      write_transport_for_each(transp, list) {
+        if (!transp->available) {
+          return;
+        }
+        if (((*transp->available)(static_cast<log_id_t>(i)) < 0) &&
+            (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0))) {
+          list_add_tail(list, &transport->node);
+          return;
+        }
+      }
+    }
+  }
+}
+
+void __android_log_config_write() {
+  if ((__android_log_transport == LOGGER_DEFAULT) || (__android_log_transport & LOGGER_LOGD)) {
+#if (FAKE_LOG_DEVICE == 0)
+    extern struct android_log_transport_write logdLoggerWrite;
+    extern struct android_log_transport_write pmsgLoggerWrite;
+
+    __android_log_add_transport(&__android_log_transport_write, &logdLoggerWrite);
+    __android_log_add_transport(&__android_log_persist_write, &pmsgLoggerWrite);
+#else
+    extern struct android_log_transport_write fakeLoggerWrite;
+
+    __android_log_add_transport(&__android_log_transport_write, &fakeLoggerWrite);
+#endif
+  }
+
+  if (__android_log_transport & LOGGER_STDERR) {
+    extern struct android_log_transport_write stderrLoggerWrite;
+
+    /*
+     * stderr logger should be primary if we can be the only one, or if
+     * already in the primary list.  Otherwise land in the persist list.
+     * Remember we can be called here if we are already initialized.
+     */
+    if (list_empty(&__android_log_transport_write)) {
+      __android_log_add_transport(&__android_log_transport_write, &stderrLoggerWrite);
+    } else {
+      struct android_log_transport_write* transp;
+      write_transport_for_each(transp, &__android_log_transport_write) {
+        if (transp == &stderrLoggerWrite) {
+          return;
+        }
+      }
+      __android_log_add_transport(&__android_log_persist_write, &stderrLoggerWrite);
+    }
+  }
+}
+
+void __android_log_config_write_close() {
+  struct android_log_transport_write* transport;
+  struct listnode* n;
+
+  write_transport_for_each_safe(transport, n, &__android_log_transport_write) {
+    transport->logMask = 0;
+    list_remove(&transport->node);
+  }
+  write_transport_for_each_safe(transport, n, &__android_log_persist_write) {
+    transport->logMask = 0;
+    list_remove(&transport->node);
+  }
+}
diff --git a/liblog/config_write.h b/liblog/config_write.h
index db1a083..a901f13 100644
--- a/liblog/config_write.h
+++ b/liblog/config_write.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBLOG_CONFIG_WRITE_H__
-#define _LIBLOG_CONFIG_WRITE_H__
+#pragma once
 
 #include <cutils/list.h>
 
@@ -23,8 +22,8 @@
 
 __BEGIN_DECLS
 
-extern LIBLOG_HIDDEN struct listnode __android_log_transport_write;
-extern LIBLOG_HIDDEN struct listnode __android_log_persist_write;
+extern struct listnode __android_log_transport_write;
+extern struct listnode __android_log_persist_write;
 
 #define write_transport_for_each(transp, transports)                           \
   for ((transp) = node_to_item((transports)->next,                             \
@@ -47,9 +46,7 @@
        (transp) = node_to_item((n), struct android_log_transport_write, node), \
       (n) = (transp)->node.next)
 
-LIBLOG_HIDDEN void __android_log_config_write();
-LIBLOG_HIDDEN void __android_log_config_write_close();
+void __android_log_config_write();
+void __android_log_config_write_close();
 
 __END_DECLS
-
-#endif /* _LIBLOG_CONFIG_WRITE_H__ */
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 2e2bf87..22cf43b 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -72,7 +72,7 @@
   explicit MapString(const std::string& str)
       : alloc(new std::string(str)), str(alloc->data(), alloc->length()) {
   }
-  MapString(MapString&& rval)
+  MapString(MapString&& rval) noexcept
       : alloc(rval.alloc), str(rval.data(), rval.length()) {
     rval.alloc = NULL;
   }
@@ -407,7 +407,7 @@
 //
 // We create a private mapping because we want to terminate the log tag
 // strings with '\0'.
-LIBLOG_ABI_PUBLIC EventTagMap* android_openEventTagMap(const char* fileName) {
+EventTagMap* android_openEventTagMap(const char* fileName) {
   EventTagMap* newTagMap;
   off_t end[NUM_MAPS];
   int save_errno, fd[NUM_MAPS];
@@ -488,7 +488,7 @@
 }
 
 // Close the map.
-LIBLOG_ABI_PUBLIC void android_closeEventTagMap(EventTagMap* map) {
+void android_closeEventTagMap(EventTagMap* map) {
   if (map) delete map;
 }
 
@@ -535,9 +535,7 @@
 }
 
 // Look up an entry in the map.
-LIBLOG_ABI_PUBLIC const char* android_lookupEventTag_len(const EventTagMap* map,
-                                                         size_t* len,
-                                                         unsigned int tag) {
+const char* android_lookupEventTag_len(const EventTagMap* map, size_t* len, unsigned int tag) {
   if (len) *len = 0;
   const TagFmt* str = map->find(tag);
   if (!str) {
@@ -549,8 +547,7 @@
 }
 
 // Look up an entry in the map.
-LIBLOG_ABI_PUBLIC const char* android_lookupEventFormat_len(
-    const EventTagMap* map, size_t* len, unsigned int tag) {
+const char* android_lookupEventFormat_len(const EventTagMap* map, size_t* len, unsigned int tag) {
   if (len) *len = 0;
   const TagFmt* str = map->find(tag);
   if (!str) {
@@ -565,8 +562,7 @@
 // since it will cause the map to change from Shared and backed by a file,
 // to Private Dirty and backed up by swap, albeit highly compressible. By
 // deprecating this function everywhere, we save 100s of MB of memory space.
-LIBLOG_ABI_PUBLIC const char* android_lookupEventTag(const EventTagMap* map,
-                                                     unsigned int tag) {
+const char* android_lookupEventTag(const EventTagMap* map, unsigned int tag) {
   size_t len;
   const char* tagStr = android_lookupEventTag_len(map, &len, tag);
 
@@ -578,9 +574,7 @@
 }
 
 // Look up tagname, generate one if necessary, and return a tag
-LIBLOG_ABI_PUBLIC int android_lookupEventTagNum(EventTagMap* map,
-                                                const char* tagname,
-                                                const char* format, int prio) {
+int android_lookupEventTagNum(EventTagMap* map, const char* tagname, const char* format, int prio) {
   const char* ep = endOfTag(tagname);
   size_t len = ep - tagname;
   if (!len || *ep) {
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
deleted file mode 100644
index 1483c24..0000000
--- a/liblog/fake_log_device.c
+++ /dev/null
@@ -1,688 +0,0 @@
-/*
- * Copyright (C) 2008-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.
- */
-/*
- * Intercepts log messages intended for the Android log device.
- * Messages are printed to stderr.
- */
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#if !defined(_WIN32)
-#include <pthread.h>
-#endif
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#include <android/log.h>
-#include <log/uio.h>
-
-#include "fake_log_device.h"
-#include "log_portability.h"
-
-#define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */
-
-#define kTagSetSize 16 /* arbitrary */
-
-#if 0
-#define TRACE(...) printf("fake_log_device: " __VA_ARGS__)
-#else
-#define TRACE(...) ((void)0)
-#endif
-
-/* from the long-dead utils/Log.cpp */
-typedef enum {
-  FORMAT_OFF = 0,
-  FORMAT_BRIEF,
-  FORMAT_PROCESS,
-  FORMAT_TAG,
-  FORMAT_THREAD,
-  FORMAT_RAW,
-  FORMAT_TIME,
-  FORMAT_THREADTIME,
-  FORMAT_LONG
-} LogFormat;
-
-/*
- * Log driver state.
- */
-typedef struct LogState {
-  /* the fake fd that's seen by the user */
-  int fakeFd;
-
-  /* a printable name for this fake device */
-  char debugName[sizeof("/dev/log/security")];
-
-  /* nonzero if this is a binary log */
-  int isBinary;
-
-  /* global minimum priority */
-  int globalMinPriority;
-
-  /* output format */
-  LogFormat outputFormat;
-
-  /* tags and priorities */
-  struct {
-    char tag[kMaxTagLen];
-    int minPriority;
-  } tagSet[kTagSetSize];
-} LogState;
-
-#if !defined(_WIN32)
-/*
- * Locking.  Since we're emulating a device, we need to be prepared
- * to have multiple callers at the same time.  This lock is used
- * to both protect the fd list and to prevent LogStates from being
- * freed out from under a user.
- */
-static pthread_mutex_t fakeLogDeviceLock = PTHREAD_MUTEX_INITIALIZER;
-
-static void 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(&fakeLogDeviceLock);
-}
-
-static void unlock() {
-  pthread_mutex_unlock(&fakeLogDeviceLock);
-}
-
-#else  // !defined(_WIN32)
-
-#define lock() ((void)0)
-#define unlock() ((void)0)
-
-#endif  // !defined(_WIN32)
-
-/*
- * File descriptor management.
- */
-#define FAKE_FD_BASE 10000
-#define MAX_OPEN_LOGS 8
-static LogState openLogTable[MAX_OPEN_LOGS];
-
-/*
- * Allocate an fd and associate a new LogState with it.
- * The fd is available via the fakeFd field of the return value.
- */
-static LogState* createLogState() {
-  size_t i;
-
-  for (i = 0; i < (sizeof(openLogTable) / sizeof(openLogTable[0])); i++) {
-    if (openLogTable[i].fakeFd == 0) {
-      openLogTable[i].fakeFd = FAKE_FD_BASE + i;
-      return &openLogTable[i];
-    }
-  }
-  return NULL;
-}
-
-/*
- * Translate an fd to a LogState.
- */
-static LogState* fdToLogState(int fd) {
-  if (fd >= FAKE_FD_BASE && fd < FAKE_FD_BASE + MAX_OPEN_LOGS) {
-    return &openLogTable[fd - FAKE_FD_BASE];
-  }
-  return NULL;
-}
-
-/*
- * Unregister the fake fd and free the memory it pointed to.
- */
-static void deleteFakeFd(int fd) {
-  LogState* ls;
-
-  lock();
-
-  ls = fdToLogState(fd);
-  if (ls != NULL) {
-    memset(&openLogTable[fd - FAKE_FD_BASE], 0, sizeof(openLogTable[0]));
-  }
-
-  unlock();
-}
-
-/*
- * Configure logging based on ANDROID_LOG_TAGS environment variable.  We
- * need to parse a string that looks like
- *
- *   *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
- *
- * The tag (or '*' for the global level) comes first, followed by a colon
- * and a letter indicating the minimum priority level we're expected to log.
- * This can be used to reveal or conceal logs with specific tags.
- *
- * We also want to check ANDROID_PRINTF_LOG to determine how the output
- * will look.
- */
-static void configureInitialState(const char* pathName, LogState* logState) {
-  static const int kDevLogLen = sizeof("/dev/log/") - 1;
-
-  strncpy(logState->debugName, pathName, sizeof(logState->debugName));
-  logState->debugName[sizeof(logState->debugName) - 1] = '\0';
-
-  /* identify binary logs */
-  if (!strcmp(pathName + kDevLogLen, "events") ||
-      !strcmp(pathName + kDevLogLen, "security")) {
-    logState->isBinary = 1;
-  }
-
-  /* global min priority defaults to "info" level */
-  logState->globalMinPriority = ANDROID_LOG_INFO;
-
-  /*
-   * This is based on the the long-dead utils/Log.cpp code.
-   */
-  const char* tags = getenv("ANDROID_LOG_TAGS");
-  TRACE("Found ANDROID_LOG_TAGS='%s'\n", tags);
-  if (tags != NULL) {
-    int entry = 0;
-
-    while (*tags != '\0') {
-      char tagName[kMaxTagLen];
-      int i, minPrio;
-
-      while (isspace(*tags)) tags++;
-
-      i = 0;
-      while (*tags != '\0' && !isspace(*tags) && *tags != ':' &&
-             i < kMaxTagLen) {
-        tagName[i++] = *tags++;
-      }
-      if (i == kMaxTagLen) {
-        TRACE("ERROR: env tag too long (%d chars max)\n", kMaxTagLen - 1);
-        return;
-      }
-      tagName[i] = '\0';
-
-      /* default priority, if there's no ":" part; also zero out '*' */
-      minPrio = ANDROID_LOG_VERBOSE;
-      if (tagName[0] == '*' && tagName[1] == '\0') {
-        minPrio = ANDROID_LOG_DEBUG;
-        tagName[0] = '\0';
-      }
-
-      if (*tags == ':') {
-        tags++;
-        if (*tags >= '0' && *tags <= '9') {
-          if (*tags >= ('0' + ANDROID_LOG_SILENT))
-            minPrio = ANDROID_LOG_VERBOSE;
-          else
-            minPrio = *tags - '\0';
-        } else {
-          switch (*tags) {
-            case 'v':
-              minPrio = ANDROID_LOG_VERBOSE;
-              break;
-            case 'd':
-              minPrio = ANDROID_LOG_DEBUG;
-              break;
-            case 'i':
-              minPrio = ANDROID_LOG_INFO;
-              break;
-            case 'w':
-              minPrio = ANDROID_LOG_WARN;
-              break;
-            case 'e':
-              minPrio = ANDROID_LOG_ERROR;
-              break;
-            case 'f':
-              minPrio = ANDROID_LOG_FATAL;
-              break;
-            case 's':
-              minPrio = ANDROID_LOG_SILENT;
-              break;
-            default:
-              minPrio = ANDROID_LOG_DEFAULT;
-              break;
-          }
-        }
-
-        tags++;
-        if (*tags != '\0' && !isspace(*tags)) {
-          TRACE("ERROR: garbage in tag env; expected whitespace\n");
-          TRACE("       env='%s'\n", tags);
-          return;
-        }
-      }
-
-      if (tagName[0] == 0) {
-        logState->globalMinPriority = minPrio;
-        TRACE("+++ global min prio %d\n", logState->globalMinPriority);
-      } else {
-        logState->tagSet[entry].minPriority = minPrio;
-        strcpy(logState->tagSet[entry].tag, tagName);
-        TRACE("+++ entry %d: %s:%d\n", entry, logState->tagSet[entry].tag,
-              logState->tagSet[entry].minPriority);
-        entry++;
-      }
-    }
-  }
-
-  /*
-   * Taken from the long-dead utils/Log.cpp
-   */
-  const char* fstr = getenv("ANDROID_PRINTF_LOG");
-  LogFormat format;
-  if (fstr == NULL) {
-    format = FORMAT_BRIEF;
-  } else {
-    if (strcmp(fstr, "brief") == 0)
-      format = FORMAT_BRIEF;
-    else if (strcmp(fstr, "process") == 0)
-      format = FORMAT_PROCESS;
-    else if (strcmp(fstr, "tag") == 0)
-      format = FORMAT_PROCESS;
-    else if (strcmp(fstr, "thread") == 0)
-      format = FORMAT_PROCESS;
-    else if (strcmp(fstr, "raw") == 0)
-      format = FORMAT_PROCESS;
-    else if (strcmp(fstr, "time") == 0)
-      format = FORMAT_PROCESS;
-    else if (strcmp(fstr, "long") == 0)
-      format = FORMAT_PROCESS;
-    else
-      format = (LogFormat)atoi(fstr);  // really?!
-  }
-
-  logState->outputFormat = format;
-}
-
-/*
- * Return a human-readable string for the priority level.  Always returns
- * a valid string.
- */
-static const char* getPriorityString(int priority) {
-  /* the first character of each string should be unique */
-  static const char* priorityStrings[] = { "Verbose", "Debug", "Info",
-                                           "Warn",    "Error", "Assert" };
-  int idx;
-
-  idx = (int)priority - (int)ANDROID_LOG_VERBOSE;
-  if (idx < 0 ||
-      idx >= (int)(sizeof(priorityStrings) / sizeof(priorityStrings[0])))
-    return "?unknown?";
-  return priorityStrings[idx];
-}
-
-#if defined(_WIN32)
-/*
- * WIN32 does not have writev().
- * Make up something to replace it.
- */
-static ssize_t fake_writev(int fd, const struct iovec* iov, int iovcnt) {
-  ssize_t result = 0;
-  const struct iovec* end = iov + iovcnt;
-  for (; iov < end; iov++) {
-    ssize_t w = write(fd, iov->iov_base, iov->iov_len);
-    if (w != (ssize_t)iov->iov_len) {
-      if (w < 0) return w;
-      return result + w;
-    }
-    result += w;
-  }
-  return result;
-}
-
-#define writev fake_writev
-#endif
-
-/*
- * Write a filtered log message to stderr.
- *
- * Log format parsing taken from the long-dead utils/Log.cpp.
- */
-static void showLog(LogState* state, int logPrio, const char* tag,
-                    const char* msg) {
-#if !defined(_WIN32)
-  struct tm tmBuf;
-#endif
-  struct tm* ptm;
-  char timeBuf[32];
-  char prefixBuf[128], suffixBuf[128];
-  char priChar;
-  time_t when;
-#if !defined(_WIN32)
-  pid_t pid, tid;
-#else
-  uint32_t pid, tid;
-#endif
-
-  TRACE("LOG %d: %s %s", logPrio, tag, msg);
-
-  priChar = getPriorityString(logPrio)[0];
-  when = time(NULL);
-  pid = tid = getpid();  // find gettid()?
-
-/*
- * Get the current date/time in pretty form
- *
- * It's often useful when examining a log with "less" to jump to
- * a specific point in the file by searching for the date/time stamp.
- * For this reason it's very annoying to have regexp meta characters
- * in the time stamp.  Don't use forward slashes, parenthesis,
- * brackets, asterisks, or other special chars here.
- */
-#if !defined(_WIN32)
-  ptm = localtime_r(&when, &tmBuf);
-#else
-  ptm = localtime(&when);
-#endif
-  // strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
-  strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
-
-  /*
-   * Construct a buffer containing the log header and log message.
-   */
-  size_t prefixLen, suffixLen;
-
-  switch (state->outputFormat) {
-    case FORMAT_TAG:
-      prefixLen =
-          snprintf(prefixBuf, sizeof(prefixBuf), "%c/%-8s: ", priChar, tag);
-      strcpy(suffixBuf, "\n");
-      suffixLen = 1;
-      break;
-    case FORMAT_PROCESS:
-      prefixLen =
-          snprintf(prefixBuf, sizeof(prefixBuf), "%c(%5d) ", priChar, pid);
-      suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), "  (%s)\n", tag);
-      break;
-    case FORMAT_THREAD:
-      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c(%5d:%5d) ",
-                           priChar, pid, tid);
-      strcpy(suffixBuf, "\n");
-      suffixLen = 1;
-      break;
-    case FORMAT_RAW:
-      prefixBuf[0] = 0;
-      prefixLen = 0;
-      strcpy(suffixBuf, "\n");
-      suffixLen = 1;
-      break;
-    case FORMAT_TIME:
-      prefixLen =
-          snprintf(prefixBuf, sizeof(prefixBuf), "%s %-8s\n\t", timeBuf, tag);
-      strcpy(suffixBuf, "\n");
-      suffixLen = 1;
-      break;
-    case FORMAT_THREADTIME:
-      prefixLen =
-          snprintf(prefixBuf, sizeof(prefixBuf), "%s %5d %5d %c %-8s \n\t",
-                   timeBuf, pid, tid, priChar, tag);
-      strcpy(suffixBuf, "\n");
-      suffixLen = 1;
-      break;
-    case FORMAT_LONG:
-      prefixLen =
-          snprintf(prefixBuf, sizeof(prefixBuf), "[ %s %5d:%5d %c/%-8s ]\n",
-                   timeBuf, pid, tid, priChar, tag);
-      strcpy(suffixBuf, "\n\n");
-      suffixLen = 2;
-      break;
-    default:
-      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
-                           "%c/%-8s(%5d): ", priChar, tag, pid);
-      strcpy(suffixBuf, "\n");
-      suffixLen = 1;
-      break;
-  }
-
-  /*
-   * Figure out how many lines there will be.
-   */
-  const char* end = msg + strlen(msg);
-  size_t numLines = 0;
-  const char* p = msg;
-  while (p < end) {
-    if (*p++ == '\n') numLines++;
-  }
-  if (p > msg && *(p - 1) != '\n') {
-    numLines++;
-  }
-
-  /*
-   * Create an array of iovecs large enough to write all of
-   * the lines with a prefix and a suffix.
-   */
-  const size_t INLINE_VECS = 64;
-  const size_t MAX_LINES = ((size_t)~0) / (3 * sizeof(struct iovec*));
-  struct iovec stackVec[INLINE_VECS];
-  struct iovec* vec = stackVec;
-  size_t numVecs;
-
-  if (numLines > MAX_LINES) numLines = MAX_LINES;
-
-  numVecs = numLines * 3;  // 3 iovecs per line.
-  if (numVecs > INLINE_VECS) {
-    vec = (struct iovec*)malloc(sizeof(struct iovec) * numVecs);
-    if (vec == NULL) {
-      msg = "LOG: write failed, no memory";
-      numVecs = INLINE_VECS;
-      numLines = numVecs / 3;
-      vec = stackVec;
-    }
-  }
-
-  /*
-   * Fill in the iovec pointers.
-   */
-  p = msg;
-  struct iovec* v = vec;
-  int totalLen = 0;
-  while (numLines > 0 && p < end) {
-    if (prefixLen > 0) {
-      v->iov_base = prefixBuf;
-      v->iov_len = prefixLen;
-      totalLen += prefixLen;
-      v++;
-    }
-    const char* start = p;
-    while (p < end && *p != '\n') {
-      p++;
-    }
-    if ((p - start) > 0) {
-      v->iov_base = (void*)start;
-      v->iov_len = p - start;
-      totalLen += p - start;
-      v++;
-    }
-    if (*p == '\n') p++;
-    if (suffixLen > 0) {
-      v->iov_base = suffixBuf;
-      v->iov_len = suffixLen;
-      totalLen += suffixLen;
-      v++;
-    }
-    numLines -= 1;
-  }
-
-  /*
-   * Write the entire message to the log file with a single writev() call.
-   * We need to use this rather than a collection of printf()s on a FILE*
-   * because of multi-threading and multi-process issues.
-   *
-   * If the file was not opened with O_APPEND, this will produce interleaved
-   * output when called on the same file from multiple processes.
-   *
-   * If the file descriptor is actually a network socket, the writev()
-   * call may return with a partial write.  Putting the writev() call in
-   * a loop can result in interleaved data.  This can be alleviated
-   * somewhat by wrapping the writev call in the Mutex.
-   */
-
-  for (;;) {
-    int cc = writev(fileno(stderr), vec, v - vec);
-
-    if (cc == totalLen) break;
-
-    if (cc < 0) {
-      if (errno == EINTR) continue;
-
-      /* can't really log the failure; for now, throw out a stderr */
-      fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
-      break;
-    } else {
-      /* shouldn't happen when writing to file or tty */
-      fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", cc, totalLen);
-      break;
-    }
-  }
-
-  /* if we allocated storage for the iovecs, free it */
-  if (vec != stackVec) free(vec);
-}
-
-/*
- * Receive a log message.  We happen to know that "vector" has three parts:
- *
- *  priority (1 byte)
- *  tag (N bytes -- null-terminated ASCII string)
- *  message (N bytes -- null-terminated ASCII string)
- */
-LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd, const struct iovec* vector,
-                                    int count) {
-  LogState* state;
-
-  /* Make sure that no-one frees the LogState while we're using it.
-   * Also guarantees that only one thread is in showLog() at a given
-   * time (if it matters).
-   */
-  lock();
-
-  state = fdToLogState(fd);
-  if (state == NULL) {
-    errno = EBADF;
-    goto error;
-  }
-
-  if (state->isBinary) {
-    TRACE("%s: ignoring binary log\n", state->debugName);
-    goto bail;
-  }
-
-  if (count != 3) {
-    TRACE("%s: writevLog with count=%d not expected\n", state->debugName, count);
-    goto error;
-  }
-
-  /* pull out the three fields */
-  int logPrio = *(const char*)vector[0].iov_base;
-  const char* tag = (const char*)vector[1].iov_base;
-  const char* msg = (const char*)vector[2].iov_base;
-
-  /* see if this log tag is configured */
-  int i;
-  int minPrio = state->globalMinPriority;
-  for (i = 0; i < kTagSetSize; i++) {
-    if (state->tagSet[i].minPriority == ANDROID_LOG_UNKNOWN)
-      break; /* reached end of configured values */
-
-    if (strcmp(state->tagSet[i].tag, tag) == 0) {
-      // TRACE("MATCH tag '%s'\n", tag);
-      minPrio = state->tagSet[i].minPriority;
-      break;
-    }
-  }
-
-  if (logPrio >= minPrio) {
-    showLog(state, logPrio, tag, msg);
-  } else {
-    // TRACE("+++ NOLOG(%d): %s %s", logPrio, tag, msg);
-  }
-
-bail:
-  unlock();
-  int len = 0;
-  for (i = 0; i < count; ++i) {
-    len += vector[i].iov_len;
-  }
-  return len;
-
-error:
-  unlock();
-  return -1;
-}
-
-/*
- * Free up our state and close the fake descriptor.
- *
- * The logger API has no means or need to 'stop' or 'close' using the logs,
- * and as such, there is no way for that 'stop' or 'close' to translate into
- * a close operation to the fake log handler. fakeLogClose is provided for
- * completeness only.
- *
- * We have no intention of adding a log close operation as it would complicate
- * every user of the logging API with no gain since the only valid place to
- * call is in the exit handler. Logging can continue in the exit handler to
- * help debug HOST tools ...
- */
-LIBLOG_HIDDEN int fakeLogClose(int fd) {
-  deleteFakeFd(fd);
-  return 0;
-}
-
-/*
- * Open a log output device and return a fake fd.
- */
-LIBLOG_HIDDEN int fakeLogOpen(const char* pathName) {
-  LogState* logState;
-  int fd = -1;
-
-  lock();
-
-  logState = createLogState();
-  if (logState != NULL) {
-    configureInitialState(pathName, logState);
-    fd = logState->fakeFd;
-  } else {
-    errno = ENFILE;
-  }
-
-  unlock();
-
-  return fd;
-}
-
-LIBLOG_HIDDEN ssize_t __send_log_msg(char* buf __unused,
-                                     size_t buf_size __unused) {
-  return -ENODEV;
-}
-
-LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio,
-                                                const char* tag __unused,
-                                                int def) {
-  int logLevel = def;
-  return logLevel >= 0 && prio >= logLevel;
-}
-
-LIBLOG_ABI_PUBLIC int __android_log_is_loggable_len(int prio,
-                                                    const char* tag __unused,
-                                                    size_t len __unused,
-                                                    int def) {
-  int logLevel = def;
-  return logLevel >= 0 && prio >= logLevel;
-}
-
-LIBLOG_ABI_PRIVATE int __android_log_is_debuggable() {
-  return 1;
-}
diff --git a/liblog/fake_log_device.cpp b/liblog/fake_log_device.cpp
new file mode 100644
index 0000000..428a482
--- /dev/null
+++ b/liblog/fake_log_device.cpp
@@ -0,0 +1,669 @@
+/*
+ * Copyright (C) 2008-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.
+ */
+/*
+ * Intercepts log messages intended for the Android log device.
+ * Messages are printed to stderr.
+ */
+
+#include "fake_log_device.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#if !defined(_WIN32)
+#include <pthread.h>
+#endif
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <android/log.h>
+
+#include "log_portability.h"
+
+#define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */
+
+#define kTagSetSize 16 /* arbitrary */
+
+#if 0
+#define TRACE(...) printf("fake_log_device: " __VA_ARGS__)
+#else
+#define TRACE(...) ((void)0)
+#endif
+
+/* from the long-dead utils/Log.cpp */
+typedef enum {
+  FORMAT_OFF = 0,
+  FORMAT_BRIEF,
+  FORMAT_PROCESS,
+  FORMAT_TAG,
+  FORMAT_THREAD,
+  FORMAT_RAW,
+  FORMAT_TIME,
+  FORMAT_THREADTIME,
+  FORMAT_LONG
+} LogFormat;
+
+/*
+ * Log driver state.
+ */
+typedef struct LogState {
+  /* the fake fd that's seen by the user */
+  int fakeFd;
+
+  /* a printable name for this fake device */
+  char debugName[sizeof("/dev/log/security")];
+
+  /* nonzero if this is a binary log */
+  int isBinary;
+
+  /* global minimum priority */
+  int globalMinPriority;
+
+  /* output format */
+  LogFormat outputFormat;
+
+  /* tags and priorities */
+  struct {
+    char tag[kMaxTagLen];
+    int minPriority;
+  } tagSet[kTagSetSize];
+} LogState;
+
+#if !defined(_WIN32)
+/*
+ * Locking.  Since we're emulating a device, we need to be prepared
+ * to have multiple callers at the same time.  This lock is used
+ * to both protect the fd list and to prevent LogStates from being
+ * freed out from under a user.
+ */
+static pthread_mutex_t fakeLogDeviceLock = PTHREAD_MUTEX_INITIALIZER;
+
+static void 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(&fakeLogDeviceLock);
+}
+
+static void unlock() {
+  pthread_mutex_unlock(&fakeLogDeviceLock);
+}
+
+#else  // !defined(_WIN32)
+
+#define lock() ((void)0)
+#define unlock() ((void)0)
+
+#endif  // !defined(_WIN32)
+
+/*
+ * File descriptor management.
+ */
+#define FAKE_FD_BASE 10000
+#define MAX_OPEN_LOGS 8
+static LogState openLogTable[MAX_OPEN_LOGS];
+
+/*
+ * Allocate an fd and associate a new LogState with it.
+ * The fd is available via the fakeFd field of the return value.
+ */
+static LogState* createLogState() {
+  size_t i;
+
+  for (i = 0; i < (sizeof(openLogTable) / sizeof(openLogTable[0])); i++) {
+    if (openLogTable[i].fakeFd == 0) {
+      openLogTable[i].fakeFd = FAKE_FD_BASE + i;
+      return &openLogTable[i];
+    }
+  }
+  return NULL;
+}
+
+/*
+ * Translate an fd to a LogState.
+ */
+static LogState* fdToLogState(int fd) {
+  if (fd >= FAKE_FD_BASE && fd < FAKE_FD_BASE + MAX_OPEN_LOGS) {
+    return &openLogTable[fd - FAKE_FD_BASE];
+  }
+  return NULL;
+}
+
+/*
+ * Unregister the fake fd and free the memory it pointed to.
+ */
+static void deleteFakeFd(int fd) {
+  LogState* ls;
+
+  lock();
+
+  ls = fdToLogState(fd);
+  if (ls != NULL) {
+    memset(&openLogTable[fd - FAKE_FD_BASE], 0, sizeof(openLogTable[0]));
+  }
+
+  unlock();
+}
+
+/*
+ * Configure logging based on ANDROID_LOG_TAGS environment variable.  We
+ * need to parse a string that looks like
+ *
+ *   *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
+ *
+ * The tag (or '*' for the global level) comes first, followed by a colon
+ * and a letter indicating the minimum priority level we're expected to log.
+ * This can be used to reveal or conceal logs with specific tags.
+ *
+ * We also want to check ANDROID_PRINTF_LOG to determine how the output
+ * will look.
+ */
+static void configureInitialState(const char* pathName, LogState* logState) {
+  static const int kDevLogLen = sizeof("/dev/log/") - 1;
+
+  strncpy(logState->debugName, pathName, sizeof(logState->debugName));
+  logState->debugName[sizeof(logState->debugName) - 1] = '\0';
+
+  /* identify binary logs */
+  if (!strcmp(pathName + kDevLogLen, "events") || !strcmp(pathName + kDevLogLen, "security")) {
+    logState->isBinary = 1;
+  }
+
+  /* global min priority defaults to "info" level */
+  logState->globalMinPriority = ANDROID_LOG_INFO;
+
+  /*
+   * This is based on the the long-dead utils/Log.cpp code.
+   */
+  const char* tags = getenv("ANDROID_LOG_TAGS");
+  TRACE("Found ANDROID_LOG_TAGS='%s'\n", tags);
+  if (tags != NULL) {
+    int entry = 0;
+
+    while (*tags != '\0') {
+      char tagName[kMaxTagLen];
+      int i, minPrio;
+
+      while (isspace(*tags)) tags++;
+
+      i = 0;
+      while (*tags != '\0' && !isspace(*tags) && *tags != ':' && i < kMaxTagLen) {
+        tagName[i++] = *tags++;
+      }
+      if (i == kMaxTagLen) {
+        TRACE("ERROR: env tag too long (%d chars max)\n", kMaxTagLen - 1);
+        return;
+      }
+      tagName[i] = '\0';
+
+      /* default priority, if there's no ":" part; also zero out '*' */
+      minPrio = ANDROID_LOG_VERBOSE;
+      if (tagName[0] == '*' && tagName[1] == '\0') {
+        minPrio = ANDROID_LOG_DEBUG;
+        tagName[0] = '\0';
+      }
+
+      if (*tags == ':') {
+        tags++;
+        if (*tags >= '0' && *tags <= '9') {
+          if (*tags >= ('0' + ANDROID_LOG_SILENT))
+            minPrio = ANDROID_LOG_VERBOSE;
+          else
+            minPrio = *tags - '\0';
+        } else {
+          switch (*tags) {
+            case 'v':
+              minPrio = ANDROID_LOG_VERBOSE;
+              break;
+            case 'd':
+              minPrio = ANDROID_LOG_DEBUG;
+              break;
+            case 'i':
+              minPrio = ANDROID_LOG_INFO;
+              break;
+            case 'w':
+              minPrio = ANDROID_LOG_WARN;
+              break;
+            case 'e':
+              minPrio = ANDROID_LOG_ERROR;
+              break;
+            case 'f':
+              minPrio = ANDROID_LOG_FATAL;
+              break;
+            case 's':
+              minPrio = ANDROID_LOG_SILENT;
+              break;
+            default:
+              minPrio = ANDROID_LOG_DEFAULT;
+              break;
+          }
+        }
+
+        tags++;
+        if (*tags != '\0' && !isspace(*tags)) {
+          TRACE("ERROR: garbage in tag env; expected whitespace\n");
+          TRACE("       env='%s'\n", tags);
+          return;
+        }
+      }
+
+      if (tagName[0] == 0) {
+        logState->globalMinPriority = minPrio;
+        TRACE("+++ global min prio %d\n", logState->globalMinPriority);
+      } else {
+        logState->tagSet[entry].minPriority = minPrio;
+        strcpy(logState->tagSet[entry].tag, tagName);
+        TRACE("+++ entry %d: %s:%d\n", entry, logState->tagSet[entry].tag,
+              logState->tagSet[entry].minPriority);
+        entry++;
+      }
+    }
+  }
+
+  /*
+   * Taken from the long-dead utils/Log.cpp
+   */
+  const char* fstr = getenv("ANDROID_PRINTF_LOG");
+  LogFormat format;
+  if (fstr == NULL) {
+    format = FORMAT_BRIEF;
+  } else {
+    if (strcmp(fstr, "brief") == 0)
+      format = FORMAT_BRIEF;
+    else if (strcmp(fstr, "process") == 0)
+      format = FORMAT_PROCESS;
+    else if (strcmp(fstr, "tag") == 0)
+      format = FORMAT_PROCESS;
+    else if (strcmp(fstr, "thread") == 0)
+      format = FORMAT_PROCESS;
+    else if (strcmp(fstr, "raw") == 0)
+      format = FORMAT_PROCESS;
+    else if (strcmp(fstr, "time") == 0)
+      format = FORMAT_PROCESS;
+    else if (strcmp(fstr, "long") == 0)
+      format = FORMAT_PROCESS;
+    else
+      format = (LogFormat)atoi(fstr);  // really?!
+  }
+
+  logState->outputFormat = format;
+}
+
+/*
+ * Return a human-readable string for the priority level.  Always returns
+ * a valid string.
+ */
+static const char* getPriorityString(int priority) {
+  /* the first character of each string should be unique */
+  static const char* priorityStrings[] = {"Verbose", "Debug", "Info", "Warn", "Error", "Assert"};
+  int idx;
+
+  idx = (int)priority - (int)ANDROID_LOG_VERBOSE;
+  if (idx < 0 || idx >= (int)(sizeof(priorityStrings) / sizeof(priorityStrings[0])))
+    return "?unknown?";
+  return priorityStrings[idx];
+}
+
+#if defined(_WIN32)
+/*
+ * WIN32 does not have writev().
+ * Make up something to replace it.
+ */
+static ssize_t fake_writev(int fd, const struct iovec* iov, int iovcnt) {
+  ssize_t result = 0;
+  const struct iovec* end = iov + iovcnt;
+  for (; iov < end; iov++) {
+    ssize_t w = write(fd, iov->iov_base, iov->iov_len);
+    if (w != (ssize_t)iov->iov_len) {
+      if (w < 0) return w;
+      return result + w;
+    }
+    result += w;
+  }
+  return result;
+}
+
+#define writev fake_writev
+#endif
+
+/*
+ * Write a filtered log message to stderr.
+ *
+ * Log format parsing taken from the long-dead utils/Log.cpp.
+ */
+static void showLog(LogState* state, int logPrio, const char* tag, const char* msg) {
+#if !defined(_WIN32)
+  struct tm tmBuf;
+#endif
+  struct tm* ptm;
+  char timeBuf[32];
+  char prefixBuf[128], suffixBuf[128];
+  char priChar;
+  time_t when;
+#if !defined(_WIN32)
+  pid_t pid, tid;
+#else
+  uint32_t pid, tid;
+#endif
+
+  TRACE("LOG %d: %s %s", logPrio, tag, msg);
+
+  priChar = getPriorityString(logPrio)[0];
+  when = time(NULL);
+  pid = tid = getpid();  // find gettid()?
+
+/*
+ * Get the current date/time in pretty form
+ *
+ * It's often useful when examining a log with "less" to jump to
+ * a specific point in the file by searching for the date/time stamp.
+ * For this reason it's very annoying to have regexp meta characters
+ * in the time stamp.  Don't use forward slashes, parenthesis,
+ * brackets, asterisks, or other special chars here.
+ */
+#if !defined(_WIN32)
+  ptm = localtime_r(&when, &tmBuf);
+#else
+  ptm = localtime(&when);
+#endif
+  // strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
+  strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
+
+  /*
+   * Construct a buffer containing the log header and log message.
+   */
+  size_t prefixLen, suffixLen;
+
+  switch (state->outputFormat) {
+    case FORMAT_TAG:
+      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c/%-8s: ", priChar, tag);
+      strcpy(suffixBuf, "\n");
+      suffixLen = 1;
+      break;
+    case FORMAT_PROCESS:
+      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c(%5d) ", priChar, pid);
+      suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), "  (%s)\n", tag);
+      break;
+    case FORMAT_THREAD:
+      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c(%5d:%5d) ", priChar, pid, tid);
+      strcpy(suffixBuf, "\n");
+      suffixLen = 1;
+      break;
+    case FORMAT_RAW:
+      prefixBuf[0] = 0;
+      prefixLen = 0;
+      strcpy(suffixBuf, "\n");
+      suffixLen = 1;
+      break;
+    case FORMAT_TIME:
+      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%s %-8s\n\t", timeBuf, tag);
+      strcpy(suffixBuf, "\n");
+      suffixLen = 1;
+      break;
+    case FORMAT_THREADTIME:
+      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%s %5d %5d %c %-8s \n\t", timeBuf, pid,
+                           tid, priChar, tag);
+      strcpy(suffixBuf, "\n");
+      suffixLen = 1;
+      break;
+    case FORMAT_LONG:
+      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "[ %s %5d:%5d %c/%-8s ]\n", timeBuf, pid,
+                           tid, priChar, tag);
+      strcpy(suffixBuf, "\n\n");
+      suffixLen = 2;
+      break;
+    default:
+      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c/%-8s(%5d): ", priChar, tag, pid);
+      strcpy(suffixBuf, "\n");
+      suffixLen = 1;
+      break;
+  }
+
+  /*
+   * Figure out how many lines there will be.
+   */
+  const char* end = msg + strlen(msg);
+  size_t numLines = 0;
+  const char* p = msg;
+  while (p < end) {
+    if (*p++ == '\n') numLines++;
+  }
+  if (p > msg && *(p - 1) != '\n') {
+    numLines++;
+  }
+
+  /*
+   * Create an array of iovecs large enough to write all of
+   * the lines with a prefix and a suffix.
+   */
+  const size_t INLINE_VECS = 64;
+  const size_t MAX_LINES = ((size_t)~0) / (3 * sizeof(struct iovec*));
+  struct iovec stackVec[INLINE_VECS];
+  struct iovec* vec = stackVec;
+  size_t numVecs;
+
+  if (numLines > MAX_LINES) numLines = MAX_LINES;
+
+  numVecs = numLines * 3;  // 3 iovecs per line.
+  if (numVecs > INLINE_VECS) {
+    vec = (struct iovec*)malloc(sizeof(struct iovec) * numVecs);
+    if (vec == NULL) {
+      msg = "LOG: write failed, no memory";
+      numVecs = INLINE_VECS;
+      numLines = numVecs / 3;
+      vec = stackVec;
+    }
+  }
+
+  /*
+   * Fill in the iovec pointers.
+   */
+  p = msg;
+  struct iovec* v = vec;
+  int totalLen = 0;
+  while (numLines > 0 && p < end) {
+    if (prefixLen > 0) {
+      v->iov_base = prefixBuf;
+      v->iov_len = prefixLen;
+      totalLen += prefixLen;
+      v++;
+    }
+    const char* start = p;
+    while (p < end && *p != '\n') {
+      p++;
+    }
+    if ((p - start) > 0) {
+      v->iov_base = (void*)start;
+      v->iov_len = p - start;
+      totalLen += p - start;
+      v++;
+    }
+    if (*p == '\n') p++;
+    if (suffixLen > 0) {
+      v->iov_base = suffixBuf;
+      v->iov_len = suffixLen;
+      totalLen += suffixLen;
+      v++;
+    }
+    numLines -= 1;
+  }
+
+  /*
+   * Write the entire message to the log file with a single writev() call.
+   * We need to use this rather than a collection of printf()s on a FILE*
+   * because of multi-threading and multi-process issues.
+   *
+   * If the file was not opened with O_APPEND, this will produce interleaved
+   * output when called on the same file from multiple processes.
+   *
+   * If the file descriptor is actually a network socket, the writev()
+   * call may return with a partial write.  Putting the writev() call in
+   * a loop can result in interleaved data.  This can be alleviated
+   * somewhat by wrapping the writev call in the Mutex.
+   */
+
+  for (;;) {
+    int cc = writev(fileno(stderr), vec, v - vec);
+
+    if (cc == totalLen) break;
+
+    if (cc < 0) {
+      if (errno == EINTR) continue;
+
+      /* can't really log the failure; for now, throw out a stderr */
+      fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
+      break;
+    } else {
+      /* shouldn't happen when writing to file or tty */
+      fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", cc, totalLen);
+      break;
+    }
+  }
+
+  /* if we allocated storage for the iovecs, free it */
+  if (vec != stackVec) free(vec);
+}
+
+/*
+ * Receive a log message.  We happen to know that "vector" has three parts:
+ *
+ *  priority (1 byte)
+ *  tag (N bytes -- null-terminated ASCII string)
+ *  message (N bytes -- null-terminated ASCII string)
+ */
+ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count) {
+  LogState* state;
+
+  /* Make sure that no-one frees the LogState while we're using it.
+   * Also guarantees that only one thread is in showLog() at a given
+   * time (if it matters).
+   */
+  lock();
+
+  state = fdToLogState(fd);
+  if (state == NULL) {
+    errno = EBADF;
+    unlock();
+    return -1;
+  }
+
+  if (state->isBinary) {
+    TRACE("%s: ignoring binary log\n", state->debugName);
+    unlock();
+    int len = 0;
+    for (int i = 0; i < count; ++i) {
+      len += vector[i].iov_len;
+    }
+    return len;
+  }
+
+  if (count != 3) {
+    TRACE("%s: writevLog with count=%d not expected\n", state->debugName, count);
+    unlock();
+    return -1;
+  }
+
+  /* pull out the three fields */
+  int logPrio = *(const char*)vector[0].iov_base;
+  const char* tag = (const char*)vector[1].iov_base;
+  const char* msg = (const char*)vector[2].iov_base;
+
+  /* see if this log tag is configured */
+  int i;
+  int minPrio = state->globalMinPriority;
+  for (i = 0; i < kTagSetSize; i++) {
+    if (state->tagSet[i].minPriority == ANDROID_LOG_UNKNOWN)
+      break; /* reached end of configured values */
+
+    if (strcmp(state->tagSet[i].tag, tag) == 0) {
+      minPrio = state->tagSet[i].minPriority;
+      break;
+    }
+  }
+
+  if (logPrio >= minPrio) {
+    showLog(state, logPrio, tag, msg);
+  }
+
+  unlock();
+  int len = 0;
+  for (i = 0; i < count; ++i) {
+    len += vector[i].iov_len;
+  }
+  return len;
+}
+
+/*
+ * Free up our state and close the fake descriptor.
+ *
+ * The logger API has no means or need to 'stop' or 'close' using the logs,
+ * and as such, there is no way for that 'stop' or 'close' to translate into
+ * a close operation to the fake log handler. fakeLogClose is provided for
+ * completeness only.
+ *
+ * We have no intention of adding a log close operation as it would complicate
+ * every user of the logging API with no gain since the only valid place to
+ * call is in the exit handler. Logging can continue in the exit handler to
+ * help debug HOST tools ...
+ */
+int fakeLogClose(int fd) {
+  deleteFakeFd(fd);
+  return 0;
+}
+
+/*
+ * Open a log output device and return a fake fd.
+ */
+int fakeLogOpen(const char* pathName) {
+  LogState* logState;
+  int fd = -1;
+
+  lock();
+
+  logState = createLogState();
+  if (logState != NULL) {
+    configureInitialState(pathName, logState);
+    fd = logState->fakeFd;
+  } else {
+    errno = ENFILE;
+  }
+
+  unlock();
+
+  return fd;
+}
+
+ssize_t __send_log_msg(char*, size_t) {
+  return -ENODEV;
+}
+
+int __android_log_is_loggable(int prio, const char*, int def) {
+  int logLevel = def;
+  return logLevel >= 0 && prio >= logLevel;
+}
+
+int __android_log_is_loggable_len(int prio, const char*, size_t, int def) {
+  int logLevel = def;
+  return logLevel >= 0 && prio >= logLevel;
+}
+
+int __android_log_is_debuggable() {
+  return 1;
+}
diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h
index 7b0e745..ce54db2 100644
--- a/liblog/fake_log_device.h
+++ b/liblog/fake_log_device.h
@@ -14,18 +14,24 @@
  * limitations under the License.
  */
 
-#ifndef _LIBLOG_FAKE_LOG_DEVICE_H
-#define _LIBLOG_FAKE_LOG_DEVICE_H
+#pragma once
 
 #include <sys/types.h>
 
 #include "log_portability.h"
+#include "uio.h"
 
 struct iovec;
 
-LIBLOG_HIDDEN int fakeLogOpen(const char* pathName);
-LIBLOG_HIDDEN int fakeLogClose(int fd);
-LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd, const struct iovec* vector,
-                                    int count);
+__BEGIN_DECLS
 
-#endif  // _LIBLOG_FAKE_LOG_DEVICE_H
+int fakeLogOpen(const char* pathName);
+int fakeLogClose(int fd);
+ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count);
+
+ssize_t __send_log_msg(char*, size_t);
+int __android_log_is_loggable(int prio, const char*, int def);
+int __android_log_is_loggable_len(int prio, const char*, size_t, int def);
+int __android_log_is_debuggable();
+
+__END_DECLS
diff --git a/liblog/fake_writer.c b/liblog/fake_writer.c
deleted file mode 100644
index 403dc72..0000000
--- a/liblog/fake_writer.c
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2007-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 <fcntl.h>
-#include <unistd.h>
-
-#include <log/log.h>
-
-#include "config_write.h"
-#include "fake_log_device.h"
-#include "log_portability.h"
-#include "logger.h"
-
-static int fakeOpen();
-static void fakeClose();
-static int fakeWrite(log_id_t log_id, struct timespec* ts, struct iovec* vec,
-                     size_t nr);
-
-static int logFds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1, -1 };
-
-LIBLOG_HIDDEN struct android_log_transport_write fakeLoggerWrite = {
-  .node = { &fakeLoggerWrite.node, &fakeLoggerWrite.node },
-  .context.priv = &logFds,
-  .name = "fake",
-  .available = NULL,
-  .open = fakeOpen,
-  .close = fakeClose,
-  .write = fakeWrite,
-};
-
-static int fakeOpen() {
-  int i;
-
-  for (i = 0; i < LOG_ID_MAX; i++) {
-    /*
-     * Known maximum size string, plus an 8 character margin to deal with
-     * possible independent changes to android_log_id_to_name().
-     */
-    char buf[sizeof("/dev/log_security") + 8];
-    if (logFds[i] >= 0) {
-      continue;
-    }
-    snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
-    logFds[i] = fakeLogOpen(buf);
-    if (logFds[i] < 0) {
-      fprintf(stderr, "fakeLogOpen(%s) failed\n", buf);
-    }
-  }
-  return 0;
-}
-
-static void fakeClose() {
-  int i;
-
-  for (i = 0; i < LOG_ID_MAX; i++) {
-    fakeLogClose(logFds[i]);
-    logFds[i] = -1;
-  }
-}
-
-static int fakeWrite(log_id_t log_id, struct timespec* ts __unused,
-                     struct iovec* vec, size_t nr) {
-  ssize_t ret;
-  size_t i;
-  int logFd, len;
-
-  if (/*(int)log_id >= 0 &&*/ (int)log_id >= (int)LOG_ID_MAX) {
-    return -EINVAL;
-  }
-
-  len = 0;
-  for (i = 0; i < nr; ++i) {
-    len += vec[i].iov_len;
-  }
-
-  if (len > LOGGER_ENTRY_MAX_PAYLOAD) {
-    len = LOGGER_ENTRY_MAX_PAYLOAD;
-  }
-
-  logFd = logFds[(int)log_id];
-  ret = TEMP_FAILURE_RETRY(fakeLogWritev(logFd, vec, nr));
-  if (ret < 0) {
-    ret = -errno;
-  } else if (ret > len) {
-    ret = len;
-  }
-
-  return ret;
-}
diff --git a/liblog/fake_writer.cpp b/liblog/fake_writer.cpp
new file mode 100644
index 0000000..c0b0e69
--- /dev/null
+++ b/liblog/fake_writer.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2007-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 <fcntl.h>
+#include <unistd.h>
+
+#include <log/log.h>
+
+#include "config_write.h"
+#include "fake_log_device.h"
+#include "log_portability.h"
+#include "logger.h"
+
+static int fakeOpen();
+static void fakeClose();
+static int fakeWrite(log_id_t log_id, struct timespec* ts, struct iovec* vec, size_t nr);
+
+static int logFds[(int)LOG_ID_MAX] = {-1, -1, -1, -1, -1, -1};
+
+struct android_log_transport_write fakeLoggerWrite = {
+    .node = {&fakeLoggerWrite.node, &fakeLoggerWrite.node},
+    .context.priv = &logFds,
+    .name = "fake",
+    .available = NULL,
+    .open = fakeOpen,
+    .close = fakeClose,
+    .write = fakeWrite,
+};
+
+static int fakeOpen() {
+  int i;
+
+  for (i = 0; i < LOG_ID_MAX; i++) {
+    /*
+     * Known maximum size string, plus an 8 character margin to deal with
+     * possible independent changes to android_log_id_to_name().
+     */
+    char buf[sizeof("/dev/log_security") + 8];
+    if (logFds[i] >= 0) {
+      continue;
+    }
+    snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(static_cast<log_id_t>(i)));
+    logFds[i] = fakeLogOpen(buf);
+    if (logFds[i] < 0) {
+      fprintf(stderr, "fakeLogOpen(%s) failed\n", buf);
+    }
+  }
+  return 0;
+}
+
+static void fakeClose() {
+  int i;
+
+  for (i = 0; i < LOG_ID_MAX; i++) {
+    fakeLogClose(logFds[i]);
+    logFds[i] = -1;
+  }
+}
+
+static int fakeWrite(log_id_t log_id, struct timespec*, struct iovec* vec, size_t nr) {
+  ssize_t ret;
+  size_t i;
+  int logFd, len;
+
+  if (/*(int)log_id >= 0 &&*/ (int)log_id >= (int)LOG_ID_MAX) {
+    return -EINVAL;
+  }
+
+  len = 0;
+  for (i = 0; i < nr; ++i) {
+    len += vec[i].iov_len;
+  }
+
+  if (len > LOGGER_ENTRY_MAX_PAYLOAD) {
+    len = LOGGER_ENTRY_MAX_PAYLOAD;
+  }
+
+  logFd = logFds[(int)log_id];
+  ret = TEMP_FAILURE_RETRY(fakeLogWritev(logFd, vec, nr));
+  if (ret < 0) {
+    ret = -errno;
+  } else if (ret > len) {
+    ret = len;
+  }
+
+  return ret;
+}
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index 28c87e4..935590d 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -14,24 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef _ANDROID_LOG_H
-#define _ANDROID_LOG_H
+#pragma once
 
-/******************************************************************
- *
- * IMPORTANT NOTICE:
- *
- *   This file is part of Android's set of stable system headers
- *   exposed by the Android NDK (Native Development Kit) since
- *   platform release 1.5
- *
- *   Third-party source AND binary code relies on the definitions
- *   here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
- *
- *   - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
- *   - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
- *   - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
- *   - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
+/**
+ * @addtogroup Logging
+ * @{
  */
 
 /**
@@ -110,16 +97,8 @@
  */
 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
     ;
 
 /**
@@ -128,16 +107,8 @@
  */
 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
     ;
 
 /**
@@ -151,49 +122,66 @@
  *
  * 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.
+ * `&lt;assert.h&gt;` 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)))
-#endif
-#endif
     ;
 
 #ifndef log_id_t_defined
 #define log_id_t_defined
+/**
+ * Identifies a specific log buffer for __android_log_buf_write()
+ * and __android_log_buf_print().
+ */
 typedef enum log_id {
   LOG_ID_MIN = 0,
 
+  /** The main log buffer. This is the only log buffer available to apps. */
   LOG_ID_MAIN = 0,
+  /** The radio log buffer. */
   LOG_ID_RADIO = 1,
+  /** The event log buffer. */
   LOG_ID_EVENTS = 2,
+  /** The system log buffer. */
   LOG_ID_SYSTEM = 3,
+  /** The crash log buffer. */
   LOG_ID_CRASH = 4,
+  /** The statistics log buffer. */
   LOG_ID_STATS = 5,
+  /** The security log buffer. */
   LOG_ID_SECURITY = 6,
-  LOG_ID_KERNEL = 7, /* place last, third-parties can not use it */
+  /** The kernel log buffer. */
+  LOG_ID_KERNEL = 7,
 
   LOG_ID_MAX
 } log_id_t;
 #endif
 
-/*
- * Send a simple string to the log.
+/**
+ * Writes the constant string `text` to the log buffer `id`,
+ * with priority `prio` and tag `tag`.
+ *
+ * Apps should use __android_log_write() instead.
  */
 int __android_log_buf_write(int bufID, int prio, const char* tag,
                             const char* text);
+
+/**
+ * Writes a formatted string to log buffer `id`,
+ * 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).
+ *
+ * Apps should use __android_log_print() instead.
+ */
 int __android_log_buf_print(int bufID, int prio, const char* tag,
                             const char* fmt, ...)
 #if defined(__GNUC__)
@@ -205,4 +193,4 @@
 }
 #endif
 
-#endif /* _ANDROID_LOG_H */
+/** @} */
diff --git a/liblog/include/log/event_tag_map.h b/liblog/include/log/event_tag_map.h
index 8dd9157..2687b3a 100644
--- a/liblog/include/log/event_tag_map.h
+++ b/liblog/include/log/event_tag_map.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_CUTILS_EVENTTAGMAP_H
-#define _LIBS_CUTILS_EVENTTAGMAP_H
+#pragma once
 
 #ifdef __cplusplus
 extern "C" {
@@ -69,5 +68,3 @@
 #ifdef __cplusplus
 }
 #endif
-
-#endif /*_LIBS_CUTILS_EVENTTAGMAP_H*/
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index 3813e6e..5928649 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_LOG_LOG_H
-#define _LIBS_LOG_LOG_H
+#pragma once
 
 /* Too many in the ecosystem assume these are included */
 #if !defined(_WIN32)
@@ -35,7 +34,6 @@
 #include <log/log_safetynet.h>
 #include <log/log_system.h>
 #include <log/log_time.h>
-#include <log/uio.h> /* helper to define iovec for portability */
 
 #ifdef __cplusplus
 extern "C" {
@@ -152,35 +150,12 @@
 
 #ifdef __linux__
 
-#ifndef __ANDROID_USE_LIBLOG_CLOCK_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_CLOCK_INTERFACE 1
-#elif __ANDROID_API__ > 22 /* > Lollipop */
-#define __ANDROID_USE_LIBLOG_CLOCK_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_CLOCK_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_CLOCK_INTERFACE
 clockid_t android_log_clockid(void);
-#endif
 
 #endif /* __linux__ */
 
 /* --------------------------------------------------------------------- */
 
-#ifndef __ANDROID_USE_LIBLOG_CLOSE_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_CLOSE_INTERFACE 1
-#elif __ANDROID_API__ > 18 /* > JellyBean */
-#define __ANDROID_USE_LIBLOG_CLOSE_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_CLOSE_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_CLOSE_INTERFACE
 /*
  * Release any logger resources (a new log write will immediately re-acquire)
  *
@@ -188,54 +163,6 @@
  * all O_CLOEXEC so wil self clean on exec().
  */
 void __android_log_close(void);
-#endif
-
-#ifndef __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE 1
-#elif __ANDROID_API__ > 25 /* > OC */
-#define __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE
-
-/*
- * if last is NULL, caller _must_ provide a consistent value for seconds.
- *
- * Return -1 if we can not acquire a lock, which below will permit the logging,
- * error on allowing a log message through.
- */
-int __android_log_ratelimit(time_t seconds, time_t* last);
-
-/*
- * Usage:
- *
- *   // Global default and state
- *   IF_ALOG_RATELIMIT() {
- *      ALOG*(...);
- *   }
- *
- *   // local state, 10 seconds ratelimit
- *   static time_t local_state;
- *   IF_ALOG_RATELIMIT_LOCAL(10, &local_state) {
- *     ALOG*(...);
- *   }
- */
-
-#define IF_ALOG_RATELIMIT() if (__android_log_ratelimit(0, NULL) > 0)
-#define IF_ALOG_RATELIMIT_LOCAL(seconds, state) \
-  if (__android_log_ratelimit(seconds, state) > 0)
-
-#else
-
-/* No ratelimiting as API unsupported */
-#define IF_ALOG_RATELIMIT() if (1)
-#define IF_ALOG_RATELIMIT_LOCAL(...) if (1)
-
-#endif
 
 #if defined(__clang__)
 #pragma clang diagnostic pop
@@ -244,5 +171,3 @@
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* _LIBS_LOG_LOG_H */
diff --git a/liblog/include/log/log_event_list.h b/liblog/include/log/log_event_list.h
index bb1ce34..636d417 100644
--- a/liblog/include/log/log_event_list.h
+++ b/liblog/include/log/log_event_list.h
@@ -14,16 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_LOG_EVENT_LIST_H
-#define _LIBS_LOG_EVENT_LIST_H
+#pragma once
 
 #include <errno.h>
 #include <stdint.h>
 
-#if (defined(__cplusplus) && defined(_USING_LIBCXX))
-extern "C++" {
+#ifdef __cplusplus
 #include <string>
-}
 #endif
 
 #include <log/log.h>
@@ -32,18 +29,6 @@
 extern "C" {
 #endif
 
-#ifndef __ANDROID_USE_LIBLOG_EVENT_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_EVENT_INTERFACE 1
-#elif __ANDROID_API__ > 23 /* > Marshmallow */
-#define __ANDROID_USE_LIBLOG_EVENT_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_EVENT_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_EVENT_INTERFACE
-
 /* For manipulating lists of events. */
 
 #define ANDROID_MAX_LIST_NEST_DEPTH 8
@@ -108,6 +93,13 @@
 android_log_list_element android_log_read_next(android_log_context ctx);
 android_log_list_element android_log_peek_next(android_log_context ctx);
 
+/* Reset writer context */
+int android_log_reset(android_log_context ctx);
+
+/* Reset reader context */
+int android_log_parser_reset(android_log_context ctx,
+                             const char* msg, size_t len);
+
 /* Finished with reader or writer context */
 int android_log_destroy(android_log_context* ctx);
 
@@ -117,8 +109,6 @@
 /* android_log_list C++ helpers */
 extern "C++" {
 class android_log_event_list {
-  friend class __android_log_event_list;
-
  private:
   android_log_context ctx;
   int ret;
@@ -130,10 +120,6 @@
   explicit android_log_event_list(int tag) : ret(0) {
     ctx = create_android_logger(static_cast<uint32_t>(tag));
   }
-  explicit android_log_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));
-  }
   ~android_log_event_list() {
     android_log_destroy(&ctx);
   }
@@ -201,14 +187,12 @@
     return *this;
   }
 
-#if defined(_USING_LIBCXX)
   android_log_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
 
   android_log_event_list& operator<<(float value) {
     int retval = android_log_write_float32(ctx, value);
@@ -262,7 +246,6 @@
     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());
@@ -276,7 +259,6 @@
     if (retval < 0) ret = retval;
     return ret;
   }
-#endif
 
   bool AppendFloat(float value) {
     int retval = android_log_write_float32(ctx, value);
@@ -295,22 +277,11 @@
     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
 
-#endif /* __ANDROID_USE_LIBLOG_EVENT_INTERFACE */
-
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* _LIBS_LOG_EVENT_LIST_H */
diff --git a/liblog/include/log/log_id.h b/liblog/include/log/log_id.h
index c44f5a2..c052a50 100644
--- a/liblog/include/log/log_id.h
+++ b/liblog/include/log/log_id.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_LOG_LOG_ID_H
-#define _LIBS_LOG_LOG_ID_H
+#pragma once
 
 #ifdef __cplusplus
 extern "C" {
@@ -62,5 +61,3 @@
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* _LIBS_LOG_LOG_ID_H */
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h
index 21fc7cc..64791c2 100644
--- a/liblog/include/log/log_main.h
+++ b/liblog/include/log/log_main.h
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_LOG_LOG_MAIN_H
-#define _LIBS_LOG_LOG_MAIN_H
+#pragma once
 
 #include <stdbool.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
 
 #include <android/log.h>
-#include <sys/cdefs.h>
 
 __BEGIN_DECLS
 
@@ -53,6 +53,28 @@
 #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__.
+ * Use contexpr function in C++ mode, so these macros can be used
+ * in other constexpr functions without warning.
+ */
+#ifdef __clang_analyzer__
+#ifdef __cplusplus
+extern "C++" {
+template <typename... Ts>
+constexpr int __fake_use_va_args(Ts...) {
+  return 0;
+}
+}
+#else
+extern int __fake_use_va_args(int, ...);
+#endif /* __cplusplus */
+#define __FAKE_USE_VA_ARGS(...) ((void)__fake_use_va_args(0, ##__VA_ARGS__))
+#else
+#define __FAKE_USE_VA_ARGS(...) ((void)(0))
+#endif /* __clang_analyzer__ */
+
 #ifndef __predict_false
 #define __predict_false(exp) __builtin_expect((exp) != 0, 0)
 #endif
@@ -112,7 +134,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
@@ -128,10 +150,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
@@ -175,11 +197,12 @@
 #ifndef ALOGV
 #define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
 #if LOG_NDEBUG
-#define ALOGV(...)          \
-  do {                      \
-    if (false) {            \
-      __ALOGV(__VA_ARGS__); \
-    }                       \
+#define ALOGV(...)                   \
+  do {                               \
+    __FAKE_USE_VA_ARGS(__VA_ARGS__); \
+    if (false) {                     \
+      __ALOGV(__VA_ARGS__);          \
+    }                                \
   } while (false)
 #else
 #define ALOGV(...) __ALOGV(__VA_ARGS__)
@@ -188,11 +211,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
 
@@ -206,7 +229,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
 
 /*
@@ -219,7 +242,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
 
 /*
@@ -232,7 +255,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
 
 /*
@@ -245,7 +268,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
 
 /* --------------------------------------------------------------------- */
@@ -326,20 +349,6 @@
  *        over Android.
  */
 
-#ifndef __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE 2
-#elif __ANDROID_API__ > 24 /* > Nougat */
-#define __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE 2
-#elif __ANDROID_API__ > 22 /* > Lollipop */
-#define __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE
-
 /*
  * Use the per-tag properties "log.tag.<tagname>" to generate a runtime
  * result of non-zero to expose a log. prio is ANDROID_LOG_VERBOSE to
@@ -347,12 +356,7 @@
  * any other value.
  */
 int __android_log_is_loggable(int prio, const char* tag, int default_prio);
-
-#if __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE > 1
-#include <sys/types.h>
-
-int __android_log_is_loggable_len(int prio, const char* tag, size_t len,
-                                  int default_prio);
+int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio);
 
 #if LOG_NDEBUG /* Production */
 #define android_testLog(prio, tag)                                           \
@@ -364,28 +368,8 @@
                                  ANDROID_LOG_VERBOSE) != 0)
 #endif
 
-#else
-
-#if LOG_NDEBUG /* Production */
-#define android_testLog(prio, tag) \
-  (__android_log_is_loggable(prio, tag, ANDROID_LOG_DEBUG) != 0)
-#else
-#define android_testLog(prio, tag) \
-  (__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE) != 0)
-#endif
-
-#endif
-
-#else /* __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE */
-
-#define android_testLog(prio, tag) (1)
-
-#endif /* !__ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE */
-
 #if defined(__clang__)
 #pragma clang diagnostic pop
 #endif
 
 __END_DECLS
-
-#endif /* _LIBS_LOG_LOG_MAIN_H */
diff --git a/liblog/include/log/log_properties.h b/liblog/include/log/log_properties.h
index 7d398a6..3a8af6d 100644
--- a/liblog/include/log/log_properties.h
+++ b/liblog/include/log/log_properties.h
@@ -7,29 +7,14 @@
 ** General Public License.
 */
 
-#ifndef _LIBS_LOG_PROPERTIES_H
-#define _LIBS_LOG_PROPERTIES_H
+#pragma once
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-#ifndef __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE 1
-#elif __ANDROID_API__ > 24 /* > Nougat */
-#define __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE
 int __android_log_is_debuggable();
-#endif
 
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* _LIBS_LOG_PROPERTIES_H */
diff --git a/liblog/include/log/log_radio.h b/liblog/include/log/log_radio.h
index bd629fe..8b8a362 100644
--- a/liblog/include/log/log_radio.h
+++ b/liblog/include/log/log_radio.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_LOG_LOG_RADIO_H
-#define _LIBS_LOG_LOG_RADIO_H
+#pragma once
 
 #include <android/log.h>
 #include <log/log_id.h>
@@ -140,5 +139,3 @@
                                         LOG_TAG, __VA_ARGS__))           \
        : (void)0)
 #endif
-
-#endif /* _LIBS_LOG_LOG_RADIO_H */
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index d118563..fdef306 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_LOG_LOG_READ_H
-#define _LIBS_LOG_LOG_READ_H
+#pragma once
+
+#include <sys/types.h>
 
 /* deal with possible sys/cdefs.h conflict with fcntl.h */
 #ifdef __unused
@@ -47,6 +48,8 @@
  * access to raw information, or parsing is an issue.
  */
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wzero-length-array"
 /*
  * The userspace structure for version 1 of the logger_entry ABI.
  */
@@ -59,9 +62,7 @@
   int32_t tid;    /* generating process's tid */
   int32_t sec;    /* seconds since Epoch */
   int32_t nsec;   /* nanoseconds */
-#ifndef __cplusplus
   char msg[0]; /* the entry's payload */
-#endif
 };
 #endif
 
@@ -78,9 +79,7 @@
   int32_t sec;       /* seconds since Epoch */
   int32_t nsec;      /* nanoseconds */
   uint32_t euid;     /* effective UID of logger */
-#ifndef __cplusplus
   char msg[0]; /* the entry's payload */
-#endif
 } __attribute__((__packed__));
 #endif
 
@@ -97,9 +96,7 @@
   int32_t sec;       /* seconds since Epoch */
   int32_t nsec;      /* nanoseconds */
   uint32_t lid;      /* log id of the payload */
-#ifndef __cplusplus
   char msg[0]; /* the entry's payload */
-#endif
 } __attribute__((__packed__));
 #endif
 
@@ -117,11 +114,10 @@
   uint32_t nsec;     /* nanoseconds */
   uint32_t lid;      /* log id of the payload, bottom 4 bits currently */
   uint32_t uid;      /* generating process's uid */
-#ifndef __cplusplus
   char msg[0]; /* the entry's payload */
-#endif
 };
 #endif
+#pragma clang diagnostic pop
 
 /*
  * The maximum size of the log entry payload that can be
@@ -184,7 +180,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;
   }
@@ -197,22 +193,6 @@
 };
 #endif
 
-#ifndef __ANDROID_USE_LIBLOG_READER_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_READER_INTERFACE 3
-#elif __ANDROID_API__ > 23 /* > Marshmallow */
-#define __ANDROID_USE_LIBLOG_READER_INTERFACE 3
-#elif __ANDROID_API__ > 22 /* > Lollipop */
-#define __ANDROID_USE_LIBLOG_READER_INTERFACE 2
-#elif __ANDROID_API__ > 19 /* > KitKat */
-#define __ANDROID_USE_LIBLOG_READER_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_READER_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_READER_INTERFACE
-
 struct logger;
 
 log_id_t android_logger_get_id(struct logger* logger);
@@ -225,14 +205,12 @@
 
 struct logger_list;
 
-#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 1
 ssize_t android_logger_get_statistics(struct logger_list* logger_list,
                                       char* buf, size_t len);
 ssize_t android_logger_get_prune_list(struct logger_list* logger_list,
                                       char* buf, size_t len);
 int android_logger_set_prune_list(struct logger_list* logger_list, char* buf,
                                   size_t len);
-#endif
 
 #define ANDROID_LOG_RDONLY O_RDONLY
 #define ANDROID_LOG_WRONLY O_WRONLY
@@ -243,13 +221,9 @@
 #else
 #define ANDROID_LOG_NONBLOCK O_NONBLOCK
 #endif
-#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 2
 #define ANDROID_LOG_WRAP 0x40000000 /* Block until buffer about to wrap */
 #define ANDROID_LOG_WRAP_DEFAULT_TIMEOUT 7200 /* 2 hour default */
-#endif
-#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 1
 #define ANDROID_LOG_PSTORE 0x80000000
-#endif
 
 struct logger_list* android_logger_list_alloc(int mode, unsigned int tail,
                                               pid_t pid);
@@ -268,10 +242,6 @@
                                              unsigned int tail, pid_t pid);
 #define android_logger_list_close android_logger_list_free
 
-#endif /* __ANDROID_USE_LIBLOG_READER_INTERFACE */
-
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* _LIBS_LOG_LOG_H */
diff --git a/liblog/include/log/log_safetynet.h b/liblog/include/log/log_safetynet.h
index 07e8c8a..d3e9b19 100644
--- a/liblog/include/log/log_safetynet.h
+++ b/liblog/include/log/log_safetynet.h
@@ -7,8 +7,7 @@
 ** General Public License.
 */
 
-#ifndef _LIBS_LOG_SAFETYNET_H
-#define _LIBS_LOG_SAFETYNET_H
+#pragma once
 
 #include <stdint.h>
 
@@ -16,18 +15,6 @@
 extern "C" {
 #endif
 
-#ifndef _ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 1
-#elif __ANDROID_API__ > 22 /* > Lollipop */
-#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE
-
 #define android_errorWriteLog(tag, subTag) \
   __android_log_error_write(tag, subTag, -1, NULL, 0)
 
@@ -37,10 +24,6 @@
 int __android_log_error_write(int tag, const char* subTag, int32_t uid,
                               const char* data, uint32_t dataLen);
 
-#endif /* __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE */
-
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* _LIBS_LOG_SAFETYNET_H */
diff --git a/liblog/include/log/log_system.h b/liblog/include/log/log_system.h
index 3b5ae22..eaec741 100644
--- a/liblog/include/log/log_system.h
+++ b/liblog/include/log/log_system.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_LOG_LOG_SYSTEM_H
-#define _LIBS_LOG_LOG_SYSTEM_H
+#pragma once
 
 #include <android/log.h>
 #include <log/log_id.h>
@@ -138,5 +137,3 @@
                                         LOG_TAG, __VA_ARGS__))            \
        : (void)0)
 #endif
-
-#endif /* _LIBS_LOG_LOG_SYSTEM_H */
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
index 309f5d1..09c9910 100644
--- a/liblog/include/log/log_time.h
+++ b/liblog/include/log/log_time.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_LOG_LOG_TIME_H
-#define _LIBS_LOG_LOG_TIME_H
+#pragma once
 
 #include <stdint.h>
 #include <time.h>
@@ -34,6 +33,8 @@
 
 #ifdef __cplusplus
 
+extern "C" {
+
 /*
  * NB: we did NOT define a copy constructor. This will result in structure
  * no longer being compatible with pass-by-value which is desired
@@ -41,25 +42,19 @@
  */
 struct log_time {
  public:
-  uint32_t tv_sec; /* good to Feb 5 2106 */
-  uint32_t tv_nsec;
+  uint32_t tv_sec = 0; /* good to Feb 5 2106 */
+  uint32_t tv_nsec = 0;
 
   static const uint32_t tv_sec_max = 0xFFFFFFFFUL;
   static const uint32_t tv_nsec_max = 999999999UL;
+  static const timespec EPOCH;
 
-  log_time(const timespec& T)
-      : tv_sec(static_cast<uint32_t>(T.tv_sec)),
-        tv_nsec(static_cast<uint32_t>(T.tv_nsec)) {
-  }
+  log_time() {}
+  explicit log_time(const timespec& T)
+      : tv_sec(static_cast<uint32_t>(T.tv_sec)), tv_nsec(static_cast<uint32_t>(T.tv_nsec)) {}
   explicit log_time(uint32_t sec, uint32_t nsec = 0)
       : tv_sec(sec), tv_nsec(nsec) {
   }
-#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
-#define __struct_log_time_private_defined
-  static const timespec EPOCH;
-#endif
-  log_time() {
-  }
 #ifdef __linux__
   explicit log_time(clockid_t id) {
     timespec T;
@@ -103,7 +98,6 @@
     return !(*this > T);
   }
 
-#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
   log_time operator-=(const timespec& T);
   log_time operator-(const timespec& T) const {
     log_time local(*this);
@@ -114,7 +108,6 @@
     log_time local(*this);
     return local += T;
   }
-#endif
 
   /* log_time */
   bool operator==(const log_time& T) const {
@@ -138,7 +131,6 @@
     return !(*this > T);
   }
 
-#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
   log_time operator-=(const log_time& T);
   log_time operator-(const log_time& T) const {
     log_time local(*this);
@@ -149,7 +141,6 @@
     log_time local(*this);
     return local += T;
   }
-#endif
 
   uint64_t nsec() const {
     return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
@@ -163,13 +154,12 @@
            tv_nsec / (NS_PER_SEC / MS_PER_SEC);
   }
 
-#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
   static const char default_format[];
 
   /* Add %#q for the fraction of a second to the standard library functions */
   char* strptime(const char* s, const char* format = default_format);
-#endif
 } __attribute__((__packed__));
+}
 
 #else /* __cplusplus */
 
@@ -181,5 +171,3 @@
 #endif /* __cplusplus */
 
 #endif /* __struct_log_time_defined */
-
-#endif /* _LIBS_LOG_LOG_TIME_H */
diff --git a/liblog/include/log/log_transport.h b/liblog/include/log/log_transport.h
index 80b30db..b48761a 100644
--- a/liblog/include/log/log_transport.h
+++ b/liblog/include/log/log_transport.h
@@ -7,8 +7,7 @@
 ** General Public License.
 */
 
-#ifndef _LIBS_LOG_TRANSPORT_H
-#define _LIBS_LOG_TRANSPORT_H
+#pragma once
 
 #ifdef __cplusplus
 extern "C" {
@@ -22,7 +21,7 @@
 #define LOGGER_LOGD    0x01
 #define LOGGER_KERNEL  0x02 /* Reserved/Deprecated */
 #define LOGGER_NULL    0x04 /* Does not release resources of other selections */
-#define LOGGER_LOCAL   0x08 /* logs sent to local memory */
+#define LOGGER_RESERVED 0x08 /* Reserved, previously for logging to local memory */
 #define LOGGER_STDERR  0x10 /* logs sent to stderr */
 /* clang-format on */
 
@@ -33,5 +32,3 @@
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* _LIBS_LOG_TRANSPORT_H */
diff --git a/liblog/include/log/logd.h b/liblog/include/log/logd.h
deleted file mode 100644
index 77400ca..0000000
--- a/liblog/include/log/logd.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#ifndef _LIBS_LOG_LOGD_H
-#define _LIBS_LOG_LOGD_H
-#include <log/log.h>
-#warning "Deprecated: do not include log/logd.h, use log/log.h instead"
-#endif /*_LIBS_LOG_LOGD_H*/
diff --git a/liblog/include/log/logger.h b/liblog/include/log/logger.h
deleted file mode 100644
index 1bf2d17..0000000
--- a/liblog/include/log/logger.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#ifndef _LIBS_LOG_LOGGER_H
-#define _LIBS_LOG_LOGGER_H
-#include <log/log.h>
-#warning "Deprecated: do not include log/logger.h, use log/log.h instead"
-#endif /*_LIBS_LOG_LOGGER_H*/
diff --git a/liblog/include/log/logprint.h b/liblog/include/log/logprint.h
index ca58bc7..8f4b187 100644
--- a/liblog/include/log/logprint.h
+++ b/liblog/include/log/logprint.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LOGPRINT_H
-#define _LOGPRINT_H
+#pragma once
 
 #include <pthread.h>
 
@@ -158,5 +157,3 @@
 #ifdef __cplusplus
 }
 #endif
-
-#endif /*_LOGPRINT_H*/
diff --git a/liblog/include/log/uio.h b/liblog/include/log/uio.h
deleted file mode 100644
index a492bae..0000000
--- a/liblog/include/log/uio.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2007-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.
- */
-
-#ifndef _LIBS_CUTILS_UIO_H
-#define _LIBS_CUTILS_UIO_H
-
-#if !defined(_WIN32)
-
-#include <sys/uio.h>
-
-#else
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-//
-// Implementation of sys/uio.h for Win32.
-//
-
-#include <stddef.h>
-
-struct iovec {
-  void* iov_base;
-  size_t iov_len;
-};
-
-extern int readv(int fd, struct iovec* vecs, int count);
-extern int writev(int fd, const struct iovec* vecs, int count);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-
-#endif /* _LIBS_UTILS_UIO_H */
diff --git a/liblog/include/private/android_logger.h b/liblog/include/private/android_logger.h
index 965de37..5e04148 100644
--- a/liblog/include/private/android_logger.h
+++ b/liblog/include/private/android_logger.h
@@ -16,8 +16,7 @@
 
 /* This file is used to define the internal protocol for the Android Logger */
 
-#ifndef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
-#define _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
+#pragma once
 
 /* Android private interfaces */
 
@@ -25,10 +24,8 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#if (defined(__cplusplus) && defined(_USING_LIBCXX))
-extern "C++" {
+#ifdef __cplusplus
 #include <string>
-}
 #endif
 
 #include <log/log.h>
@@ -153,41 +150,6 @@
 /* Retrieve the composed event buffer */
 int android_log_write_list_buffer(android_log_context ctx, const char** msg);
 
-#ifdef __cplusplus
-#ifdef __class_android_log_event_list_defined
-#ifndef __class_android_log_event_list_private_defined
-#define __class_android_log_event_list_private_defined
-/* android_log_context C++ helpers */
-extern "C++" {
-class __android_log_event_list : public android_log_event_list {
-  __android_log_event_list(const android_log_event_list&) = delete;
-  void operator=(const __android_log_event_list&) = delete;
-
- public:
-  explicit __android_log_event_list(int tag) : android_log_event_list(tag) {
-  }
-  explicit __android_log_event_list(log_msg& log_msg)
-      : android_log_event_list(log_msg) {
-  }
-
-#if defined(_USING_LIBCXX)
-  operator std::string() {
-    if (ret) return std::string("");
-    const char* cp = NULL;
-    ssize_t len = android_log_write_list_buffer(ctx, &cp);
-    if (len < 0) ret = len;
-    if (!cp || (len <= 0)) return std::string("");
-    return std::string(cp, len);
-  }
-#endif
-};
-}
-#endif
-#endif
-#endif
-
 #if defined(__cplusplus)
 }
 #endif
-
-#endif /* _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_ */
diff --git a/liblog/include_vndk/log/log_event_list.h b/liblog/include_vndk/log/log_event_list.h
index cbd3091..1f3dd37 100644
--- a/liblog/include_vndk/log/log_event_list.h
+++ b/liblog/include_vndk/log/log_event_list.h
@@ -27,8 +27,6 @@
 extern "C" {
 #endif
 
-#define __ANDROID_USE_LIBLOG_EVENT_INTERFACE 1
-
 /*
  * The opaque context used to manipulate lists of events.
  */
@@ -63,6 +61,13 @@
 /* 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);
 
+/* Reset writer context */
+int android_log_reset(android_log_context ctx);
+
+/* Reset reader context */
+int android_log_parser_reset(android_log_context ctx,
+                             const char* msg, size_t len);
+
 /* Finished with reader or writer context */
 int android_log_destroy(android_log_context* ctx);
 
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 66670fe..ce4c53c 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -19,12 +19,12 @@
     android_logger_get_log_readable_size; # vndk
     android_logger_get_log_version; # vndk
     android_logger_get_log_size; # vndk
-    android_logger_list_alloc; # vndk
-    android_logger_list_alloc_time; # vndk
-    android_logger_list_free; # vndk
+    android_logger_list_alloc; # apex vndk
+    android_logger_list_alloc_time; # apex vndk
+    android_logger_list_free; # apex vndk
     android_logger_list_open; # vndk
-    android_logger_list_read; # vndk
-    android_logger_open; # vndk
+    android_logger_list_read; # apex vndk
+    android_logger_open; # apex vndk
     android_logger_set_log_size; # vndk
 };
 
@@ -33,18 +33,18 @@
     android_logger_get_prune_list; # vndk
     android_logger_set_prune_list; # vndk
     android_logger_get_statistics; # vndk
-    __android_log_error_write; # vndk
+    __android_log_error_write; # apex 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
+    create_android_logger; # apex vndk
+    android_log_destroy; # apex vndk
+    android_log_write_list_begin; # apex vndk
+    android_log_write_list_end; # apex vndk
+    android_log_write_int32; # apex vndk
+    android_log_write_int64; # apex vndk
+    android_log_write_string8; # apex vndk
+    android_log_write_string8_len; # apex vndk
+    android_log_write_float32; # apex vndk
+    android_log_write_list; # apex vndk
 
 };
 
@@ -53,3 +53,30 @@
     __android_log_is_loggable_len;
     __android_log_is_debuggable; # vndk
 };
+
+LIBLOG_Q {
+  global:
+    __android_log_bswrite; # apex
+    __android_log_btwrite; # apex
+    __android_log_bwrite; # apex
+    __android_log_close; # apex
+    __android_log_security; # apex
+    android_log_reset; #vndk
+    android_log_parser_reset; #vndk
+};
+
+LIBLOG_PRIVATE {
+  global:
+    __android_log_pmsg_file_read;
+    __android_log_pmsg_file_write;
+    __android_log_security_bswrite;
+    __android_logger_get_buffer_size;
+    __android_logger_property_get_bool;
+    android_openEventTagMap;
+    android_log_processBinaryLogBuffer;
+    android_log_processLogBuffer;
+    android_log_read_next;
+    android_log_write_list_buffer;
+    android_lookupEventTagNum;
+    create_android_log_parser;
+};
diff --git a/liblog/local_logger.c b/liblog/local_logger.c
deleted file mode 100644
index 563cb3f..0000000
--- a/liblog/local_logger.c
+++ /dev/null
@@ -1,550 +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 <errno.h>
-#include <fcntl.h>
-#include <pthread.h>
-#if !defined(__MINGW32__)
-#include <pwd.h>
-#endif
-#include <log/uio.h>
-#include <sched.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
-#include <cutils/list.h> /* template, no library dependency */
-#include <log/log_transport.h>
-#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
-#include <system/thread_defs.h>
-
-#include "config_read.h"
-#include "config_write.h"
-#include "log_portability.h"
-#include "logger.h"
-
-static const char baseServiceName[] = "android.logd";
-
-static int writeToLocalInit();
-static int writeToLocalAvailable(log_id_t logId);
-static void writeToLocalReset();
-static int writeToLocalWrite(log_id_t logId, struct timespec* ts,
-                             struct iovec* vec, size_t nr);
-
-LIBLOG_HIDDEN struct android_log_transport_write localLoggerWrite = {
-  .node = { &localLoggerWrite.node, &localLoggerWrite.node },
-  .context.priv = NULL,
-  .name = "local",
-  .available = writeToLocalAvailable,
-  .open = writeToLocalInit,
-  .close = writeToLocalReset,
-  .write = writeToLocalWrite,
-};
-
-static int writeToLocalVersion(struct android_log_logger* logger,
-                               struct android_log_transport_context* transp);
-static int writeToLocalRead(struct android_log_logger_list* logger_list,
-                            struct android_log_transport_context* transp,
-                            struct log_msg* log_msg);
-static int writeToLocalPoll(struct android_log_logger_list* logger_list,
-                            struct android_log_transport_context* transp);
-static void writeToLocalClose(struct android_log_logger_list* logger_list,
-                              struct android_log_transport_context* transp);
-static int writeToLocalClear(struct android_log_logger* logger,
-                             struct android_log_transport_context* transp);
-static ssize_t writeToLocalGetSize(struct android_log_logger* logger,
-                                   struct android_log_transport_context* transp);
-static ssize_t writeToLocalSetSize(
-    struct android_log_logger* logger,
-    struct android_log_transport_context* transp __unused, size_t size);
-static ssize_t writeToLocalGetReadbleSize(
-    struct android_log_logger* logger,
-    struct android_log_transport_context* transp);
-
-struct android_log_transport_read localLoggerRead = {
-  .node = { &localLoggerRead.node, &localLoggerRead.node },
-  .name = "local",
-  .available = writeToLocalAvailable,
-  .version = writeToLocalVersion,
-  .read = writeToLocalRead,
-  .poll = writeToLocalPoll,
-  .close = writeToLocalClose,
-  .clear = writeToLocalClear,
-  .getSize = writeToLocalGetSize,
-  .setSize = writeToLocalSetSize,
-  .getReadableSize = writeToLocalGetReadbleSize,
-  .getPrune = NULL,
-  .setPrune = NULL,
-  .getStats = NULL,
-};
-
-struct LogBufferElement {
-  struct listnode node;
-  log_id_t logId;
-  pid_t tid;
-  log_time timestamp;
-  unsigned short len;
-  char msg[];
-};
-
-static const size_t MAX_SIZE_DEFAULT = 32768;
-
-/*
- * Number of log buffers we support with the following assumption:
- *  . . .
- *   LOG_ID_SECURITY = 5, // security logs go to the system logs only
- *   LOG_ID_KERNEL = 6,   // place last, third-parties can not use it
- *   LOG_ID_MAX
- * } log_id_t;
- *
- * Confirm the following should <log/log_id.h> be adjusted in the future.
- */
-#define NUMBER_OF_LOG_BUFFERS \
-  ((LOG_ID_SECURITY == (LOG_ID_MAX - 2)) ? LOG_ID_SECURITY : LOG_ID_KERNEL)
-#define BLOCK_LOG_BUFFERS(id) \
-  (((id) == LOG_ID_SECURITY) || ((id) == LOG_ID_KERNEL))
-
-static struct LogBuffer {
-  struct listnode head;
-  pthread_rwlock_t listLock;
-  char* serviceName; /* Also indicates ready by having a value */
-  /* Order and proximity important for memset */
-  size_t number[NUMBER_OF_LOG_BUFFERS];         /* clear memset          */
-  size_t size[NUMBER_OF_LOG_BUFFERS];           /* clear memset          */
-  size_t totalSize[NUMBER_OF_LOG_BUFFERS];      /* init memset           */
-  size_t maxSize[NUMBER_OF_LOG_BUFFERS];        /* init MAX_SIZE_DEFAULT */
-  struct listnode* last[NUMBER_OF_LOG_BUFFERS]; /* init &head            */
-} logbuf = {
-  .head = { &logbuf.head, &logbuf.head }, .listLock = PTHREAD_RWLOCK_INITIALIZER,
-};
-
-static void LogBufferInit(struct LogBuffer* log) {
-  size_t i;
-
-  pthread_rwlock_wrlock(&log->listLock);
-  list_init(&log->head);
-  memset(log->number, 0,
-         sizeof(log->number) + sizeof(log->size) + sizeof(log->totalSize));
-  for (i = 0; i < NUMBER_OF_LOG_BUFFERS; ++i) {
-    log->maxSize[i] = MAX_SIZE_DEFAULT;
-    log->last[i] = &log->head;
-  }
-#ifdef __BIONIC__
-  asprintf(&log->serviceName, "%s@%d:%d", baseServiceName, __android_log_uid(),
-           getpid());
-#else
-  char buffer[sizeof(baseServiceName) + 1 + 5 + 1 + 5 + 8];
-  snprintf(buffer, sizeof(buffer), "%s@%d:%d", baseServiceName,
-           __android_log_uid(), getpid());
-  log->serviceName = strdup(buffer);
-#endif
-  pthread_rwlock_unlock(&log->listLock);
-}
-
-static void LogBufferClear(struct LogBuffer* log) {
-  size_t i;
-  struct listnode* node;
-
-  pthread_rwlock_wrlock(&log->listLock);
-  memset(log->number, 0, sizeof(log->number) + sizeof(log->size));
-  for (i = 0; i < NUMBER_OF_LOG_BUFFERS; ++i) {
-    log->last[i] = &log->head;
-  }
-  while ((node = list_head(&log->head)) != &log->head) {
-    struct LogBufferElement* element;
-
-    element = node_to_item(node, struct LogBufferElement, node);
-    list_remove(node);
-    free(element);
-  }
-  pthread_rwlock_unlock(&log->listLock);
-}
-
-static inline void LogBufferFree(struct LogBuffer* log) {
-  pthread_rwlock_wrlock(&log->listLock);
-  free(log->serviceName);
-  log->serviceName = NULL;
-  pthread_rwlock_unlock(&log->listLock);
-  LogBufferClear(log);
-}
-
-static int LogBufferLog(struct LogBuffer* log,
-                        struct LogBufferElement* element) {
-  log_id_t logId = element->logId;
-
-  pthread_rwlock_wrlock(&log->listLock);
-  log->number[logId]++;
-  log->size[logId] += element->len;
-  log->totalSize[logId] += element->len;
-  /* prune entry(s) until enough space is available */
-  if (log->last[logId] == &log->head) {
-    log->last[logId] = list_tail(&log->head);
-  }
-  while (log->size[logId] > log->maxSize[logId]) {
-    struct listnode* node = log->last[logId];
-    struct LogBufferElement* e;
-    struct android_log_logger_list* logger_list;
-
-    e = node_to_item(node, struct LogBufferElement, node);
-    log->number[logId]--;
-    log->size[logId] -= e->len;
-    logger_list_rdlock();
-    logger_list_for_each(logger_list) {
-      struct android_log_transport_context* transp;
-
-      transport_context_for_each(transp, logger_list) {
-        if ((transp->transport == &localLoggerRead) &&
-            (transp->context.node == node)) {
-          if (node == &log->head) {
-            transp->context.node = &log->head;
-          } else {
-            transp->context.node = node->next;
-          }
-        }
-      }
-    }
-    logger_list_unlock();
-    if (node != &log->head) {
-      log->last[logId] = node->prev;
-    }
-    list_remove(node);
-    LOG_ALWAYS_FATAL_IF(node == log->last[logId], "corrupted list");
-    free(e);
-  }
-  /* add entry to list */
-  list_add_head(&log->head, &element->node);
-  /* ToDo: wake up all readers */
-  pthread_rwlock_unlock(&log->listLock);
-
-  return element->len;
-}
-
-/*
- * return zero if permitted to log directly to logd,
- * return 1 if binder server started and
- * return negative error number if failed to start binder server.
- */
-static int writeToLocalInit() {
-  pthread_attr_t attr;
-  struct LogBuffer* log;
-
-  if (writeToLocalAvailable(LOG_ID_MAIN) < 0) {
-    return -EPERM;
-  }
-
-  log = &logbuf;
-  if (!log->serviceName) {
-    LogBufferInit(log);
-  }
-
-  if (!log->serviceName) {
-    LogBufferFree(log);
-    return -ENOMEM;
-  }
-
-  return EPERM; /* successful local-only logging */
-}
-
-static void writeToLocalReset() {
-  LogBufferFree(&logbuf);
-}
-
-static int writeToLocalAvailable(log_id_t logId) {
-#if !defined(__MINGW32__)
-  uid_t uid;
-#endif
-
-  if ((logId >= NUMBER_OF_LOG_BUFFERS) || BLOCK_LOG_BUFFERS(logId)) {
-    return -EINVAL;
-  }
-
-/* Android hard coded permitted, system goes to logd */
-#if !defined(__MINGW32__)
-  if (__android_log_transport == LOGGER_DEFAULT) {
-    uid = __android_log_uid();
-    if ((uid < AID_APP) && (getpwuid(uid) != NULL)) {
-      return -EPERM;
-    }
-  }
-#endif
-
-  /* ToDo: Ask package manager for LOGD permissions */
-  /* Assume we do _not_ have permissions to go to LOGD, so must go local */
-  return 0;
-}
-
-static int writeToLocalWrite(log_id_t logId, struct timespec* ts,
-                             struct iovec* vec, size_t nr) {
-  size_t len, i;
-  struct LogBufferElement* element;
-
-  if ((logId >= NUMBER_OF_LOG_BUFFERS) || BLOCK_LOG_BUFFERS(logId)) {
-    return -EINVAL;
-  }
-
-  len = 0;
-  for (i = 0; i < nr; ++i) {
-    len += vec[i].iov_len;
-  }
-
-  if (len > LOGGER_ENTRY_MAX_PAYLOAD) {
-    len = LOGGER_ENTRY_MAX_PAYLOAD;
-  }
-  element = (struct LogBufferElement*)calloc(
-      1, sizeof(struct LogBufferElement) + len + 1);
-  if (!element) {
-    return errno ? -errno : -ENOMEM;
-  }
-  element->timestamp.tv_sec = ts->tv_sec;
-  element->timestamp.tv_nsec = ts->tv_nsec;
-#ifdef __BIONIC__
-  element->tid = gettid();
-#else
-  element->tid = getpid();
-#endif
-  element->logId = logId;
-  element->len = len;
-
-  char* cp = element->msg;
-  for (i = 0; i < nr; ++i) {
-    size_t iov_len = vec[i].iov_len;
-    if (iov_len > len) {
-      iov_len = len;
-    }
-    memcpy(cp, vec[i].iov_base, iov_len);
-    len -= iov_len;
-    if (len == 0) {
-      break;
-    }
-    cp += iov_len;
-  }
-
-  return LogBufferLog(&logbuf, element);
-}
-
-static int writeToLocalVersion(struct android_log_logger* logger __unused,
-                               struct android_log_transport_context* transp
-                                   __unused) {
-  return 3;
-}
-
-/* within reader lock, serviceName already validated */
-static struct listnode* writeToLocalNode(
-    struct android_log_logger_list* logger_list,
-    struct android_log_transport_context* transp) {
-  struct listnode* node;
-  unsigned logMask;
-  unsigned int tail;
-
-  node = transp->context.node;
-  if (node) {
-    return node;
-  }
-
-  if (!logger_list->tail) {
-    return transp->context.node = &logbuf.head;
-  }
-
-  logMask = transp->logMask;
-  tail = logger_list->tail;
-
-  for (node = list_head(&logbuf.head); node != &logbuf.head; node = node->next) {
-    struct LogBufferElement* element;
-    log_id_t logId;
-
-    element = node_to_item(node, struct LogBufferElement, node);
-    logId = element->logId;
-
-    if ((logMask & (1 << logId)) && !--tail) {
-      node = node->next;
-      break;
-    }
-  }
-  return transp->context.node = node;
-}
-
-static int writeToLocalRead(struct android_log_logger_list* logger_list,
-                            struct android_log_transport_context* transp,
-                            struct log_msg* log_msg) {
-  int ret;
-  struct listnode* node;
-  unsigned logMask;
-
-  pthread_rwlock_rdlock(&logbuf.listLock);
-  if (!logbuf.serviceName) {
-    pthread_rwlock_unlock(&logbuf.listLock);
-    return (logger_list->mode & ANDROID_LOG_NONBLOCK) ? -ENODEV : 0;
-  }
-
-  logMask = transp->logMask;
-
-  node = writeToLocalNode(logger_list, transp);
-
-  ret = 0;
-
-  while (node != list_head(&logbuf.head)) {
-    struct LogBufferElement* element;
-    log_id_t logId;
-
-    node = node->prev;
-    element = node_to_item(node, struct LogBufferElement, node);
-    logId = element->logId;
-
-    if (logMask & (1 << logId)) {
-      ret = log_msg->entry_v3.len = element->len;
-      log_msg->entry_v3.hdr_size = sizeof(log_msg->entry_v3);
-      log_msg->entry_v3.pid = getpid();
-      log_msg->entry_v3.tid = element->tid;
-      log_msg->entry_v3.sec = element->timestamp.tv_sec;
-      log_msg->entry_v3.nsec = element->timestamp.tv_nsec;
-      log_msg->entry_v3.lid = logId;
-      memcpy(log_msg->entry_v3.msg, element->msg, ret);
-      ret += log_msg->entry_v3.hdr_size;
-      break;
-    }
-  }
-
-  transp->context.node = node;
-
-  /* ToDo: if blocking, and no entry, put reader to sleep */
-  pthread_rwlock_unlock(&logbuf.listLock);
-  return ret;
-}
-
-static int writeToLocalPoll(struct android_log_logger_list* logger_list,
-                            struct android_log_transport_context* transp) {
-  int ret = (logger_list->mode & ANDROID_LOG_NONBLOCK) ? -ENODEV : 0;
-
-  pthread_rwlock_rdlock(&logbuf.listLock);
-
-  if (logbuf.serviceName) {
-    unsigned logMask = transp->logMask;
-    struct listnode* node = writeToLocalNode(logger_list, transp);
-
-    ret = (node != list_head(&logbuf.head));
-    if (ret) {
-      do {
-        ret = !!(logMask &
-                 (1 << (node_to_item(node->prev, struct LogBufferElement, node))
-                           ->logId));
-      } while (!ret && ((node = node->prev) != list_head(&logbuf.head)));
-    }
-
-    transp->context.node = node;
-  }
-
-  pthread_rwlock_unlock(&logbuf.listLock);
-
-  return ret;
-}
-
-static void writeToLocalClose(struct android_log_logger_list* logger_list
-                                  __unused,
-                              struct android_log_transport_context* transp) {
-  pthread_rwlock_wrlock(&logbuf.listLock);
-  transp->context.node = list_head(&logbuf.head);
-  pthread_rwlock_unlock(&logbuf.listLock);
-}
-
-static int writeToLocalClear(struct android_log_logger* logger,
-                             struct android_log_transport_context* unused
-                                 __unused) {
-  log_id_t logId = logger->logId;
-  struct listnode *node, *n;
-
-  if ((logId >= NUMBER_OF_LOG_BUFFERS) || BLOCK_LOG_BUFFERS(logId)) {
-    return -EINVAL;
-  }
-
-  pthread_rwlock_wrlock(&logbuf.listLock);
-  logbuf.number[logId] = 0;
-  logbuf.last[logId] = &logbuf.head;
-  list_for_each_safe(node, n, &logbuf.head) {
-    struct LogBufferElement* element;
-    element = node_to_item(node, struct LogBufferElement, node);
-
-    if (logId == element->logId) {
-      struct android_log_logger_list* logger_list;
-
-      logger_list_rdlock();
-      logger_list_for_each(logger_list) {
-        struct android_log_transport_context* transp;
-
-        transport_context_for_each(transp, logger_list) {
-          if ((transp->transport == &localLoggerRead) &&
-              (transp->context.node == node)) {
-            transp->context.node = node->next;
-          }
-        }
-      }
-      logger_list_unlock();
-      list_remove(node);
-      free(element);
-    }
-  }
-
-  pthread_rwlock_unlock(&logbuf.listLock);
-
-  return 0;
-}
-
-static ssize_t writeToLocalGetSize(struct android_log_logger* logger,
-                                   struct android_log_transport_context* transp
-                                       __unused) {
-  ssize_t ret = -EINVAL;
-  log_id_t logId = logger->logId;
-
-  if ((logId < NUMBER_OF_LOG_BUFFERS) && !BLOCK_LOG_BUFFERS(logId)) {
-    pthread_rwlock_rdlock(&logbuf.listLock);
-    ret = logbuf.maxSize[logId];
-    pthread_rwlock_unlock(&logbuf.listLock);
-  }
-
-  return ret;
-}
-
-static ssize_t writeToLocalSetSize(
-    struct android_log_logger* logger,
-    struct android_log_transport_context* transp __unused, size_t size) {
-  ssize_t ret = -EINVAL;
-
-  if ((size > LOGGER_ENTRY_MAX_LEN) || (size < (4 * 1024 * 1024))) {
-    log_id_t logId = logger->logId;
-    if ((logId < NUMBER_OF_LOG_BUFFERS) || !BLOCK_LOG_BUFFERS(logId)) {
-      pthread_rwlock_wrlock(&logbuf.listLock);
-      ret = logbuf.maxSize[logId] = size;
-      pthread_rwlock_unlock(&logbuf.listLock);
-    }
-  }
-
-  return ret;
-}
-
-static ssize_t writeToLocalGetReadbleSize(
-    struct android_log_logger* logger,
-    struct android_log_transport_context* transp __unused) {
-  ssize_t ret = -EINVAL;
-  log_id_t logId = logger->logId;
-
-  if ((logId < NUMBER_OF_LOG_BUFFERS) && !BLOCK_LOG_BUFFERS(logId)) {
-    pthread_rwlock_rdlock(&logbuf.listLock);
-    ret = logbuf.serviceName ? (ssize_t)logbuf.size[logId] : -EBADF;
-    pthread_rwlock_unlock(&logbuf.listLock);
-  }
-
-  return ret;
-}
diff --git a/liblog/log_event_list.c b/liblog/log_event_list.c
deleted file mode 100644
index a59cb87..0000000
--- a/liblog/log_event_list.c
+++ /dev/null
@@ -1,567 +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 <inttypes.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <log/log_event_list.h>
-#include <private/android_logger.h>
-
-#include "log_portability.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;
-
-LIBLOG_ABI_PUBLIC android_log_context create_android_logger(uint32_t tag) {
-  size_t needed, i;
-  android_log_context_internal* context;
-
-  context = calloc(1, sizeof(android_log_context_internal));
-  if (!context) {
-    return NULL;
-  }
-  context->tag = tag;
-  context->read_write_flag = kAndroidLoggerWrite;
-  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;
-
-  return (android_log_context)context;
-}
-
-LIBLOG_ABI_PUBLIC android_log_context create_android_log_parser(const char* msg,
-                                                                size_t len) {
-  android_log_context_internal* context;
-  size_t i;
-
-  context = calloc(1, sizeof(android_log_context_internal));
-  if (!context) {
-    return NULL;
-  }
-  len = (len <= MAX_EVENT_PAYLOAD) ? len : MAX_EVENT_PAYLOAD;
-  context->len = len;
-  memcpy(context->storage, msg, len);
-  context->read_write_flag = kAndroidLoggerRead;
-
-  return (android_log_context)context;
-}
-
-LIBLOG_ABI_PUBLIC int android_log_destroy(android_log_context* ctx) {
-  android_log_context_internal* context;
-
-  context = (android_log_context_internal*)*ctx;
-  if (!context) {
-    return -EBADF;
-  }
-  memset(context, 0, sizeof(*context));
-  free(context);
-  *ctx = NULL;
-  return 0;
-}
-
-LIBLOG_ABI_PUBLIC int android_log_write_list_begin(android_log_context ctx) {
-  size_t needed;
-  android_log_context_internal* context;
-
-  context = (android_log_context_internal*)ctx;
-  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
-    return -EBADF;
-  }
-  if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
-    context->overflow = true;
-    return -EOVERFLOW;
-  }
-  needed = sizeof(uint8_t) + sizeof(uint8_t);
-  if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
-    context->overflow = true;
-    return -EIO;
-  }
-  context->count[context->list_nest_depth]++;
-  context->list_nest_depth++;
-  if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
-    context->overflow = true;
-    return -EOVERFLOW;
-  }
-  if (context->overflow) {
-    return -EIO;
-  }
-  context->storage[context->pos + 0] = EVENT_TYPE_LIST;
-  context->storage[context->pos + 1] = 0;
-  context->list[context->list_nest_depth] = context->pos + 1;
-  context->count[context->list_nest_depth] = 0;
-  context->pos += needed;
-  return 0;
-}
-
-static inline void copy4LE(uint8_t* buf, uint32_t val) {
-  buf[0] = val & 0xFF;
-  buf[1] = (val >> 8) & 0xFF;
-  buf[2] = (val >> 16) & 0xFF;
-  buf[3] = (val >> 24) & 0xFF;
-}
-
-LIBLOG_ABI_PUBLIC int android_log_write_int32(android_log_context ctx,
-                                              int32_t value) {
-  size_t needed;
-  android_log_context_internal* context;
-
-  context = (android_log_context_internal*)ctx;
-  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
-    return -EBADF;
-  }
-  if (context->overflow) {
-    return -EIO;
-  }
-  needed = sizeof(uint8_t) + sizeof(value);
-  if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
-    context->overflow = true;
-    return -EIO;
-  }
-  context->count[context->list_nest_depth]++;
-  context->storage[context->pos + 0] = EVENT_TYPE_INT;
-  copy4LE(&context->storage[context->pos + 1], value);
-  context->pos += needed;
-  return 0;
-}
-
-static inline void copy8LE(uint8_t* buf, uint64_t val) {
-  buf[0] = val & 0xFF;
-  buf[1] = (val >> 8) & 0xFF;
-  buf[2] = (val >> 16) & 0xFF;
-  buf[3] = (val >> 24) & 0xFF;
-  buf[4] = (val >> 32) & 0xFF;
-  buf[5] = (val >> 40) & 0xFF;
-  buf[6] = (val >> 48) & 0xFF;
-  buf[7] = (val >> 56) & 0xFF;
-}
-
-LIBLOG_ABI_PUBLIC int android_log_write_int64(android_log_context ctx,
-                                              int64_t value) {
-  size_t needed;
-  android_log_context_internal* context;
-
-  context = (android_log_context_internal*)ctx;
-  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
-    return -EBADF;
-  }
-  if (context->overflow) {
-    return -EIO;
-  }
-  needed = sizeof(uint8_t) + sizeof(value);
-  if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
-    context->overflow = true;
-    return -EIO;
-  }
-  context->count[context->list_nest_depth]++;
-  context->storage[context->pos + 0] = EVENT_TYPE_LONG;
-  copy8LE(&context->storage[context->pos + 1], value);
-  context->pos += needed;
-  return 0;
-}
-
-LIBLOG_ABI_PUBLIC int android_log_write_string8_len(android_log_context ctx,
-                                                    const char* value,
-                                                    size_t maxlen) {
-  size_t needed;
-  ssize_t len;
-  android_log_context_internal* context;
-
-  context = (android_log_context_internal*)ctx;
-  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
-    return -EBADF;
-  }
-  if (context->overflow) {
-    return -EIO;
-  }
-  if (!value) {
-    value = "";
-  }
-  len = strnlen(value, maxlen);
-  needed = sizeof(uint8_t) + sizeof(int32_t) + len;
-  if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
-    /* Truncate string for delivery */
-    len = MAX_EVENT_PAYLOAD - context->pos - 1 - sizeof(int32_t);
-    if (len <= 0) {
-      context->overflow = true;
-      return -EIO;
-    }
-  }
-  context->count[context->list_nest_depth]++;
-  context->storage[context->pos + 0] = EVENT_TYPE_STRING;
-  copy4LE(&context->storage[context->pos + 1], len);
-  if (len) {
-    memcpy(&context->storage[context->pos + 5], value, len);
-  }
-  context->pos += needed;
-  return len;
-}
-
-LIBLOG_ABI_PUBLIC int android_log_write_string8(android_log_context ctx,
-                                                const char* value) {
-  return android_log_write_string8_len(ctx, value, MAX_EVENT_PAYLOAD);
-}
-
-LIBLOG_ABI_PUBLIC int android_log_write_float32(android_log_context ctx,
-                                                float value) {
-  size_t needed;
-  uint32_t ivalue;
-  android_log_context_internal* context;
-
-  context = (android_log_context_internal*)ctx;
-  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
-    return -EBADF;
-  }
-  if (context->overflow) {
-    return -EIO;
-  }
-  needed = sizeof(uint8_t) + sizeof(ivalue);
-  if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
-    context->overflow = true;
-    return -EIO;
-  }
-  ivalue = *(uint32_t*)&value;
-  context->count[context->list_nest_depth]++;
-  context->storage[context->pos + 0] = EVENT_TYPE_FLOAT;
-  copy4LE(&context->storage[context->pos + 1], ivalue);
-  context->pos += needed;
-  return 0;
-}
-
-LIBLOG_ABI_PUBLIC int android_log_write_list_end(android_log_context ctx) {
-  android_log_context_internal* context;
-
-  context = (android_log_context_internal*)ctx;
-  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
-    return -EBADF;
-  }
-  if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
-    context->overflow = true;
-    context->list_nest_depth--;
-    return -EOVERFLOW;
-  }
-  if (!context->list_nest_depth) {
-    context->overflow = true;
-    return -EOVERFLOW;
-  }
-  if (context->list[context->list_nest_depth] <= 0) {
-    context->list_nest_depth--;
-    context->overflow = true;
-    return -EOVERFLOW;
-  }
-  context->storage[context->list[context->list_nest_depth]] =
-      context->count[context->list_nest_depth];
-  context->list_nest_depth--;
-  return 0;
-}
-
-/*
- * Logs the list of elements to the event log.
- */
-LIBLOG_ABI_PUBLIC int android_log_write_list(android_log_context ctx,
-                                             log_id_t id) {
-  android_log_context_internal* context;
-  const char* msg;
-  ssize_t len;
-
-  if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY) && (id != LOG_ID_STATS)) {
-    return -EINVAL;
-  }
-
-  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);
-  }
-  return (id == LOG_ID_EVENTS)
-             ? __android_log_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,
-                                                     const char** buffer) {
-  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;
-  }
-  if (buffer == NULL) {
-    return -EFAULT;
-  }
-  /* 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);
-  }
-  *buffer = msg;
-  return len;
-}
-
-/*
- * Extract a 4-byte value from a byte stream.
- */
-static inline uint32_t get4LE(const uint8_t* src) {
-  return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
-}
-
-/*
- * Extract an 8-byte value from a byte stream.
- */
-static inline uint64_t get8LE(const uint8_t* src) {
-  uint32_t low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
-  uint32_t high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
-  return ((uint64_t)high << 32) | (uint64_t)low;
-}
-
-/*
- * Gets the next element. Parsing errors result in an EVENT_TYPE_UNKNOWN type.
- * If there is nothing to process, the complete field is set to non-zero. If
- * an EVENT_TYPE_UNKNOWN type is returned once, and the caller does not check
- * this and continues to call this function, the behavior is undefined
- * (although it won't crash).
- */
-static android_log_list_element android_log_read_next_internal(
-    android_log_context ctx, int peek) {
-  android_log_list_element elem;
-  unsigned pos;
-  android_log_context_internal* context;
-
-  context = (android_log_context_internal*)ctx;
-
-  memset(&elem, 0, sizeof(elem));
-
-  /* Nothing to parse from this context, so return complete. */
-  if (!context || (kAndroidLoggerRead != context->read_write_flag) ||
-      (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) ||
-      (context->count[context->list_nest_depth] >=
-       (MAX_EVENT_PAYLOAD / (sizeof(uint8_t) + sizeof(uint8_t))))) {
-    elem.type = EVENT_TYPE_UNKNOWN;
-    if (context && (context->list_stop ||
-                    ((context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) &&
-                     !context->count[context->list_nest_depth]))) {
-      elem.type = EVENT_TYPE_LIST_STOP;
-    }
-    elem.complete = true;
-    return elem;
-  }
-
-  /*
-   * Use a different variable to update the position in case this
-   * operation is a "peek".
-   */
-  pos = context->pos;
-  if (context->list_stop) {
-    elem.type = EVENT_TYPE_LIST_STOP;
-    elem.complete = !context->count[0] &&
-                    (!context->list_nest_depth ||
-                     ((context->list_nest_depth == 1) && !context->count[1]));
-    if (!peek) {
-      /* Suck in superfluous stop */
-      if (context->storage[pos] == EVENT_TYPE_LIST_STOP) {
-        context->pos = pos + 1;
-      }
-      if (context->list_nest_depth) {
-        --context->list_nest_depth;
-        if (context->count[context->list_nest_depth]) {
-          context->list_stop = false;
-        }
-      } else {
-        context->list_stop = false;
-      }
-    }
-    return elem;
-  }
-  if ((pos + 1) > context->len) {
-    elem.type = EVENT_TYPE_UNKNOWN;
-    elem.complete = true;
-    return elem;
-  }
-
-  elem.type = context->storage[pos++];
-  switch ((int)elem.type) {
-    case EVENT_TYPE_FLOAT:
-    /* Rely on union to translate elem.data.int32 into elem.data.float32 */
-    /* FALLTHRU */
-    case EVENT_TYPE_INT:
-      elem.len = sizeof(int32_t);
-      if ((pos + elem.len) > context->len) {
-        elem.type = EVENT_TYPE_UNKNOWN;
-        return elem;
-      }
-      elem.data.int32 = get4LE(&context->storage[pos]);
-      /* common tangeable object suffix */
-      pos += elem.len;
-      elem.complete = !context->list_nest_depth && !context->count[0];
-      if (!peek) {
-        if (!context->count[context->list_nest_depth] ||
-            !--(context->count[context->list_nest_depth])) {
-          context->list_stop = true;
-        }
-        context->pos = pos;
-      }
-      return elem;
-
-    case EVENT_TYPE_LONG:
-      elem.len = sizeof(int64_t);
-      if ((pos + elem.len) > context->len) {
-        elem.type = EVENT_TYPE_UNKNOWN;
-        return elem;
-      }
-      elem.data.int64 = get8LE(&context->storage[pos]);
-      /* common tangeable object suffix */
-      pos += elem.len;
-      elem.complete = !context->list_nest_depth && !context->count[0];
-      if (!peek) {
-        if (!context->count[context->list_nest_depth] ||
-            !--(context->count[context->list_nest_depth])) {
-          context->list_stop = true;
-        }
-        context->pos = pos;
-      }
-      return elem;
-
-    case EVENT_TYPE_STRING:
-      if ((pos + sizeof(int32_t)) > context->len) {
-        elem.type = EVENT_TYPE_UNKNOWN;
-        elem.complete = true;
-        return elem;
-      }
-      elem.len = get4LE(&context->storage[pos]);
-      pos += sizeof(int32_t);
-      if ((pos + elem.len) > context->len) {
-        elem.len = context->len - pos; /* truncate string */
-        elem.complete = true;
-        if (!elem.len) {
-          elem.type = EVENT_TYPE_UNKNOWN;
-          return elem;
-        }
-      }
-      elem.data.string = (char*)&context->storage[pos];
-      /* common tangeable object suffix */
-      pos += elem.len;
-      elem.complete = !context->list_nest_depth && !context->count[0];
-      if (!peek) {
-        if (!context->count[context->list_nest_depth] ||
-            !--(context->count[context->list_nest_depth])) {
-          context->list_stop = true;
-        }
-        context->pos = pos;
-      }
-      return elem;
-
-    case EVENT_TYPE_LIST:
-      if ((pos + sizeof(uint8_t)) > context->len) {
-        elem.type = EVENT_TYPE_UNKNOWN;
-        elem.complete = true;
-        return elem;
-      }
-      elem.complete = context->list_nest_depth >= ANDROID_MAX_LIST_NEST_DEPTH;
-      if (peek) {
-        return elem;
-      }
-      if (context->count[context->list_nest_depth]) {
-        context->count[context->list_nest_depth]--;
-      }
-      context->list_stop = !context->storage[pos];
-      context->list_nest_depth++;
-      if (context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) {
-        context->count[context->list_nest_depth] = context->storage[pos];
-      }
-      context->pos = pos + sizeof(uint8_t);
-      return elem;
-
-    case EVENT_TYPE_LIST_STOP: /* Suprise Newline terminates lists. */
-      if (!peek) {
-        context->pos = pos;
-      }
-      elem.type = EVENT_TYPE_UNKNOWN;
-      elem.complete = !context->list_nest_depth;
-      if (context->list_nest_depth > 0) {
-        elem.type = EVENT_TYPE_LIST_STOP;
-        if (!peek) {
-          context->list_nest_depth--;
-        }
-      }
-      return elem;
-
-    default:
-      elem.type = EVENT_TYPE_UNKNOWN;
-      return elem;
-  }
-}
-
-LIBLOG_ABI_PUBLIC android_log_list_element
-android_log_read_next(android_log_context ctx) {
-  return android_log_read_next_internal(ctx, 0);
-}
-
-LIBLOG_ABI_PUBLIC android_log_list_element
-android_log_peek_next(android_log_context ctx) {
-  return android_log_read_next_internal(ctx, 1);
-}
diff --git a/liblog/log_event_list.cpp b/liblog/log_event_list.cpp
new file mode 100644
index 0000000..b1b527c
--- /dev/null
+++ b/liblog/log_event_list.cpp
@@ -0,0 +1,600 @@
+/*
+ * 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 <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <log/log_event_list.h>
+#include <private/android_logger.h>
+
+#include "log_portability.h"
+
+#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
+
+enum ReadWriteFlag {
+  kAndroidLoggerRead = 1,
+  kAndroidLoggerWrite = 2,
+};
+
+struct android_log_context_internal {
+  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 */
+  ReadWriteFlag read_write_flag;
+  uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
+};
+
+// TODO(tomcherry): real C++ structs.
+typedef struct android_log_context_internal android_log_context_internal;
+
+static void init_context(android_log_context_internal* context, uint32_t tag) {
+  size_t needed;
+
+  context->tag = tag;
+  context->read_write_flag = kAndroidLoggerWrite;
+  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;
+}
+
+static void init_parser_context(android_log_context_internal* context, const char* msg,
+                                size_t len) {
+  len = (len <= MAX_EVENT_PAYLOAD) ? len : MAX_EVENT_PAYLOAD;
+  context->len = len;
+  memcpy(context->storage, msg, len);
+  context->read_write_flag = kAndroidLoggerRead;
+}
+
+android_log_context create_android_logger(uint32_t tag) {
+  android_log_context_internal* context;
+
+  context =
+      static_cast<android_log_context_internal*>(calloc(1, sizeof(android_log_context_internal)));
+  if (!context) {
+    return NULL;
+  }
+  init_context(context, tag);
+
+  return (android_log_context)context;
+}
+
+android_log_context create_android_log_parser(const char* msg, size_t len) {
+  android_log_context_internal* context;
+  size_t i;
+
+  context =
+      static_cast<android_log_context_internal*>(calloc(1, sizeof(android_log_context_internal)));
+  if (!context) {
+    return NULL;
+  }
+  init_parser_context(context, msg, len);
+
+  return (android_log_context)context;
+}
+
+int android_log_destroy(android_log_context* ctx) {
+  android_log_context_internal* context;
+
+  context = (android_log_context_internal*)*ctx;
+  if (!context) {
+    return -EBADF;
+  }
+  memset(context, 0, sizeof(*context));
+  free(context);
+  *ctx = NULL;
+  return 0;
+}
+
+int android_log_reset(android_log_context ctx) {
+  android_log_context_internal* context;
+  uint32_t tag;
+
+  context = (android_log_context_internal*)ctx;
+  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+    return -EBADF;
+  }
+
+  tag = context->tag;
+  memset(context, 0, sizeof(*context));
+  init_context(context, tag);
+
+  return 0;
+}
+
+int android_log_parser_reset(android_log_context ctx, const char* msg, size_t len) {
+  android_log_context_internal* context;
+
+  context = (android_log_context_internal*)ctx;
+  if (!context || (kAndroidLoggerRead != context->read_write_flag)) {
+    return -EBADF;
+  }
+
+  memset(context, 0, sizeof(*context));
+  init_parser_context(context, msg, len);
+
+  return 0;
+}
+
+int android_log_write_list_begin(android_log_context ctx) {
+  size_t needed;
+  android_log_context_internal* context;
+
+  context = (android_log_context_internal*)ctx;
+  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+    return -EBADF;
+  }
+  if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+    context->overflow = true;
+    return -EOVERFLOW;
+  }
+  needed = sizeof(uint8_t) + sizeof(uint8_t);
+  if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+    context->overflow = true;
+    return -EIO;
+  }
+  context->count[context->list_nest_depth]++;
+  context->list_nest_depth++;
+  if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+    context->overflow = true;
+    return -EOVERFLOW;
+  }
+  if (context->overflow) {
+    return -EIO;
+  }
+  context->storage[context->pos + 0] = EVENT_TYPE_LIST;
+  context->storage[context->pos + 1] = 0;
+  context->list[context->list_nest_depth] = context->pos + 1;
+  context->count[context->list_nest_depth] = 0;
+  context->pos += needed;
+  return 0;
+}
+
+static inline void copy4LE(uint8_t* buf, uint32_t val) {
+  buf[0] = val & 0xFF;
+  buf[1] = (val >> 8) & 0xFF;
+  buf[2] = (val >> 16) & 0xFF;
+  buf[3] = (val >> 24) & 0xFF;
+}
+
+int android_log_write_int32(android_log_context ctx, int32_t value) {
+  size_t needed;
+  android_log_context_internal* context;
+
+  context = (android_log_context_internal*)ctx;
+  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+    return -EBADF;
+  }
+  if (context->overflow) {
+    return -EIO;
+  }
+  needed = sizeof(uint8_t) + sizeof(value);
+  if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+    context->overflow = true;
+    return -EIO;
+  }
+  context->count[context->list_nest_depth]++;
+  context->storage[context->pos + 0] = EVENT_TYPE_INT;
+  copy4LE(&context->storage[context->pos + 1], value);
+  context->pos += needed;
+  return 0;
+}
+
+static inline void copy8LE(uint8_t* buf, uint64_t val) {
+  buf[0] = val & 0xFF;
+  buf[1] = (val >> 8) & 0xFF;
+  buf[2] = (val >> 16) & 0xFF;
+  buf[3] = (val >> 24) & 0xFF;
+  buf[4] = (val >> 32) & 0xFF;
+  buf[5] = (val >> 40) & 0xFF;
+  buf[6] = (val >> 48) & 0xFF;
+  buf[7] = (val >> 56) & 0xFF;
+}
+
+int android_log_write_int64(android_log_context ctx, int64_t value) {
+  size_t needed;
+  android_log_context_internal* context;
+
+  context = (android_log_context_internal*)ctx;
+  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+    return -EBADF;
+  }
+  if (context->overflow) {
+    return -EIO;
+  }
+  needed = sizeof(uint8_t) + sizeof(value);
+  if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+    context->overflow = true;
+    return -EIO;
+  }
+  context->count[context->list_nest_depth]++;
+  context->storage[context->pos + 0] = EVENT_TYPE_LONG;
+  copy8LE(&context->storage[context->pos + 1], value);
+  context->pos += needed;
+  return 0;
+}
+
+int android_log_write_string8_len(android_log_context ctx, const char* value, size_t maxlen) {
+  size_t needed;
+  ssize_t len;
+  android_log_context_internal* context;
+
+  context = (android_log_context_internal*)ctx;
+  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+    return -EBADF;
+  }
+  if (context->overflow) {
+    return -EIO;
+  }
+  if (!value) {
+    value = "";
+  }
+  len = strnlen(value, maxlen);
+  needed = sizeof(uint8_t) + sizeof(int32_t) + len;
+  if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+    /* Truncate string for delivery */
+    len = MAX_EVENT_PAYLOAD - context->pos - 1 - sizeof(int32_t);
+    if (len <= 0) {
+      context->overflow = true;
+      return -EIO;
+    }
+  }
+  context->count[context->list_nest_depth]++;
+  context->storage[context->pos + 0] = EVENT_TYPE_STRING;
+  copy4LE(&context->storage[context->pos + 1], len);
+  if (len) {
+    memcpy(&context->storage[context->pos + 5], value, len);
+  }
+  context->pos += needed;
+  return len;
+}
+
+int android_log_write_string8(android_log_context ctx, const char* value) {
+  return android_log_write_string8_len(ctx, value, MAX_EVENT_PAYLOAD);
+}
+
+int android_log_write_float32(android_log_context ctx, float value) {
+  size_t needed;
+  uint32_t ivalue;
+  android_log_context_internal* context;
+
+  context = (android_log_context_internal*)ctx;
+  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+    return -EBADF;
+  }
+  if (context->overflow) {
+    return -EIO;
+  }
+  needed = sizeof(uint8_t) + sizeof(ivalue);
+  if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+    context->overflow = true;
+    return -EIO;
+  }
+  ivalue = *(uint32_t*)&value;
+  context->count[context->list_nest_depth]++;
+  context->storage[context->pos + 0] = EVENT_TYPE_FLOAT;
+  copy4LE(&context->storage[context->pos + 1], ivalue);
+  context->pos += needed;
+  return 0;
+}
+
+int android_log_write_list_end(android_log_context ctx) {
+  android_log_context_internal* context;
+
+  context = (android_log_context_internal*)ctx;
+  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+    return -EBADF;
+  }
+  if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+    context->overflow = true;
+    context->list_nest_depth--;
+    return -EOVERFLOW;
+  }
+  if (!context->list_nest_depth) {
+    context->overflow = true;
+    return -EOVERFLOW;
+  }
+  if (context->list[context->list_nest_depth] <= 0) {
+    context->list_nest_depth--;
+    context->overflow = true;
+    return -EOVERFLOW;
+  }
+  context->storage[context->list[context->list_nest_depth]] =
+      context->count[context->list_nest_depth];
+  context->list_nest_depth--;
+  return 0;
+}
+
+/*
+ * Logs the list of elements to the event log.
+ */
+int android_log_write_list(android_log_context ctx, log_id_t id) {
+  android_log_context_internal* context;
+  const char* msg;
+  ssize_t len;
+
+  if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY) && (id != LOG_ID_STATS)) {
+    return -EINVAL;
+  }
+
+  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);
+  }
+  return (id == LOG_ID_EVENTS)
+             ? __android_log_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));
+}
+
+int android_log_write_list_buffer(android_log_context ctx, const char** buffer) {
+  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;
+  }
+  if (buffer == NULL) {
+    return -EFAULT;
+  }
+  /* 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);
+  }
+  *buffer = msg;
+  return len;
+}
+
+/*
+ * Extract a 4-byte value from a byte stream.
+ */
+static inline uint32_t get4LE(const uint8_t* src) {
+  return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+/*
+ * Extract an 8-byte value from a byte stream.
+ */
+static inline uint64_t get8LE(const uint8_t* src) {
+  uint32_t low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+  uint32_t high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
+  return ((uint64_t)high << 32) | (uint64_t)low;
+}
+
+/*
+ * Gets the next element. Parsing errors result in an EVENT_TYPE_UNKNOWN type.
+ * If there is nothing to process, the complete field is set to non-zero. If
+ * an EVENT_TYPE_UNKNOWN type is returned once, and the caller does not check
+ * this and continues to call this function, the behavior is undefined
+ * (although it won't crash).
+ */
+static android_log_list_element android_log_read_next_internal(android_log_context ctx, int peek) {
+  android_log_list_element elem;
+  unsigned pos;
+  android_log_context_internal* context;
+
+  context = (android_log_context_internal*)ctx;
+
+  memset(&elem, 0, sizeof(elem));
+
+  /* Nothing to parse from this context, so return complete. */
+  if (!context || (kAndroidLoggerRead != context->read_write_flag) ||
+      (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) ||
+      (context->count[context->list_nest_depth] >=
+       (MAX_EVENT_PAYLOAD / (sizeof(uint8_t) + sizeof(uint8_t))))) {
+    elem.type = EVENT_TYPE_UNKNOWN;
+    if (context &&
+        (context->list_stop || ((context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) &&
+                                !context->count[context->list_nest_depth]))) {
+      elem.type = EVENT_TYPE_LIST_STOP;
+    }
+    elem.complete = true;
+    return elem;
+  }
+
+  /*
+   * Use a different variable to update the position in case this
+   * operation is a "peek".
+   */
+  pos = context->pos;
+  if (context->list_stop) {
+    elem.type = EVENT_TYPE_LIST_STOP;
+    elem.complete = !context->count[0] && (!context->list_nest_depth ||
+                                           ((context->list_nest_depth == 1) && !context->count[1]));
+    if (!peek) {
+      /* Suck in superfluous stop */
+      if (context->storage[pos] == EVENT_TYPE_LIST_STOP) {
+        context->pos = pos + 1;
+      }
+      if (context->list_nest_depth) {
+        --context->list_nest_depth;
+        if (context->count[context->list_nest_depth]) {
+          context->list_stop = false;
+        }
+      } else {
+        context->list_stop = false;
+      }
+    }
+    return elem;
+  }
+  if ((pos + 1) > context->len) {
+    elem.type = EVENT_TYPE_UNKNOWN;
+    elem.complete = true;
+    return elem;
+  }
+
+  elem.type = static_cast<AndroidEventLogType>(context->storage[pos++]);
+  switch ((int)elem.type) {
+    case EVENT_TYPE_FLOAT:
+    /* Rely on union to translate elem.data.int32 into elem.data.float32 */
+    /* FALLTHRU */
+    case EVENT_TYPE_INT:
+      elem.len = sizeof(int32_t);
+      if ((pos + elem.len) > context->len) {
+        elem.type = EVENT_TYPE_UNKNOWN;
+        return elem;
+      }
+      elem.data.int32 = get4LE(&context->storage[pos]);
+      /* common tangeable object suffix */
+      pos += elem.len;
+      elem.complete = !context->list_nest_depth && !context->count[0];
+      if (!peek) {
+        if (!context->count[context->list_nest_depth] ||
+            !--(context->count[context->list_nest_depth])) {
+          context->list_stop = true;
+        }
+        context->pos = pos;
+      }
+      return elem;
+
+    case EVENT_TYPE_LONG:
+      elem.len = sizeof(int64_t);
+      if ((pos + elem.len) > context->len) {
+        elem.type = EVENT_TYPE_UNKNOWN;
+        return elem;
+      }
+      elem.data.int64 = get8LE(&context->storage[pos]);
+      /* common tangeable object suffix */
+      pos += elem.len;
+      elem.complete = !context->list_nest_depth && !context->count[0];
+      if (!peek) {
+        if (!context->count[context->list_nest_depth] ||
+            !--(context->count[context->list_nest_depth])) {
+          context->list_stop = true;
+        }
+        context->pos = pos;
+      }
+      return elem;
+
+    case EVENT_TYPE_STRING:
+      if ((pos + sizeof(int32_t)) > context->len) {
+        elem.type = EVENT_TYPE_UNKNOWN;
+        elem.complete = true;
+        return elem;
+      }
+      elem.len = get4LE(&context->storage[pos]);
+      pos += sizeof(int32_t);
+      if ((pos + elem.len) > context->len) {
+        elem.len = context->len - pos; /* truncate string */
+        elem.complete = true;
+        if (!elem.len) {
+          elem.type = EVENT_TYPE_UNKNOWN;
+          return elem;
+        }
+      }
+      elem.data.string = (char*)&context->storage[pos];
+      /* common tangeable object suffix */
+      pos += elem.len;
+      elem.complete = !context->list_nest_depth && !context->count[0];
+      if (!peek) {
+        if (!context->count[context->list_nest_depth] ||
+            !--(context->count[context->list_nest_depth])) {
+          context->list_stop = true;
+        }
+        context->pos = pos;
+      }
+      return elem;
+
+    case EVENT_TYPE_LIST:
+      if ((pos + sizeof(uint8_t)) > context->len) {
+        elem.type = EVENT_TYPE_UNKNOWN;
+        elem.complete = true;
+        return elem;
+      }
+      elem.complete = context->list_nest_depth >= ANDROID_MAX_LIST_NEST_DEPTH;
+      if (peek) {
+        return elem;
+      }
+      if (context->count[context->list_nest_depth]) {
+        context->count[context->list_nest_depth]--;
+      }
+      context->list_stop = !context->storage[pos];
+      context->list_nest_depth++;
+      if (context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) {
+        context->count[context->list_nest_depth] = context->storage[pos];
+      }
+      context->pos = pos + sizeof(uint8_t);
+      return elem;
+
+    case EVENT_TYPE_LIST_STOP: /* Suprise Newline terminates lists. */
+      if (!peek) {
+        context->pos = pos;
+      }
+      elem.type = EVENT_TYPE_UNKNOWN;
+      elem.complete = !context->list_nest_depth;
+      if (context->list_nest_depth > 0) {
+        elem.type = EVENT_TYPE_LIST_STOP;
+        if (!peek) {
+          context->list_nest_depth--;
+        }
+      }
+      return elem;
+
+    default:
+      elem.type = EVENT_TYPE_UNKNOWN;
+      return elem;
+  }
+}
+
+android_log_list_element android_log_read_next(android_log_context ctx) {
+  return android_log_read_next_internal(ctx, 0);
+}
+
+android_log_list_element android_log_peek_next(android_log_context ctx) {
+  return android_log_read_next_internal(ctx, 1);
+}
diff --git a/liblog/log_event_write.c b/liblog/log_event_write.c
deleted file mode 100644
index 45a6f37..0000000
--- a/liblog/log_event_write.c
+++ /dev/null
@@ -1,51 +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 <stdint.h>
-
-#include <log/log.h>
-#include <log/log_event_list.h>
-
-#include "log_portability.h"
-
-#define MAX_SUBTAG_LEN 32
-
-LIBLOG_ABI_PUBLIC int __android_log_error_write(int tag, const char* subTag,
-                                                int32_t uid, const char* data,
-                                                uint32_t dataLen) {
-  int ret = -EINVAL;
-
-  if (subTag && (data || !dataLen)) {
-    android_log_context ctx = create_android_logger(tag);
-
-    ret = -ENOMEM;
-    if (ctx) {
-      ret = android_log_write_string8_len(ctx, subTag, MAX_SUBTAG_LEN);
-      if (ret >= 0) {
-        ret = android_log_write_int32(ctx, uid);
-        if (ret >= 0) {
-          ret = android_log_write_string8_len(ctx, data, dataLen);
-          if (ret >= 0) {
-            ret = android_log_write_list(ctx, LOG_ID_EVENTS);
-          }
-        }
-      }
-      android_log_destroy(&ctx);
-    }
-  }
-  return ret;
-}
diff --git a/liblog/log_event_write.cpp b/liblog/log_event_write.cpp
new file mode 100644
index 0000000..d04ba90
--- /dev/null
+++ b/liblog/log_event_write.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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 <stdint.h>
+
+#include <log/log.h>
+#include <log/log_event_list.h>
+
+#include "log_portability.h"
+
+#define MAX_SUBTAG_LEN 32
+
+int __android_log_error_write(int tag, const char* subTag, int32_t uid, const char* data,
+                              uint32_t dataLen) {
+  int ret = -EINVAL;
+
+  if (subTag && (data || !dataLen)) {
+    android_log_context ctx = create_android_logger(tag);
+
+    ret = -ENOMEM;
+    if (ctx) {
+      ret = android_log_write_string8_len(ctx, subTag, MAX_SUBTAG_LEN);
+      if (ret >= 0) {
+        ret = android_log_write_int32(ctx, uid);
+        if (ret >= 0) {
+          ret = android_log_write_string8_len(ctx, data, dataLen);
+          if (ret >= 0) {
+            ret = android_log_write_list(ctx, LOG_ID_EVENTS);
+          }
+        }
+      }
+      android_log_destroy(&ctx);
+    }
+  }
+  return ret;
+}
diff --git a/liblog/log_portability.h b/liblog/log_portability.h
index 88805c7..b7279d1 100644
--- a/liblog/log_portability.h
+++ b/liblog/log_portability.h
@@ -14,41 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef _LIBLOG_PORTABILITY_H__
-#define _LIBLOG_PORTABILITY_H__
+#pragma once
 
 #include <sys/cdefs.h>
 #include <unistd.h>
 
-/* Helpful private sys/cdefs.h like definitions */
-
-/* Declare this library function hidden and internal */
-#if defined(_WIN32)
-#define LIBLOG_HIDDEN
-#else
-#define LIBLOG_HIDDEN __attribute__((visibility("hidden")))
-#endif
-
-/* Declare this library function visible and external */
-#if defined(_WIN32)
-#define LIBLOG_ABI_PUBLIC
-#else
-#define LIBLOG_ABI_PUBLIC __attribute__((visibility("default")))
-#endif
-
-/* Declare this library function visible but private */
-#define LIBLOG_ABI_PRIVATE LIBLOG_ABI_PUBLIC
-
-/*
- * Declare this library function as reimplementation.
- * Prevent circular dependencies, but allow _real_ library to hijack
- */
-#if defined(_WIN32)
-#define LIBLOG_WEAK static /* Accept that it is totally private */
-#else
-#define LIBLOG_WEAK __attribute__((weak, visibility("default")))
-#endif
-
 /* possible missing definitions in sys/cdefs.h */
 
 /* DECLS */
@@ -62,11 +32,6 @@
 #endif
 #endif
 
-/* Unused argument. For C code only, remove symbol name for C++ */
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
-
 /* possible missing definitions in unistd.h */
 
 #ifndef TEMP_FAILURE_RETRY
@@ -80,5 +45,3 @@
     _rc;                                   \
   })
 #endif
-
-#endif /* _LIBLOG_PORTABILITY_H__ */
diff --git a/liblog/log_ratelimit.cpp b/liblog/log_ratelimit.cpp
deleted file mode 100644
index 33770dd..0000000
--- a/liblog/log_ratelimit.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
-** Copyright 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 <pthread.h>
-#include <time.h>
-
-#include <log/log.h>
-
-#include "log_portability.h"
-
-// Global default if 'last' argument in __android_log_ratelimit is NULL
-static time_t g_last_clock;
-// Global above can not deal well with callers playing games with the
-// seconds argument, so we will also hold on to the maximum value
-// ever provided and use that to gain consistency.  If the caller
-// provides their own 'last' argument, then they can play such games
-// of varying the 'seconds' argument to their pleasure.
-static time_t g_last_seconds;
-static const time_t last_seconds_default = 10;
-static const time_t last_seconds_max = 24 * 60 * 60;  // maximum of a day
-static const time_t last_seconds_min = 2;             // granularity
-// Lock to protect last_clock and last_seconds, but also 'last'
-// argument (not NULL) as supplied to __android_log_ratelimit.
-static pthread_mutex_t lock_ratelimit = PTHREAD_MUTEX_INITIALIZER;
-
-// if last is NULL, caller _must_ provide a consistent value for
-// seconds, otherwise we will take the maximum ever issued and hold
-// on to that.  Preserves value of non-zero errno.  Return -1 if we
-// can not acquire a lock, 0 if we are not to log a message, and 1
-// if we are ok to log a message.  Caller should check > 0 for true.
-LIBLOG_ABI_PUBLIC int __android_log_ratelimit(time_t seconds, time_t* last) {
-  int save_errno = errno;
-
-  // Two reasons for trylock failure:
-  //   1. In a signal handler. Must prevent deadlock
-  //   2. Too many threads calling __android_log_ratelimit.
-  //      Bonus to not print if they race here because that
-  //      dovetails the goal of ratelimiting. One may print
-  //      and the others will wait their turn ...
-  if (pthread_mutex_trylock(&lock_ratelimit)) {
-    if (save_errno) errno = save_errno;
-    return -1;
-  }
-
-  if (seconds == 0) {
-    seconds = last_seconds_default;
-  } else if (seconds < last_seconds_min) {
-    seconds = last_seconds_min;
-  } else if (seconds > last_seconds_max) {
-    seconds = last_seconds_max;
-  }
-
-  if (!last) {
-    if (g_last_seconds > seconds) {
-      seconds = g_last_seconds;
-    } else if (g_last_seconds < seconds) {
-      g_last_seconds = seconds;
-    }
-    last = &g_last_clock;
-  }
-
-  time_t now = time(NULL);
-  if ((now == (time_t)-1) || ((*last + seconds) > now)) {
-    pthread_mutex_unlock(&lock_ratelimit);
-    if (save_errno) errno = save_errno;
-    return 0;
-  }
-  *last = now;
-  pthread_mutex_unlock(&lock_ratelimit);
-  if (save_errno) errno = save_errno;
-  return 1;
-}
diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp
index ae376be..3ae250f 100644
--- a/liblog/log_time.cpp
+++ b/liblog/log_time.cpp
@@ -23,12 +23,12 @@
 
 #include "log_portability.h"
 
-LIBLOG_ABI_PRIVATE const char log_time::default_format[] = "%m-%d %H:%M:%S.%q";
-LIBLOG_ABI_PRIVATE const timespec log_time::EPOCH = { 0, 0 };
+const char log_time::default_format[] = "%m-%d %H:%M:%S.%q";
+const timespec log_time::EPOCH = {0, 0};
 
 // Add %#q for fractional seconds to standard strptime function
 
-LIBLOG_ABI_PRIVATE char* log_time::strptime(const char* s, const char* format) {
+char* log_time::strptime(const char* s, const char* format) {
   time_t now;
 #ifdef __linux__
   *this = log_time(CLOCK_REALTIME);
@@ -134,10 +134,10 @@
   return ret;
 }
 
-LIBLOG_ABI_PRIVATE log_time log_time::operator-=(const timespec& T) {
+log_time log_time::operator-=(const timespec& T) {
   // No concept of negative time, clamp to EPOCH
   if (*this <= T) {
-    return *this = EPOCH;
+    return *this = log_time(EPOCH);
   }
 
   if (this->tv_nsec < (unsigned long int)T.tv_nsec) {
@@ -151,7 +151,7 @@
   return *this;
 }
 
-LIBLOG_ABI_PRIVATE log_time log_time::operator+=(const timespec& T) {
+log_time log_time::operator+=(const timespec& T) {
   this->tv_nsec += (unsigned long int)T.tv_nsec;
   if (this->tv_nsec >= NS_PER_SEC) {
     this->tv_nsec -= NS_PER_SEC;
@@ -162,10 +162,10 @@
   return *this;
 }
 
-LIBLOG_ABI_PRIVATE log_time log_time::operator-=(const log_time& T) {
+log_time log_time::operator-=(const log_time& T) {
   // No concept of negative time, clamp to EPOCH
   if (*this <= T) {
-    return *this = EPOCH;
+    return *this = log_time(EPOCH);
   }
 
   if (this->tv_nsec < T.tv_nsec) {
@@ -179,7 +179,7 @@
   return *this;
 }
 
-LIBLOG_ABI_PRIVATE log_time log_time::operator+=(const log_time& T) {
+log_time log_time::operator+=(const log_time& T) {
   this->tv_nsec += T.tv_nsec;
   if (this->tv_nsec >= NS_PER_SEC) {
     this->tv_nsec -= NS_PER_SEC;
diff --git a/liblog/logd_reader.c b/liblog/logd_reader.c
deleted file mode 100644
index 603ba24..0000000
--- a/liblog/logd_reader.c
+++ /dev/null
@@ -1,660 +0,0 @@
-/*
- * Copyright (C) 2007-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 <endian.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <poll.h>
-#include <stdarg.h>
-#include <stdatomic.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <cutils/sockets.h>
-#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
-
-#include "config_read.h"
-#include "log_portability.h"
-#include "logd_reader.h"
-#include "logger.h"
-
-/* branchless on many architectures. */
-#define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
-
-static int logdAvailable(log_id_t LogId);
-static int logdVersion(struct android_log_logger* logger,
-                       struct android_log_transport_context* transp);
-static int logdRead(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp,
-                    struct log_msg* log_msg);
-static int logdPoll(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp);
-static void logdClose(struct android_log_logger_list* logger_list,
-                      struct android_log_transport_context* transp);
-static int logdClear(struct android_log_logger* logger,
-                     struct android_log_transport_context* transp);
-static ssize_t logdSetSize(struct android_log_logger* logger,
-                           struct android_log_transport_context* transp,
-                           size_t size);
-static ssize_t logdGetSize(struct android_log_logger* logger,
-                           struct android_log_transport_context* transp);
-static ssize_t logdGetReadableSize(struct android_log_logger* logger,
-                                   struct android_log_transport_context* transp);
-static ssize_t logdGetPrune(struct android_log_logger_list* logger,
-                            struct android_log_transport_context* transp,
-                            char* buf, size_t len);
-static ssize_t logdSetPrune(struct android_log_logger_list* logger,
-                            struct android_log_transport_context* transp,
-                            char* buf, size_t len);
-static ssize_t logdGetStats(struct android_log_logger_list* logger,
-                            struct android_log_transport_context* transp,
-                            char* buf, size_t len);
-
-LIBLOG_HIDDEN struct android_log_transport_read logdLoggerRead = {
-  .node = { &logdLoggerRead.node, &logdLoggerRead.node },
-  .name = "logd",
-  .available = logdAvailable,
-  .version = logdVersion,
-  .read = logdRead,
-  .poll = logdPoll,
-  .close = logdClose,
-  .clear = logdClear,
-  .getSize = logdGetSize,
-  .setSize = logdSetSize,
-  .getReadableSize = logdGetReadableSize,
-  .getPrune = logdGetPrune,
-  .setPrune = logdSetPrune,
-  .getStats = logdGetStats,
-};
-
-static int logdAvailable(log_id_t logId) {
-  if (logId >= LOG_ID_MAX) {
-    return -EINVAL;
-  }
-  if (logId == LOG_ID_SECURITY) {
-    uid_t uid = __android_log_uid();
-    if (uid != AID_SYSTEM) {
-      return -EPERM;
-    }
-  }
-  if (access("/dev/socket/logdw", W_OK) == 0) {
-    return 0;
-  }
-  return -EBADF;
-}
-
-/* Private copy of ../libcutils/socket_local_client.c prevent library loops */
-
-#if defined(_WIN32)
-
-LIBLOG_WEAK int socket_local_client(const char* name, int namespaceId,
-                                    int type) {
-  errno = ENOSYS;
-  return -ENOSYS;
-}
-
-#else /* !_WIN32 */
-
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/un.h>
-
-/* Private copy of ../libcutils/socket_local.h prevent library loops */
-#define FILESYSTEM_SOCKET_PREFIX "/tmp/"
-#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
-/* End of ../libcutils/socket_local.h */
-
-#define LISTEN_BACKLOG 4
-
-/* Documented in header file. */
-LIBLOG_WEAK 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
- */
-LIBLOG_WEAK 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
- */
-LIBLOG_WEAK 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 */
-/* End of ../libcutils/socket_local_client.c */
-
-/* worker for sending the command to the logger */
-static ssize_t send_log_msg(struct android_log_logger* logger, const char* msg,
-                            char* buf, size_t buf_size) {
-  ssize_t ret;
-  size_t len;
-  char* cp;
-  int errno_save = 0;
-  int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
-                                 SOCK_STREAM);
-  if (sock < 0) {
-    return sock;
-  }
-
-  if (msg) {
-    snprintf(buf, buf_size, msg, logger ? logger->logId : (unsigned)-1);
-  }
-
-  len = strlen(buf) + 1;
-  ret = TEMP_FAILURE_RETRY(write(sock, buf, len));
-  if (ret <= 0) {
-    goto done;
-  }
-
-  len = buf_size;
-  cp = buf;
-  while ((ret = TEMP_FAILURE_RETRY(read(sock, cp, len))) > 0) {
-    struct pollfd p;
-
-    if (((size_t)ret == len) || (buf_size < PAGE_SIZE)) {
-      break;
-    }
-
-    len -= ret;
-    cp += ret;
-
-    memset(&p, 0, sizeof(p));
-    p.fd = sock;
-    p.events = POLLIN;
-
-    /* Give other side 20ms to refill pipe */
-    ret = TEMP_FAILURE_RETRY(poll(&p, 1, 20));
-
-    if (ret <= 0) {
-      break;
-    }
-
-    if (!(p.revents & POLLIN)) {
-      ret = 0;
-      break;
-    }
-  }
-
-  if (ret >= 0) {
-    ret += buf_size - len;
-  }
-
-done:
-  if ((ret == -1) && errno) {
-    errno_save = errno;
-  }
-  close(sock);
-  if (errno_save) {
-    errno = errno_save;
-  }
-  return ret;
-}
-
-LIBLOG_HIDDEN ssize_t __send_log_msg(char* buf, size_t buf_size) {
-  return send_log_msg(NULL, NULL, buf, buf_size);
-}
-
-static int check_log_success(char* buf, ssize_t ret) {
-  if (ret < 0) {
-    return ret;
-  }
-
-  if (strncmp(buf, "success", 7)) {
-    errno = EINVAL;
-    return -1;
-  }
-
-  return 0;
-}
-
-static int logdClear(struct android_log_logger* logger,
-                     struct android_log_transport_context* transp __unused) {
-  char buf[512];
-
-  return check_log_success(buf,
-                           send_log_msg(logger, "clear %d", buf, sizeof(buf)));
-}
-
-/* returns the total size of the log's ring buffer */
-static ssize_t logdGetSize(struct android_log_logger* logger,
-                           struct android_log_transport_context* transp __unused) {
-  char buf[512];
-
-  ssize_t ret = send_log_msg(logger, "getLogSize %d", buf, sizeof(buf));
-  if (ret < 0) {
-    return ret;
-  }
-
-  if ((buf[0] < '0') || ('9' < buf[0])) {
-    return -1;
-  }
-
-  return atol(buf);
-}
-
-static ssize_t logdSetSize(struct android_log_logger* logger,
-                           struct android_log_transport_context* transp __unused,
-                           size_t size) {
-  char buf[512];
-
-  snprintf(buf, sizeof(buf), "setLogSize %d %zu", logger->logId, size);
-
-  return check_log_success(buf, send_log_msg(NULL, NULL, buf, sizeof(buf)));
-}
-
-/*
- * returns the readable size of the log's ring buffer (that is, amount of the
- * log consumed)
- */
-static ssize_t logdGetReadableSize(struct android_log_logger* logger,
-                                   struct android_log_transport_context* transp
-                                       __unused) {
-  char buf[512];
-
-  ssize_t ret = send_log_msg(logger, "getLogSizeUsed %d", buf, sizeof(buf));
-  if (ret < 0) {
-    return ret;
-  }
-
-  if ((buf[0] < '0') || ('9' < buf[0])) {
-    return -1;
-  }
-
-  return atol(buf);
-}
-
-/*
- * returns the logger version
- */
-static int logdVersion(struct android_log_logger* logger __unused,
-                       struct android_log_transport_context* transp __unused) {
-  uid_t uid = __android_log_uid();
-  return ((uid != AID_ROOT) && (uid != AID_LOG) && (uid != AID_SYSTEM)) ? 3 : 4;
-}
-
-/*
- * returns statistics
- */
-static ssize_t logdGetStats(struct android_log_logger_list* logger_list,
-                            struct android_log_transport_context* transp __unused,
-                            char* buf, size_t len) {
-  struct android_log_logger* logger;
-  char* cp = buf;
-  size_t remaining = len;
-  size_t n;
-
-  n = snprintf(cp, remaining, "getStatistics");
-  n = min(n, remaining);
-  remaining -= n;
-  cp += n;
-
-  logger_for_each(logger, logger_list) {
-    n = snprintf(cp, remaining, " %d", logger->logId);
-    n = min(n, remaining);
-    remaining -= n;
-    cp += n;
-  }
-
-  if (logger_list->pid) {
-    snprintf(cp, remaining, " pid=%u", logger_list->pid);
-  }
-
-  return send_log_msg(NULL, NULL, buf, len);
-}
-
-static ssize_t logdGetPrune(struct android_log_logger_list* logger_list __unused,
-                            struct android_log_transport_context* transp __unused,
-                            char* buf, size_t len) {
-  return send_log_msg(NULL, "getPruneList", buf, len);
-}
-
-static ssize_t logdSetPrune(struct android_log_logger_list* logger_list __unused,
-                            struct android_log_transport_context* transp __unused,
-                            char* buf, size_t len) {
-  const char cmd[] = "setPruneList ";
-  const size_t cmdlen = sizeof(cmd) - 1;
-
-  if (strlen(buf) > (len - cmdlen)) {
-    return -ENOMEM; /* KISS */
-  }
-  memmove(buf + cmdlen, buf, len - cmdlen);
-  buf[len - 1] = '\0';
-  memcpy(buf, cmd, cmdlen);
-
-  return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
-}
-
-static void caught_signal(int signum __unused) {
-}
-
-static int logdOpen(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp) {
-  struct android_log_logger* logger;
-  struct sigaction ignore;
-  struct sigaction old_sigaction;
-  unsigned int old_alarm = 0;
-  char buffer[256], *cp, c;
-  int e, ret, remaining, sock;
-
-  if (!logger_list) {
-    return -EINVAL;
-  }
-
-  sock = atomic_load(&transp->context.sock);
-  if (sock > 0) {
-    return sock;
-  }
-
-  sock = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED,
-                             SOCK_SEQPACKET);
-  if (sock == 0) {
-    /* Guarantee not file descriptor zero */
-    int newsock = socket_local_client(
-        "logdr", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET);
-    close(sock);
-    sock = newsock;
-  }
-  if (sock <= 0) {
-    if ((sock == -1) && errno) {
-      return -errno;
-    }
-    return sock;
-  }
-
-  strcpy(buffer, (logger_list->mode & ANDROID_LOG_NONBLOCK) ? "dumpAndClose"
-                                                            : "stream");
-  cp = buffer + strlen(buffer);
-
-  strcpy(cp, " lids");
-  cp += 5;
-  c = '=';
-  remaining = sizeof(buffer) - (cp - buffer);
-  logger_for_each(logger, logger_list) {
-    ret = snprintf(cp, remaining, "%c%u", c, logger->logId);
-    ret = min(ret, remaining);
-    remaining -= ret;
-    cp += ret;
-    c = ',';
-  }
-
-  if (logger_list->tail) {
-    ret = snprintf(cp, remaining, " tail=%u", logger_list->tail);
-    ret = min(ret, remaining);
-    remaining -= ret;
-    cp += ret;
-  }
-
-  if (logger_list->start.tv_sec || logger_list->start.tv_nsec) {
-    if (logger_list->mode & ANDROID_LOG_WRAP) {
-      // ToDo: alternate API to allow timeout to be adjusted.
-      ret = snprintf(cp, remaining, " timeout=%u",
-                     ANDROID_LOG_WRAP_DEFAULT_TIMEOUT);
-      ret = min(ret, remaining);
-      remaining -= ret;
-      cp += ret;
-    }
-    ret = snprintf(cp, remaining, " start=%" PRIu32 ".%09" PRIu32,
-                   logger_list->start.tv_sec, logger_list->start.tv_nsec);
-    ret = min(ret, remaining);
-    remaining -= ret;
-    cp += ret;
-  }
-
-  if (logger_list->pid) {
-    ret = snprintf(cp, remaining, " pid=%u", logger_list->pid);
-    ret = min(ret, remaining);
-    cp += ret;
-  }
-
-  if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
-    /* Deal with an unresponsive logd */
-    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);
-  }
-  ret = write(sock, buffer, cp - buffer);
-  e = errno;
-  if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
-    if (e == EINTR) {
-      e = ETIMEDOUT;
-    }
-    alarm(old_alarm);
-    sigaction(SIGALRM, &old_sigaction, NULL);
-  }
-
-  if (ret <= 0) {
-    close(sock);
-    if ((ret == -1) && e) {
-      return -e;
-    }
-    if (ret == 0) {
-      return -EIO;
-    }
-    return ret;
-  }
-
-  ret = atomic_exchange(&transp->context.sock, sock);
-  if ((ret > 0) && (ret != sock)) {
-    close(ret);
-  }
-  return sock;
-}
-
-/* Read from the selected logs */
-static int logdRead(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp,
-                    struct log_msg* log_msg) {
-  int ret, e;
-  struct sigaction ignore;
-  struct sigaction old_sigaction;
-  unsigned int old_alarm = 0;
-
-  ret = logdOpen(logger_list, transp);
-  if (ret < 0) {
-    return ret;
-  }
-
-  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(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 (new_alarm) {
-    if ((ret == 0) || (e == EINTR)) {
-      e = EAGAIN;
-      ret = -1;
-    }
-    alarm(old_alarm);
-    sigaction(SIGALRM, &old_sigaction, NULL);
-  }
-
-  if ((ret == -1) && e) {
-    return -e;
-  }
-  return ret;
-}
-
-static int logdPoll(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp) {
-  struct pollfd p;
-
-  int ret = logdOpen(logger_list, transp);
-  if (ret < 0) {
-    return ret;
-  }
-
-  memset(&p, 0, sizeof(p));
-  p.fd = ret;
-  p.events = POLLIN;
-  ret = poll(&p, 1, 20);
-  if ((ret > 0) && !(p.revents & POLLIN)) {
-    ret = 0;
-  }
-  if ((ret == -1) && errno) {
-    return -errno;
-  }
-  return ret;
-}
-
-/* Close all the logs */
-static void logdClose(struct android_log_logger_list* logger_list __unused,
-                      struct android_log_transport_context* transp) {
-  int sock = atomic_exchange(&transp->context.sock, -1);
-  if (sock > 0) {
-    close(sock);
-  }
-}
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
new file mode 100644
index 0000000..b7ba782
--- /dev/null
+++ b/liblog/logd_reader.cpp
@@ -0,0 +1,520 @@
+/*
+ * Copyright (C) 2007-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 <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <stdatomic.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <cutils/sockets.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "config_read.h"
+#include "log_portability.h"
+#include "logd_reader.h"
+#include "logger.h"
+
+/* branchless on many architectures. */
+#define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
+
+static int logdAvailable(log_id_t LogId);
+static int logdVersion(struct android_log_logger* logger,
+                       struct android_log_transport_context* transp);
+static int logdRead(struct android_log_logger_list* logger_list,
+                    struct android_log_transport_context* transp, struct log_msg* log_msg);
+static int logdPoll(struct android_log_logger_list* logger_list,
+                    struct android_log_transport_context* transp);
+static void logdClose(struct android_log_logger_list* logger_list,
+                      struct android_log_transport_context* transp);
+static int logdClear(struct android_log_logger* logger,
+                     struct android_log_transport_context* transp);
+static ssize_t logdSetSize(struct android_log_logger* logger,
+                           struct android_log_transport_context* transp, size_t size);
+static ssize_t logdGetSize(struct android_log_logger* logger,
+                           struct android_log_transport_context* transp);
+static ssize_t logdGetReadableSize(struct android_log_logger* logger,
+                                   struct android_log_transport_context* transp);
+static ssize_t logdGetPrune(struct android_log_logger_list* logger,
+                            struct android_log_transport_context* transp, char* buf, size_t len);
+static ssize_t logdSetPrune(struct android_log_logger_list* logger,
+                            struct android_log_transport_context* transp, char* buf, size_t len);
+static ssize_t logdGetStats(struct android_log_logger_list* logger,
+                            struct android_log_transport_context* transp, char* buf, size_t len);
+
+struct android_log_transport_read logdLoggerRead = {
+    .node = {&logdLoggerRead.node, &logdLoggerRead.node},
+    .name = "logd",
+    .available = logdAvailable,
+    .version = logdVersion,
+    .read = logdRead,
+    .poll = logdPoll,
+    .close = logdClose,
+    .clear = logdClear,
+    .getSize = logdGetSize,
+    .setSize = logdSetSize,
+    .getReadableSize = logdGetReadableSize,
+    .getPrune = logdGetPrune,
+    .setPrune = logdSetPrune,
+    .getStats = logdGetStats,
+};
+
+static int logdAvailable(log_id_t logId) {
+  if (logId >= LOG_ID_MAX) {
+    return -EINVAL;
+  }
+  if (logId == LOG_ID_SECURITY) {
+    uid_t uid = __android_log_uid();
+    if (uid != AID_SYSTEM) {
+      return -EPERM;
+    }
+  }
+  if (access("/dev/socket/logdw", W_OK) == 0) {
+    return 0;
+  }
+  return -EBADF;
+}
+
+// Connects to /dev/socket/<name> and returns the associated fd or returns -1 on error.
+// O_CLOEXEC is always set.
+static int socket_local_client(const std::string& name, int type) {
+  sockaddr_un addr = {.sun_family = AF_LOCAL};
+
+  std::string path = "/dev/socket/" + name;
+  if (path.size() + 1 > sizeof(addr.sun_path)) {
+    return -1;
+  }
+  strlcpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path));
+
+  int fd = socket(AF_LOCAL, type | SOCK_CLOEXEC, 0);
+  if (fd == 0) {
+    return -1;
+  }
+
+  if (connect(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
+    close(fd);
+    return -1;
+  }
+
+  return fd;
+}
+
+/* worker for sending the command to the logger */
+static ssize_t send_log_msg(struct android_log_logger* logger, const char* msg, char* buf,
+                            size_t buf_size) {
+  ssize_t ret;
+  size_t len;
+  char* cp;
+  int errno_save = 0;
+  int sock = socket_local_client("logd", SOCK_STREAM);
+  if (sock < 0) {
+    return sock;
+  }
+
+  if (msg) {
+    snprintf(buf, buf_size, msg, logger ? logger->logId : (unsigned)-1);
+  }
+
+  len = strlen(buf) + 1;
+  ret = TEMP_FAILURE_RETRY(write(sock, buf, len));
+  if (ret <= 0) {
+    goto done;
+  }
+
+  len = buf_size;
+  cp = buf;
+  while ((ret = TEMP_FAILURE_RETRY(read(sock, cp, len))) > 0) {
+    struct pollfd p;
+
+    if (((size_t)ret == len) || (buf_size < PAGE_SIZE)) {
+      break;
+    }
+
+    len -= ret;
+    cp += ret;
+
+    memset(&p, 0, sizeof(p));
+    p.fd = sock;
+    p.events = POLLIN;
+
+    /* Give other side 20ms to refill pipe */
+    ret = TEMP_FAILURE_RETRY(poll(&p, 1, 20));
+
+    if (ret <= 0) {
+      break;
+    }
+
+    if (!(p.revents & POLLIN)) {
+      ret = 0;
+      break;
+    }
+  }
+
+  if (ret >= 0) {
+    ret += buf_size - len;
+  }
+
+done:
+  if ((ret == -1) && errno) {
+    errno_save = errno;
+  }
+  close(sock);
+  if (errno_save) {
+    errno = errno_save;
+  }
+  return ret;
+}
+
+ssize_t __send_log_msg(char* buf, size_t buf_size) {
+  return send_log_msg(NULL, NULL, buf, buf_size);
+}
+
+static int check_log_success(char* buf, ssize_t ret) {
+  if (ret < 0) {
+    return ret;
+  }
+
+  if (strncmp(buf, "success", 7)) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  return 0;
+}
+
+static int logdClear(struct android_log_logger* logger,
+                     struct android_log_transport_context* transp __unused) {
+  char buf[512];
+
+  return check_log_success(buf, send_log_msg(logger, "clear %d", buf, sizeof(buf)));
+}
+
+/* returns the total size of the log's ring buffer */
+static ssize_t logdGetSize(struct android_log_logger* logger,
+                           struct android_log_transport_context* transp __unused) {
+  char buf[512];
+
+  ssize_t ret = send_log_msg(logger, "getLogSize %d", buf, sizeof(buf));
+  if (ret < 0) {
+    return ret;
+  }
+
+  if ((buf[0] < '0') || ('9' < buf[0])) {
+    return -1;
+  }
+
+  return atol(buf);
+}
+
+static ssize_t logdSetSize(struct android_log_logger* logger,
+                           struct android_log_transport_context* transp __unused, size_t size) {
+  char buf[512];
+
+  snprintf(buf, sizeof(buf), "setLogSize %d %zu", logger->logId, size);
+
+  return check_log_success(buf, send_log_msg(NULL, NULL, buf, sizeof(buf)));
+}
+
+/*
+ * returns the readable size of the log's ring buffer (that is, amount of the
+ * log consumed)
+ */
+static ssize_t logdGetReadableSize(struct android_log_logger* logger,
+                                   struct android_log_transport_context* transp __unused) {
+  char buf[512];
+
+  ssize_t ret = send_log_msg(logger, "getLogSizeUsed %d", buf, sizeof(buf));
+  if (ret < 0) {
+    return ret;
+  }
+
+  if ((buf[0] < '0') || ('9' < buf[0])) {
+    return -1;
+  }
+
+  return atol(buf);
+}
+
+/*
+ * returns the logger version
+ */
+static int logdVersion(struct android_log_logger* logger __unused,
+                       struct android_log_transport_context* transp __unused) {
+  uid_t uid = __android_log_uid();
+  return ((uid != AID_ROOT) && (uid != AID_LOG) && (uid != AID_SYSTEM)) ? 3 : 4;
+}
+
+/*
+ * returns statistics
+ */
+static ssize_t logdGetStats(struct android_log_logger_list* logger_list,
+                            struct android_log_transport_context* transp __unused, char* buf,
+                            size_t len) {
+  struct android_log_logger* logger;
+  char* cp = buf;
+  size_t remaining = len;
+  size_t n;
+
+  n = snprintf(cp, remaining, "getStatistics");
+  n = min(n, remaining);
+  remaining -= n;
+  cp += n;
+
+  logger_for_each(logger, logger_list) {
+    n = snprintf(cp, remaining, " %d", logger->logId);
+    n = min(n, remaining);
+    remaining -= n;
+    cp += n;
+  }
+
+  if (logger_list->pid) {
+    snprintf(cp, remaining, " pid=%u", logger_list->pid);
+  }
+
+  return send_log_msg(NULL, NULL, buf, len);
+}
+
+static ssize_t logdGetPrune(struct android_log_logger_list* logger_list __unused,
+                            struct android_log_transport_context* transp __unused, char* buf,
+                            size_t len) {
+  return send_log_msg(NULL, "getPruneList", buf, len);
+}
+
+static ssize_t logdSetPrune(struct android_log_logger_list* logger_list __unused,
+                            struct android_log_transport_context* transp __unused, char* buf,
+                            size_t len) {
+  const char cmd[] = "setPruneList ";
+  const size_t cmdlen = sizeof(cmd) - 1;
+
+  if (strlen(buf) > (len - cmdlen)) {
+    return -ENOMEM; /* KISS */
+  }
+  memmove(buf + cmdlen, buf, len - cmdlen);
+  buf[len - 1] = '\0';
+  memcpy(buf, cmd, cmdlen);
+
+  return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
+}
+
+static void caught_signal(int signum __unused) {}
+
+static int logdOpen(struct android_log_logger_list* logger_list,
+                    struct android_log_transport_context* transp) {
+  struct android_log_logger* logger;
+  struct sigaction ignore;
+  struct sigaction old_sigaction;
+  unsigned int old_alarm = 0;
+  char buffer[256], *cp, c;
+  int e, ret, remaining, sock;
+
+  if (!logger_list) {
+    return -EINVAL;
+  }
+
+  sock = atomic_load(&transp->context.sock);
+  if (sock > 0) {
+    return sock;
+  }
+
+  sock = socket_local_client("logdr", SOCK_SEQPACKET);
+  if (sock == 0) {
+    /* Guarantee not file descriptor zero */
+    int newsock = socket_local_client("logdr", SOCK_SEQPACKET);
+    close(sock);
+    sock = newsock;
+  }
+  if (sock <= 0) {
+    if ((sock == -1) && errno) {
+      return -errno;
+    }
+    return sock;
+  }
+
+  strcpy(buffer, (logger_list->mode & ANDROID_LOG_NONBLOCK) ? "dumpAndClose" : "stream");
+  cp = buffer + strlen(buffer);
+
+  strcpy(cp, " lids");
+  cp += 5;
+  c = '=';
+  remaining = sizeof(buffer) - (cp - buffer);
+  logger_for_each(logger, logger_list) {
+    ret = snprintf(cp, remaining, "%c%u", c, logger->logId);
+    ret = min(ret, remaining);
+    remaining -= ret;
+    cp += ret;
+    c = ',';
+  }
+
+  if (logger_list->tail) {
+    ret = snprintf(cp, remaining, " tail=%u", logger_list->tail);
+    ret = min(ret, remaining);
+    remaining -= ret;
+    cp += ret;
+  }
+
+  if (logger_list->start.tv_sec || logger_list->start.tv_nsec) {
+    if (logger_list->mode & ANDROID_LOG_WRAP) {
+      // ToDo: alternate API to allow timeout to be adjusted.
+      ret = snprintf(cp, remaining, " timeout=%u", ANDROID_LOG_WRAP_DEFAULT_TIMEOUT);
+      ret = min(ret, remaining);
+      remaining -= ret;
+      cp += ret;
+    }
+    ret = snprintf(cp, remaining, " start=%" PRIu32 ".%09" PRIu32, logger_list->start.tv_sec,
+                   logger_list->start.tv_nsec);
+    ret = min(ret, remaining);
+    remaining -= ret;
+    cp += ret;
+  }
+
+  if (logger_list->pid) {
+    ret = snprintf(cp, remaining, " pid=%u", logger_list->pid);
+    ret = min(ret, remaining);
+    cp += ret;
+  }
+
+  if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+    /* Deal with an unresponsive logd */
+    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);
+  }
+  ret = write(sock, buffer, cp - buffer);
+  e = errno;
+  if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+    if (e == EINTR) {
+      e = ETIMEDOUT;
+    }
+    alarm(old_alarm);
+    sigaction(SIGALRM, &old_sigaction, NULL);
+  }
+
+  if (ret <= 0) {
+    close(sock);
+    if ((ret == -1) && e) {
+      return -e;
+    }
+    if (ret == 0) {
+      return -EIO;
+    }
+    return ret;
+  }
+
+  ret = atomic_exchange(&transp->context.sock, sock);
+  if ((ret > 0) && (ret != sock)) {
+    close(ret);
+  }
+  return sock;
+}
+
+/* Read from the selected logs */
+static int logdRead(struct android_log_logger_list* logger_list,
+                    struct android_log_transport_context* transp, struct log_msg* log_msg) {
+  int ret, e;
+  struct sigaction ignore;
+  struct sigaction old_sigaction;
+  unsigned int old_alarm = 0;
+
+  ret = logdOpen(logger_list, transp);
+  if (ret < 0) {
+    return ret;
+  }
+
+  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(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 (new_alarm) {
+    if ((ret == 0) || (e == EINTR)) {
+      e = EAGAIN;
+      ret = -1;
+    }
+    alarm(old_alarm);
+    sigaction(SIGALRM, &old_sigaction, NULL);
+  }
+
+  if ((ret == -1) && e) {
+    return -e;
+  }
+  return ret;
+}
+
+static int logdPoll(struct android_log_logger_list* logger_list,
+                    struct android_log_transport_context* transp) {
+  struct pollfd p;
+
+  int ret = logdOpen(logger_list, transp);
+  if (ret < 0) {
+    return ret;
+  }
+
+  memset(&p, 0, sizeof(p));
+  p.fd = ret;
+  p.events = POLLIN;
+  ret = poll(&p, 1, 20);
+  if ((ret > 0) && !(p.revents & POLLIN)) {
+    ret = 0;
+  }
+  if ((ret == -1) && errno) {
+    return -errno;
+  }
+  return ret;
+}
+
+/* Close all the logs */
+static void logdClose(struct android_log_logger_list* logger_list __unused,
+                      struct android_log_transport_context* transp) {
+  int sock = atomic_exchange(&transp->context.sock, -1);
+  if (sock > 0) {
+    close(sock);
+  }
+}
diff --git a/liblog/logd_reader.h b/liblog/logd_reader.h
index 8ebb1ae..7c53cbb 100644
--- a/liblog/logd_reader.h
+++ b/liblog/logd_reader.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBLOG_LOGD_READER_H__
-#define _LIBLOG_LOGD_READER_H__
+#pragma once
 
 #include <unistd.h>
 
@@ -23,8 +22,6 @@
 
 __BEGIN_DECLS
 
-LIBLOG_HIDDEN ssize_t __send_log_msg(char* buf, size_t buf_size);
+ssize_t __send_log_msg(char* buf, size_t buf_size);
 
 __END_DECLS
-
-#endif /* _LIBLOG_LOGD_READER_H__ */
diff --git a/liblog/logd_writer.c b/liblog/logd_writer.c
deleted file mode 100644
index e0e3eca..0000000
--- a/liblog/logd_writer.c
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * Copyright (C) 2007-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 <endian.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <poll.h>
-#include <stdarg.h>
-#include <stdatomic.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <cutils/sockets.h>
-#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
-
-#include "config_write.h"
-#include "log_portability.h"
-#include "logger.h"
-
-/* branchless on many architectures. */
-#define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
-
-static int logdAvailable(log_id_t LogId);
-static int logdOpen();
-static void logdClose();
-static int logdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec,
-                     size_t nr);
-
-LIBLOG_HIDDEN struct android_log_transport_write logdLoggerWrite = {
-  .node = { &logdLoggerWrite.node, &logdLoggerWrite.node },
-  .context.sock = -EBADF,
-  .name = "logd",
-  .available = logdAvailable,
-  .open = logdOpen,
-  .close = logdClose,
-  .write = logdWrite,
-};
-
-/* log_init_lock assumed */
-static int logdOpen() {
-  int i, ret = 0;
-
-  i = atomic_load(&logdLoggerWrite.context.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/logdw");
-
-      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(&logdLoggerWrite.context.sock, ret);
-          /* FALLTHRU */
-          default:
-            break;
-        }
-        close(sock);
-      } else {
-        ret = atomic_exchange(&logdLoggerWrite.context.sock, sock);
-        if ((ret >= 0) && (ret != sock)) {
-          close(ret);
-        }
-        ret = 0;
-      }
-    }
-  }
-
-  return ret;
-}
-
-static void __logdClose(int negative_errno) {
-  int sock = atomic_exchange(&logdLoggerWrite.context.sock, negative_errno);
-  if (sock >= 0) {
-    close(sock);
-  }
-}
-
-static void logdClose() {
-  __logdClose(-EBADF);
-}
-
-static int logdAvailable(log_id_t logId) {
-  if (logId >= LOG_ID_MAX || logId == LOG_ID_KERNEL) {
-    return -EINVAL;
-  }
-  if (atomic_load(&logdLoggerWrite.context.sock) < 0) {
-    if (access("/dev/socket/logdw", W_OK) == 0) {
-      return 0;
-    }
-    return -EBADF;
-  }
-  return 1;
-}
-
-static int logdWrite(log_id_t logId, 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_fast32_t dropped;
-  static atomic_int_fast32_t droppedSecurity;
-
-  sock = atomic_load(&logdLoggerWrite.context.sock);
-  if (sock < 0) switch (sock) {
-      case -ENOTCONN:
-      case -ECONNREFUSED:
-      case -ENOENT:
-        break;
-      default:
-        return -EBADF;
-    }
-
-  /* logd, after initialization and priv drop */
-  if (__android_log_uid() == AID_LOGD) {
-    /*
-     * ignore log messages we send to ourself (logd).
-     * Such log messages are often generated by libraries we depend on
-     * which use standard Android logging.
-     */
-    return 0;
-  }
-
-  /*
-   *  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 (sock >= 0) {
-    int32_t snapshot =
-        atomic_exchange_explicit(&droppedSecurity, 0, memory_order_relaxed);
-    if (snapshot) {
-      android_log_event_int_t buffer;
-
-      header.id = LOG_ID_SECURITY;
-      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(&droppedSecurity, snapshot,
-                                  memory_order_relaxed);
-      }
-    }
-    snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
-    if (snapshot &&
-        __android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog",
-                                      strlen("liblog"), ANDROID_LOG_VERBOSE)) {
-      android_log_event_int_t buffer;
-
-      header.id = LOG_ID_EVENTS;
-      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 = logId;
-
-  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 logd has died.
-   * ENOENT occurs if logd is not running and socket is missing.
-   * ECONNREFUSED occurs if we can not reconnect to logd.
-   * EAGAIN occurs if logd 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 (__android_log_trylock()) {
-        return ret; /* in a signal handler? try again when less stressed */
-      }
-      __logdClose(ret);
-      ret = logdOpen();
-      __android_log_unlock();
-
-      if (ret < 0) {
-        return ret;
-      }
-
-      ret = TEMP_FAILURE_RETRY(
-          writev(atomic_load(&logdLoggerWrite.context.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);
-    if (logId == LOG_ID_SECURITY) {
-      atomic_fetch_add_explicit(&droppedSecurity, 1, memory_order_relaxed);
-    }
-  }
-
-  return ret;
-}
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
new file mode 100644
index 0000000..c3f72f4
--- /dev/null
+++ b/liblog/logd_writer.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2007-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 <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <stdatomic.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <cutils/sockets.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "config_write.h"
+#include "log_portability.h"
+#include "logger.h"
+#include "uio.h"
+
+/* branchless on many architectures. */
+#define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
+
+static int logdAvailable(log_id_t LogId);
+static int logdOpen();
+static void logdClose();
+static int logdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
+
+struct android_log_transport_write logdLoggerWrite = {
+    .node = {&logdLoggerWrite.node, &logdLoggerWrite.node},
+    .context.sock = -EBADF,
+    .name = "logd",
+    .available = logdAvailable,
+    .open = logdOpen,
+    .close = logdClose,
+    .write = logdWrite,
+};
+
+/* log_init_lock assumed */
+static int logdOpen() {
+  int i, ret = 0;
+
+  i = atomic_load(&logdLoggerWrite.context.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/logdw");
+
+      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(&logdLoggerWrite.context.sock, ret);
+            [[fallthrough]];
+          default:
+            break;
+        }
+        close(sock);
+      } else {
+        ret = atomic_exchange(&logdLoggerWrite.context.sock, sock);
+        if ((ret >= 0) && (ret != sock)) {
+          close(ret);
+        }
+        ret = 0;
+      }
+    }
+  }
+
+  return ret;
+}
+
+static void __logdClose(int negative_errno) {
+  int sock = atomic_exchange(&logdLoggerWrite.context.sock, negative_errno);
+  if (sock >= 0) {
+    close(sock);
+  }
+}
+
+static void logdClose() {
+  __logdClose(-EBADF);
+}
+
+static int logdAvailable(log_id_t logId) {
+  if (logId >= LOG_ID_MAX || logId == LOG_ID_KERNEL) {
+    return -EINVAL;
+  }
+  if (atomic_load(&logdLoggerWrite.context.sock) < 0) {
+    if (access("/dev/socket/logdw", W_OK) == 0) {
+      return 0;
+    }
+    return -EBADF;
+  }
+  return 1;
+}
+
+static int logdWrite(log_id_t logId, 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;
+  static atomic_int droppedSecurity;
+
+  sock = atomic_load(&logdLoggerWrite.context.sock);
+  if (sock < 0) switch (sock) {
+      case -ENOTCONN:
+      case -ECONNREFUSED:
+      case -ENOENT:
+        break;
+      default:
+        return -EBADF;
+    }
+
+  /* logd, after initialization and priv drop */
+  if (__android_log_uid() == AID_LOGD) {
+    /*
+     * ignore log messages we send to ourself (logd).
+     * Such log messages are often generated by libraries we depend on
+     * which use standard Android logging.
+     */
+    return 0;
+  }
+
+  /*
+   *  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 (sock >= 0) {
+    int32_t snapshot = atomic_exchange_explicit(&droppedSecurity, 0, memory_order_relaxed);
+    if (snapshot) {
+      android_log_event_int_t buffer;
+
+      header.id = LOG_ID_SECURITY;
+      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(&droppedSecurity, snapshot, memory_order_relaxed);
+      }
+    }
+    snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
+    if (snapshot && __android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog", strlen("liblog"),
+                                                  ANDROID_LOG_VERBOSE)) {
+      android_log_event_int_t buffer;
+
+      header.id = LOG_ID_EVENTS;
+      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 = logId;
+
+  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 logd has died.
+   * ENOENT occurs if logd is not running and socket is missing.
+   * ECONNREFUSED occurs if we can not reconnect to logd.
+   * EAGAIN occurs if logd 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 (__android_log_trylock()) {
+        return ret; /* in a signal handler? try again when less stressed */
+      }
+      __logdClose(ret);
+      ret = logdOpen();
+      __android_log_unlock();
+
+      if (ret < 0) {
+        return ret;
+      }
+
+      ret = TEMP_FAILURE_RETRY(writev(atomic_load(&logdLoggerWrite.context.sock), newVec, i));
+      if (ret < 0) {
+        ret = -errno;
+      }
+      [[fallthrough]];
+    default:
+      break;
+  }
+
+  if (ret > (ssize_t)sizeof(header)) {
+    ret -= sizeof(header);
+  } else if (ret == -EAGAIN) {
+    atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+    if (logId == LOG_ID_SECURITY) {
+      atomic_fetch_add_explicit(&droppedSecurity, 1, memory_order_relaxed);
+    }
+  }
+
+  return ret;
+}
diff --git a/liblog/logger.h b/liblog/logger.h
index 246b33c..1f632c0 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -14,22 +14,21 @@
  * limitations under the License.
  */
 
-#ifndef _LIBLOG_LOGGER_H__
-#define _LIBLOG_LOGGER_H__
+#pragma once
 
 #include <stdatomic.h>
 #include <stdbool.h>
 
 #include <cutils/list.h>
 #include <log/log.h>
-#include <log/uio.h>
 
 #include "log_portability.h"
+#include "uio.h"
 
 __BEGIN_DECLS
 
 /* Union, sock or fd of zero is not allowed unless static initialized */
-union android_log_context {
+union android_log_context_union {
   void* priv;
   atomic_int sock;
   atomic_int fd;
@@ -41,7 +40,7 @@
   struct listnode node;
   const char* name;                  /* human name to describe the transport */
   unsigned logMask;                  /* mask cache of available() success */
-  union android_log_context context; /* Initialized by static allocation */
+  union android_log_context_union context; /* Initialized by static allocation */
 
   int (*available)(log_id_t logId); /* Does not cause resources to be taken */
   int (*open)();   /* can be called multiple times, reusing current resources */
@@ -98,7 +97,6 @@
 };
 
 struct android_log_logger_list {
-  struct listnode node;
   struct listnode logger;
   struct listnode transport;
   int mode;
@@ -116,7 +114,7 @@
 
 struct android_log_transport_context {
   struct listnode node;
-  union android_log_context context; /* zero init per-transport context */
+  union android_log_context_union context; /* zero init per-transport context */
   struct android_log_logger_list* parent;
 
   struct android_log_transport_read* transport;
@@ -144,37 +142,6 @@
        (logp) =                                                     \
            node_to_item((logp)->node.next, struct android_log_logger, node))
 
-/*
- *    Global list of log readers.
- *
- * Usage case: search out transport contexts for all readers
- */
-
-LIBLOG_HIDDEN struct listnode __android_log_readers;
-
-#if defined(_WIN32)
-#define logger_list_rdlock()
-#define logger_list_wrlock()
-#define logger_list_unlock()
-#else
-LIBLOG_HIDDEN pthread_rwlock_t __android_log_readers_lock;
-
-#define logger_list_rdlock() pthread_rwlock_rdlock(&__android_log_readers_lock)
-#define logger_list_wrlock() pthread_rwlock_wrlock(&__android_log_readers_lock)
-#define logger_list_unlock() pthread_rwlock_unlock(&__android_log_readers_lock)
-#endif
-
-/* Must be called with logger_list_rdlock() or logger_list_wrlock() held */
-#define logger_list_for_each(logger_list)                                     \
-  for ((logger_list) = node_to_item(&__android_log_readers,                   \
-                                    struct android_log_logger_list, node);    \
-       (logger_list) != node_to_item(&__android_log_readers,                  \
-                                     struct android_log_logger_list, node) && \
-       (logger_list) != node_to_item((logger_list)->node.next,                \
-                                     struct android_log_logger_list, node);   \
-       (logger_list) = node_to_item((logger_list)->node.next,                 \
-                                    struct android_log_logger_list, node))
-
 /* OS specific dribs and drabs */
 
 #if defined(_WIN32)
@@ -189,12 +156,10 @@
 }
 #endif
 
-LIBLOG_HIDDEN void __android_log_lock();
-LIBLOG_HIDDEN int __android_log_trylock();
-LIBLOG_HIDDEN void __android_log_unlock();
+void __android_log_lock();
+int __android_log_trylock();
+void __android_log_unlock();
 
-LIBLOG_HIDDEN int __android_log_transport;
+extern int __android_log_transport;
 
 __END_DECLS
-
-#endif /* _LIBLOG_LOGGER_H__ */
diff --git a/liblog/logger_lock.c b/liblog/logger_lock.c
deleted file mode 100644
index d4e3a75..0000000
--- a/liblog/logger_lock.c
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2007-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.
- */
-
-/*
- * Some OS specific dribs and drabs (locking etc).
- */
-
-#if !defined(_WIN32)
-#include <pthread.h>
-#endif
-
-#include "logger.h"
-
-#if !defined(_WIN32)
-static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
-#endif
-
-LIBLOG_HIDDEN void __android_log_lock() {
-#if !defined(_WIN32)
-  /*
-   * 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);
-#endif
-}
-
-LIBLOG_HIDDEN int __android_log_trylock() {
-#if !defined(_WIN32)
-  return pthread_mutex_trylock(&log_init_lock);
-#else
-  return 0;
-#endif
-}
-
-LIBLOG_HIDDEN void __android_log_unlock() {
-#if !defined(_WIN32)
-  pthread_mutex_unlock(&log_init_lock);
-#endif
-}
diff --git a/liblog/logger_lock.cpp b/liblog/logger_lock.cpp
new file mode 100644
index 0000000..4636b00
--- /dev/null
+++ b/liblog/logger_lock.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2007-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.
+ */
+
+/*
+ * Some OS specific dribs and drabs (locking etc).
+ */
+
+#if !defined(_WIN32)
+#include <pthread.h>
+#endif
+
+#include "logger.h"
+
+#if !defined(_WIN32)
+static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+void __android_log_lock() {
+#if !defined(_WIN32)
+  /*
+   * 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);
+#endif
+}
+
+int __android_log_trylock() {
+#if !defined(_WIN32)
+  return pthread_mutex_trylock(&log_init_lock);
+#else
+  return 0;
+#endif
+}
+
+void __android_log_unlock() {
+#if !defined(_WIN32)
+  pthread_mutex_unlock(&log_init_lock);
+#endif
+}
diff --git a/liblog/logger_name.c b/liblog/logger_name.c
deleted file mode 100644
index 479bbfe..0000000
--- a/liblog/logger_name.c
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-** Copyright 2013-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 <string.h>
-
-#include <log/log.h>
-
-#include "log_portability.h"
-
-/* In the future, we would like to make this list extensible */
-static const char* LOG_NAME[LOG_ID_MAX] = {
-      /* 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 */
-};
-
-LIBLOG_ABI_PUBLIC const char* android_log_id_to_name(log_id_t log_id) {
-  if (log_id >= LOG_ID_MAX) {
-    log_id = LOG_ID_MAIN;
-  }
-  return LOG_NAME[log_id];
-}
-
-LIBLOG_ABI_PUBLIC log_id_t android_name_to_log_id(const char* logName) {
-  const char* b;
-  int ret;
-
-  if (!logName) {
-    return -1; /* NB: log_id_t is unsigned */
-  }
-  b = strrchr(logName, '/');
-  if (!b) {
-    b = logName;
-  } else {
-    ++b;
-  }
-
-  for (ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) {
-    const char* l = LOG_NAME[ret];
-    if (l && !strcmp(b, l)) {
-      return ret;
-    }
-  }
-  return -1; /* should never happen */
-}
diff --git a/liblog/logger_name.cpp b/liblog/logger_name.cpp
new file mode 100644
index 0000000..ece0550
--- /dev/null
+++ b/liblog/logger_name.cpp
@@ -0,0 +1,71 @@
+/*
+** Copyright 2013-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 <string.h>
+#include <type_traits>
+
+#include <log/log.h>
+
+#include "log_portability.h"
+
+/* In the future, we would like to make this list extensible */
+static const char* LOG_NAME[LOG_ID_MAX] = {
+    /* 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 */
+};
+
+const char* android_log_id_to_name(log_id_t log_id) {
+  if (log_id >= LOG_ID_MAX) {
+    log_id = LOG_ID_MAIN;
+  }
+  return LOG_NAME[log_id];
+}
+
+static_assert(std::is_same<std::underlying_type<log_id_t>::type, uint32_t>::value,
+              "log_id_t must be an unsigned int");
+
+log_id_t android_name_to_log_id(const char* logName) {
+  const char* b;
+  unsigned int ret;
+
+  if (!logName) {
+    return static_cast<log_id_t>(LOG_ID_MAX);
+  }
+
+  b = strrchr(logName, '/');
+  if (!b) {
+    b = logName;
+  } else {
+    ++b;
+  }
+
+  for (ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) {
+    const char* l = LOG_NAME[ret];
+    if (l && !strcmp(b, l)) {
+      return static_cast<log_id_t>(ret);
+    }
+  }
+
+  return static_cast<log_id_t>(LOG_ID_MAX);
+}
diff --git a/liblog/logger_read.c b/liblog/logger_read.c
deleted file mode 100644
index 0fd6efa..0000000
--- a/liblog/logger_read.c
+++ /dev/null
@@ -1,499 +0,0 @@
-/*
-** Copyright 2013-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 <fcntl.h>
-#include <pthread.h>
-#include <sched.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <android/log.h>
-#include <cutils/list.h>
-#include <private/android_filesystem_config.h>
-
-#include "config_read.h"
-#include "log_portability.h"
-#include "logger.h"
-
-/* android_logger_alloc unimplemented, no use case */
-/* android_logger_free not exported */
-static void android_logger_free(struct logger* logger) {
-  struct android_log_logger* logger_internal =
-      (struct android_log_logger*)logger;
-
-  if (!logger_internal) {
-    return;
-  }
-
-  list_remove(&logger_internal->node);
-
-  free(logger_internal);
-}
-
-/* android_logger_alloc unimplemented, no use case */
-
-/* method for getting the associated sublog id */
-LIBLOG_ABI_PUBLIC log_id_t android_logger_get_id(struct logger* logger) {
-  return ((struct android_log_logger*)logger)->logId;
-}
-
-static int init_transport_context(struct android_log_logger_list* logger_list) {
-  struct android_log_transport_read* transport;
-  struct listnode* node;
-
-  if (!logger_list) {
-    return -EINVAL;
-  }
-
-  if (list_empty(&logger_list->logger)) {
-    return -EINVAL;
-  }
-
-  if (!list_empty(&logger_list->transport)) {
-    return 0;
-  }
-
-  __android_log_lock();
-  /* mini __write_to_log_initialize() to populate transports */
-  if (list_empty(&__android_log_transport_read) &&
-      list_empty(&__android_log_persist_read)) {
-    __android_log_config_read();
-  }
-  __android_log_unlock();
-
-  node = (logger_list->mode & ANDROID_LOG_PSTORE)
-             ? &__android_log_persist_read
-             : &__android_log_transport_read;
-
-  read_transport_for_each(transport, node) {
-    struct android_log_transport_context* transp;
-    struct android_log_logger* logger;
-    unsigned logMask = 0;
-
-    logger_for_each(logger, logger_list) {
-      log_id_t logId = logger->logId;
-
-      if ((logId == LOG_ID_SECURITY) && (__android_log_uid() != AID_SYSTEM)) {
-        continue;
-      }
-      if (transport->read &&
-          (!transport->available || (transport->available(logId) >= 0))) {
-        logMask |= 1 << logId;
-      }
-    }
-    if (!logMask) {
-      continue;
-    }
-    transp = calloc(1, sizeof(*transp));
-    if (!transp) {
-      return -ENOMEM;
-    }
-    transp->parent = logger_list;
-    transp->transport = transport;
-    transp->logMask = logMask;
-    transp->ret = 1;
-    list_add_tail(&logger_list->transport, &transp->node);
-  }
-  if (list_empty(&logger_list->transport)) {
-    return -ENODEV;
-  }
-  return 0;
-}
-
-#define LOGGER_FUNCTION(logger, def, func, args...)                   \
-  ssize_t ret = -EINVAL;                                              \
-  struct android_log_transport_context* transp;                       \
-  struct android_log_logger* logger_internal =                        \
-      (struct android_log_logger*)(logger);                           \
-                                                                      \
-  if (!logger_internal) {                                             \
-    return ret;                                                       \
-  }                                                                   \
-  ret = init_transport_context(logger_internal->parent);              \
-  if (ret < 0) {                                                      \
-    return ret;                                                       \
-  }                                                                   \
-                                                                      \
-  ret = (def);                                                        \
-  transport_context_for_each(transp, logger_internal->parent) {       \
-    if ((transp->logMask & (1 << logger_internal->logId)) &&          \
-        transp->transport && transp->transport->func) {               \
-      ssize_t retval =                                                \
-          (transp->transport->func)(logger_internal, transp, ##args); \
-      if ((ret >= 0) || (ret == (def))) {                             \
-        ret = retval;                                                 \
-      }                                                               \
-    }                                                                 \
-  }                                                                   \
-  return ret
-
-LIBLOG_ABI_PUBLIC int android_logger_clear(struct logger* logger) {
-  LOGGER_FUNCTION(logger, -ENODEV, clear);
-}
-
-/* returns the total size of the log's ring buffer */
-LIBLOG_ABI_PUBLIC long android_logger_get_log_size(struct logger* logger) {
-  LOGGER_FUNCTION(logger, -ENODEV, getSize);
-}
-
-LIBLOG_ABI_PUBLIC int android_logger_set_log_size(struct logger* logger,
-                                                  unsigned long size) {
-  LOGGER_FUNCTION(logger, -ENODEV, setSize, size);
-}
-
-/*
- * returns the readable size of the log's ring buffer (that is, amount of the
- * log consumed)
- */
-LIBLOG_ABI_PUBLIC long android_logger_get_log_readable_size(
-    struct logger* logger) {
-  LOGGER_FUNCTION(logger, -ENODEV, getReadableSize);
-}
-
-/*
- * returns the logger version
- */
-LIBLOG_ABI_PUBLIC int android_logger_get_log_version(struct logger* logger) {
-  LOGGER_FUNCTION(logger, 4, version);
-}
-
-#define LOGGER_LIST_FUNCTION(logger_list, def, func, args...)              \
-  struct android_log_transport_context* transp;                            \
-  struct android_log_logger_list* logger_list_internal =                   \
-      (struct android_log_logger_list*)(logger_list);                      \
-                                                                           \
-  ssize_t ret = init_transport_context(logger_list_internal);              \
-  if (ret < 0) {                                                           \
-    return ret;                                                            \
-  }                                                                        \
-                                                                           \
-  ret = (def);                                                             \
-  transport_context_for_each(transp, logger_list_internal) {               \
-    if (transp->transport && (transp->transport->func)) {                  \
-      ssize_t retval =                                                     \
-          (transp->transport->func)(logger_list_internal, transp, ##args); \
-      if ((ret >= 0) || (ret == (def))) {                                  \
-        ret = retval;                                                      \
-      }                                                                    \
-    }                                                                      \
-  }                                                                        \
-  return ret
-
-/*
- * returns statistics
- */
-LIBLOG_ABI_PUBLIC ssize_t android_logger_get_statistics(
-    struct logger_list* logger_list, char* buf, size_t len) {
-  LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getStats, buf, len);
-}
-
-LIBLOG_ABI_PUBLIC ssize_t android_logger_get_prune_list(
-    struct logger_list* logger_list, char* buf, size_t len) {
-  LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getPrune, buf, len);
-}
-
-LIBLOG_ABI_PUBLIC int android_logger_set_prune_list(
-    struct logger_list* logger_list, char* buf, size_t len) {
-  LOGGER_LIST_FUNCTION(logger_list, -ENODEV, setPrune, buf, len);
-}
-
-LIBLOG_HIDDEN struct listnode __android_log_readers = { &__android_log_readers,
-                                                        &__android_log_readers };
-#if !defined(_WIN32)
-LIBLOG_HIDDEN pthread_rwlock_t __android_log_readers_lock =
-    PTHREAD_RWLOCK_INITIALIZER;
-#endif
-
-LIBLOG_ABI_PUBLIC struct logger_list* android_logger_list_alloc(
-    int mode, unsigned int tail, pid_t pid) {
-  struct android_log_logger_list* logger_list;
-
-  logger_list = calloc(1, sizeof(*logger_list));
-  if (!logger_list) {
-    return NULL;
-  }
-
-  list_init(&logger_list->logger);
-  list_init(&logger_list->transport);
-  logger_list->mode = mode;
-  logger_list->tail = tail;
-  logger_list->pid = pid;
-
-  logger_list_wrlock();
-  list_add_tail(&__android_log_readers, &logger_list->node);
-  logger_list_unlock();
-
-  return (struct logger_list*)logger_list;
-}
-
-LIBLOG_ABI_PUBLIC struct logger_list* android_logger_list_alloc_time(
-    int mode, log_time start, pid_t pid) {
-  struct android_log_logger_list* logger_list;
-
-  logger_list = calloc(1, sizeof(*logger_list));
-  if (!logger_list) {
-    return NULL;
-  }
-
-  list_init(&logger_list->logger);
-  list_init(&logger_list->transport);
-  logger_list->mode = mode;
-  logger_list->start = start;
-  logger_list->pid = pid;
-
-  logger_list_wrlock();
-  list_add_tail(&__android_log_readers, &logger_list->node);
-  logger_list_unlock();
-
-  return (struct logger_list*)logger_list;
-}
-
-/* android_logger_list_register unimplemented, no use case */
-/* android_logger_list_unregister unimplemented, no use case */
-
-/* Open the named log and add it to the logger list */
-LIBLOG_ABI_PUBLIC struct logger* android_logger_open(
-    struct logger_list* logger_list, log_id_t logId) {
-  struct android_log_logger_list* logger_list_internal =
-      (struct android_log_logger_list*)logger_list;
-  struct android_log_logger* logger;
-
-  if (!logger_list_internal || (logId >= LOG_ID_MAX)) {
-    goto err;
-  }
-
-  logger_for_each(logger, logger_list_internal) {
-    if (logger->logId == logId) {
-      goto ok;
-    }
-  }
-
-  logger = calloc(1, sizeof(*logger));
-  if (!logger) {
-    goto err;
-  }
-
-  logger->logId = logId;
-  list_add_tail(&logger_list_internal->logger, &logger->node);
-  logger->parent = logger_list_internal;
-
-  /* Reset known transports to re-evaluate, we just added one */
-  while (!list_empty(&logger_list_internal->transport)) {
-    struct listnode* node = list_head(&logger_list_internal->transport);
-    struct android_log_transport_context* transp =
-        node_to_item(node, struct android_log_transport_context, node);
-
-    list_remove(&transp->node);
-    free(transp);
-  }
-  goto ok;
-
-err:
-  logger = NULL;
-ok:
-  return (struct logger*)logger;
-}
-
-/* Open the single named log and make it part of a new logger list */
-LIBLOG_ABI_PUBLIC struct logger_list* android_logger_list_open(
-    log_id_t logId, int mode, unsigned int tail, pid_t pid) {
-  struct logger_list* logger_list = android_logger_list_alloc(mode, tail, pid);
-
-  if (!logger_list) {
-    return NULL;
-  }
-
-  if (!android_logger_open(logger_list, logId)) {
-    android_logger_list_free(logger_list);
-    return NULL;
-  }
-
-  return logger_list;
-}
-
-/* Validate log_msg packet, read function has already been null checked */
-static int android_transport_read(struct android_log_logger_list* logger_list,
-                                  struct android_log_transport_context* transp,
-                                  struct log_msg* log_msg) {
-  int ret = (*transp->transport->read)(logger_list, transp, log_msg);
-
-  if (ret > (int)sizeof(*log_msg)) {
-    ret = sizeof(*log_msg);
-  }
-
-  transp->ret = ret;
-
-  /* propagate errors, or make sure len & hdr_size members visible */
-  if (ret < (int)(sizeof(log_msg->entry.len) + sizeof(log_msg->entry.hdr_size))) {
-    if (ret >= (int)sizeof(log_msg->entry.len)) {
-      log_msg->entry.len = 0;
-    }
-    return ret;
-  }
-
-  /* hdr_size correction (logger_entry -> logger_entry_v2+ conversion) */
-  if (log_msg->entry_v2.hdr_size == 0) {
-    log_msg->entry_v2.hdr_size = sizeof(struct logger_entry);
-  }
-  if ((log_msg->entry_v2.hdr_size < sizeof(log_msg->entry_v1)) ||
-      (log_msg->entry_v2.hdr_size > sizeof(log_msg->entry))) {
-    return -EINVAL;
-  }
-
-  /* len validation */
-  if (ret <= log_msg->entry_v2.hdr_size) {
-    log_msg->entry.len = 0;
-  } else {
-    log_msg->entry.len = ret - log_msg->entry_v2.hdr_size;
-  }
-
-  return ret;
-}
-
-/* Read from the selected logs */
-LIBLOG_ABI_PUBLIC int android_logger_list_read(struct logger_list* logger_list,
-                                               struct log_msg* log_msg) {
-  struct android_log_transport_context* transp;
-  struct android_log_logger_list* logger_list_internal =
-      (struct android_log_logger_list*)logger_list;
-
-  int ret = init_transport_context(logger_list_internal);
-  if (ret < 0) {
-    return ret;
-  }
-
-  /* at least one transport */
-  transp = node_to_item(logger_list_internal->transport.next,
-                        struct android_log_transport_context, node);
-
-  /* more than one transport? */
-  if (transp->node.next != &logger_list_internal->transport) {
-    /* Poll and merge sort the entries if from multiple transports */
-    struct android_log_transport_context* oldest = NULL;
-    int ret;
-    int polled = 0;
-    do {
-      if (polled) {
-        sched_yield();
-      }
-      ret = -1000;
-      polled = 0;
-      do {
-        int retval = transp->ret;
-        if ((retval > 0) && !transp->logMsg.entry.len) {
-          if (!transp->transport->read) {
-            retval = transp->ret = 0;
-          } else if ((logger_list_internal->mode & ANDROID_LOG_NONBLOCK) ||
-                     !transp->transport->poll) {
-            retval = android_transport_read(logger_list_internal, transp,
-                                            &transp->logMsg);
-          } else {
-            int pollval =
-                (*transp->transport->poll)(logger_list_internal, transp);
-            if (pollval <= 0) {
-              sched_yield();
-              pollval = (*transp->transport->poll)(logger_list_internal, transp);
-            }
-            polled = 1;
-            if (pollval < 0) {
-              if ((pollval == -EINTR) || (pollval == -EAGAIN)) {
-                return -EAGAIN;
-              }
-              retval = transp->ret = pollval;
-            } else if (pollval > 0) {
-              retval = android_transport_read(logger_list_internal, transp,
-                                              &transp->logMsg);
-            }
-          }
-        }
-        if (ret < retval) {
-          ret = retval;
-        }
-        if ((transp->ret > 0) && transp->logMsg.entry.len &&
-            (!oldest || (oldest->logMsg.entry.sec > transp->logMsg.entry.sec) ||
-             ((oldest->logMsg.entry.sec == transp->logMsg.entry.sec) &&
-              (oldest->logMsg.entry.nsec > transp->logMsg.entry.nsec)))) {
-          oldest = transp;
-        }
-        transp = node_to_item(transp->node.next,
-                              struct android_log_transport_context, node);
-      } while (transp != node_to_item(&logger_list_internal->transport,
-                                      struct android_log_transport_context,
-                                      node));
-      if (!oldest && (logger_list_internal->mode & ANDROID_LOG_NONBLOCK)) {
-        return (ret < 0) ? ret : -EAGAIN;
-      }
-      transp = node_to_item(logger_list_internal->transport.next,
-                            struct android_log_transport_context, node);
-    } while (!oldest && (ret > 0));
-    if (!oldest) {
-      return ret;
-    }
-    // ret is a positive value less than sizeof(struct log_msg)
-    ret = oldest->ret;
-    if (ret < oldest->logMsg.entry.hdr_size) {
-      // zero truncated header fields.
-      memset(log_msg, 0,
-             (oldest->logMsg.entry.hdr_size > sizeof(oldest->logMsg)
-                  ? sizeof(oldest->logMsg)
-                  : oldest->logMsg.entry.hdr_size));
-    }
-    memcpy(log_msg, &oldest->logMsg, ret);
-    oldest->logMsg.entry.len = 0; /* Mark it as copied */
-    return ret;
-  }
-
-  /* if only one, no need to copy into transport_context and merge-sort */
-  return android_transport_read(logger_list_internal, transp, log_msg);
-}
-
-/* Close all the logs */
-LIBLOG_ABI_PUBLIC void android_logger_list_free(struct logger_list* logger_list) {
-  struct android_log_logger_list* logger_list_internal =
-      (struct android_log_logger_list*)logger_list;
-
-  if (logger_list_internal == NULL) {
-    return;
-  }
-
-  logger_list_wrlock();
-  list_remove(&logger_list_internal->node);
-  logger_list_unlock();
-
-  while (!list_empty(&logger_list_internal->transport)) {
-    struct listnode* node = list_head(&logger_list_internal->transport);
-    struct android_log_transport_context* transp =
-        node_to_item(node, struct android_log_transport_context, node);
-
-    if (transp->transport && transp->transport->close) {
-      (*transp->transport->close)(logger_list_internal, transp);
-    }
-    list_remove(&transp->node);
-    free(transp);
-  }
-
-  while (!list_empty(&logger_list_internal->logger)) {
-    struct listnode* node = list_head(&logger_list_internal->logger);
-    struct android_log_logger* logger =
-        node_to_item(node, struct android_log_logger, node);
-    android_logger_free((struct logger*)logger);
-  }
-
-  free(logger_list_internal);
-}
diff --git a/liblog/logger_read.cpp b/liblog/logger_read.cpp
new file mode 100644
index 0000000..4cf0846
--- /dev/null
+++ b/liblog/logger_read.cpp
@@ -0,0 +1,460 @@
+/*
+** Copyright 2013-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 "log/log_read.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sched.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <android/log.h>
+#include <cutils/list.h>
+#include <private/android_filesystem_config.h>
+
+#include "config_read.h"
+#include "log_portability.h"
+#include "logger.h"
+
+/* android_logger_alloc unimplemented, no use case */
+/* android_logger_free not exported */
+static void android_logger_free(struct logger* logger) {
+  struct android_log_logger* logger_internal = (struct android_log_logger*)logger;
+
+  if (!logger_internal) {
+    return;
+  }
+
+  list_remove(&logger_internal->node);
+
+  free(logger_internal);
+}
+
+/* android_logger_alloc unimplemented, no use case */
+
+/* method for getting the associated sublog id */
+log_id_t android_logger_get_id(struct logger* logger) {
+  return ((struct android_log_logger*)logger)->logId;
+}
+
+static int init_transport_context(struct android_log_logger_list* logger_list) {
+  struct android_log_transport_read* transport;
+  struct listnode* node;
+
+  if (!logger_list) {
+    return -EINVAL;
+  }
+
+  if (list_empty(&logger_list->logger)) {
+    return -EINVAL;
+  }
+
+  if (!list_empty(&logger_list->transport)) {
+    return 0;
+  }
+
+  __android_log_lock();
+  /* mini __write_to_log_initialize() to populate transports */
+  if (list_empty(&__android_log_transport_read) && list_empty(&__android_log_persist_read)) {
+    __android_log_config_read();
+  }
+  __android_log_unlock();
+
+  node = (logger_list->mode & ANDROID_LOG_PSTORE) ? &__android_log_persist_read
+                                                  : &__android_log_transport_read;
+
+  read_transport_for_each(transport, node) {
+    struct android_log_transport_context* transp;
+    struct android_log_logger* logger;
+    unsigned logMask = 0;
+
+    logger_for_each(logger, logger_list) {
+      log_id_t logId = logger->logId;
+
+      if ((logId == LOG_ID_SECURITY) && (__android_log_uid() != AID_SYSTEM)) {
+        continue;
+      }
+      if (transport->read && (!transport->available || (transport->available(logId) >= 0))) {
+        logMask |= 1 << logId;
+      }
+    }
+    if (!logMask) {
+      continue;
+    }
+    transp = static_cast<android_log_transport_context*>(calloc(1, sizeof(*transp)));
+    if (!transp) {
+      return -ENOMEM;
+    }
+    transp->parent = logger_list;
+    transp->transport = transport;
+    transp->logMask = logMask;
+    transp->ret = 1;
+    list_add_tail(&logger_list->transport, &transp->node);
+  }
+  if (list_empty(&logger_list->transport)) {
+    return -ENODEV;
+  }
+  return 0;
+}
+
+#define LOGGER_FUNCTION(logger, def, func, args...)                                  \
+  ssize_t ret = -EINVAL;                                                             \
+  struct android_log_transport_context* transp;                                      \
+  struct android_log_logger* logger_internal = (struct android_log_logger*)(logger); \
+                                                                                     \
+  if (!logger_internal) {                                                            \
+    return ret;                                                                      \
+  }                                                                                  \
+  ret = init_transport_context(logger_internal->parent);                             \
+  if (ret < 0) {                                                                     \
+    return ret;                                                                      \
+  }                                                                                  \
+                                                                                     \
+  ret = (def);                                                                       \
+  transport_context_for_each(transp, logger_internal->parent) {                      \
+    if ((transp->logMask & (1 << logger_internal->logId)) && transp->transport &&    \
+        transp->transport->func) {                                                   \
+      ssize_t retval = (transp->transport->func)(logger_internal, transp, ##args);   \
+      if ((ret >= 0) || (ret == (def))) {                                            \
+        ret = retval;                                                                \
+      }                                                                              \
+    }                                                                                \
+  }                                                                                  \
+  return ret
+
+int android_logger_clear(struct logger* logger) {
+  LOGGER_FUNCTION(logger, -ENODEV, clear);
+}
+
+/* returns the total size of the log's ring buffer */
+long android_logger_get_log_size(struct logger* logger) {
+  LOGGER_FUNCTION(logger, -ENODEV, getSize);
+}
+
+int android_logger_set_log_size(struct logger* logger, unsigned long size) {
+  LOGGER_FUNCTION(logger, -ENODEV, setSize, size);
+}
+
+/*
+ * returns the readable size of the log's ring buffer (that is, amount of the
+ * log consumed)
+ */
+long android_logger_get_log_readable_size(struct logger* logger) {
+  LOGGER_FUNCTION(logger, -ENODEV, getReadableSize);
+}
+
+/*
+ * returns the logger version
+ */
+int android_logger_get_log_version(struct logger* logger) {
+  LOGGER_FUNCTION(logger, 4, version);
+}
+
+#define LOGGER_LIST_FUNCTION(logger_list, def, func, args...)                           \
+  struct android_log_transport_context* transp;                                         \
+  struct android_log_logger_list* logger_list_internal =                                \
+      (struct android_log_logger_list*)(logger_list);                                   \
+                                                                                        \
+  ssize_t ret = init_transport_context(logger_list_internal);                           \
+  if (ret < 0) {                                                                        \
+    return ret;                                                                         \
+  }                                                                                     \
+                                                                                        \
+  ret = (def);                                                                          \
+  transport_context_for_each(transp, logger_list_internal) {                            \
+    if (transp->transport && (transp->transport->func)) {                               \
+      ssize_t retval = (transp->transport->func)(logger_list_internal, transp, ##args); \
+      if ((ret >= 0) || (ret == (def))) {                                               \
+        ret = retval;                                                                   \
+      }                                                                                 \
+    }                                                                                   \
+  }                                                                                     \
+  return ret
+
+/*
+ * returns statistics
+ */
+ssize_t android_logger_get_statistics(struct logger_list* logger_list, char* buf, size_t len) {
+  LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getStats, buf, len);
+}
+
+ssize_t android_logger_get_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
+  LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getPrune, buf, len);
+}
+
+int android_logger_set_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
+  LOGGER_LIST_FUNCTION(logger_list, -ENODEV, setPrune, buf, len);
+}
+
+struct logger_list* android_logger_list_alloc(int mode, unsigned int tail, pid_t pid) {
+  struct android_log_logger_list* logger_list;
+
+  logger_list = static_cast<android_log_logger_list*>(calloc(1, sizeof(*logger_list)));
+  if (!logger_list) {
+    return NULL;
+  }
+
+  list_init(&logger_list->logger);
+  list_init(&logger_list->transport);
+  logger_list->mode = mode;
+  logger_list->tail = tail;
+  logger_list->pid = pid;
+
+  return (struct logger_list*)logger_list;
+}
+
+struct logger_list* android_logger_list_alloc_time(int mode, log_time start, pid_t pid) {
+  struct android_log_logger_list* logger_list;
+
+  logger_list = static_cast<android_log_logger_list*>(calloc(1, sizeof(*logger_list)));
+  if (!logger_list) {
+    return NULL;
+  }
+
+  list_init(&logger_list->logger);
+  list_init(&logger_list->transport);
+  logger_list->mode = mode;
+  logger_list->start = start;
+  logger_list->pid = pid;
+
+  return (struct logger_list*)logger_list;
+}
+
+/* android_logger_list_register unimplemented, no use case */
+/* android_logger_list_unregister unimplemented, no use case */
+
+/* Open the named log and add it to the logger list */
+struct logger* android_logger_open(struct logger_list* logger_list, log_id_t logId) {
+  struct android_log_logger_list* logger_list_internal =
+      (struct android_log_logger_list*)logger_list;
+  struct android_log_logger* logger;
+
+  if (!logger_list_internal || (logId >= LOG_ID_MAX)) {
+    goto err;
+  }
+
+  logger_for_each(logger, logger_list_internal) {
+    if (logger->logId == logId) {
+      goto ok;
+    }
+  }
+
+  logger = static_cast<android_log_logger*>(calloc(1, sizeof(*logger)));
+  if (!logger) {
+    goto err;
+  }
+
+  logger->logId = logId;
+  list_add_tail(&logger_list_internal->logger, &logger->node);
+  logger->parent = logger_list_internal;
+
+  /* Reset known transports to re-evaluate, we just added one */
+  while (!list_empty(&logger_list_internal->transport)) {
+    struct listnode* node = list_head(&logger_list_internal->transport);
+    struct android_log_transport_context* transp =
+        node_to_item(node, struct android_log_transport_context, node);
+
+    list_remove(&transp->node);
+    free(transp);
+  }
+  goto ok;
+
+err:
+  logger = NULL;
+ok:
+  return (struct logger*)logger;
+}
+
+/* Open the single named log and make it part of a new logger list */
+struct logger_list* android_logger_list_open(log_id_t logId, int mode, unsigned int tail,
+                                             pid_t pid) {
+  struct logger_list* logger_list = android_logger_list_alloc(mode, tail, pid);
+
+  if (!logger_list) {
+    return NULL;
+  }
+
+  if (!android_logger_open(logger_list, logId)) {
+    android_logger_list_free(logger_list);
+    return NULL;
+  }
+
+  return logger_list;
+}
+
+/* Validate log_msg packet, read function has already been null checked */
+static int android_transport_read(struct android_log_logger_list* logger_list,
+                                  struct android_log_transport_context* transp,
+                                  struct log_msg* log_msg) {
+  int ret = (*transp->transport->read)(logger_list, transp, log_msg);
+
+  if (ret > (int)sizeof(*log_msg)) {
+    ret = sizeof(*log_msg);
+  }
+
+  transp->ret = ret;
+
+  /* propagate errors, or make sure len & hdr_size members visible */
+  if (ret < (int)(sizeof(log_msg->entry.len) + sizeof(log_msg->entry.hdr_size))) {
+    if (ret >= (int)sizeof(log_msg->entry.len)) {
+      log_msg->entry.len = 0;
+    }
+    return ret;
+  }
+
+  /* hdr_size correction (logger_entry -> logger_entry_v2+ conversion) */
+  if (log_msg->entry_v2.hdr_size == 0) {
+    log_msg->entry_v2.hdr_size = sizeof(struct logger_entry);
+  }
+  if ((log_msg->entry_v2.hdr_size < sizeof(log_msg->entry_v1)) ||
+      (log_msg->entry_v2.hdr_size > sizeof(log_msg->entry))) {
+    return -EINVAL;
+  }
+
+  /* len validation */
+  if (ret <= log_msg->entry_v2.hdr_size) {
+    log_msg->entry.len = 0;
+  } else {
+    log_msg->entry.len = ret - log_msg->entry_v2.hdr_size;
+  }
+
+  return ret;
+}
+
+/* Read from the selected logs */
+int android_logger_list_read(struct logger_list* logger_list, struct log_msg* log_msg) {
+  struct android_log_transport_context* transp;
+  struct android_log_logger_list* logger_list_internal =
+      (struct android_log_logger_list*)logger_list;
+
+  int ret = init_transport_context(logger_list_internal);
+  if (ret < 0) {
+    return ret;
+  }
+
+  /* at least one transport */
+  transp = node_to_item(logger_list_internal->transport.next, struct android_log_transport_context,
+                        node);
+
+  /* more than one transport? */
+  if (transp->node.next != &logger_list_internal->transport) {
+    /* Poll and merge sort the entries if from multiple transports */
+    struct android_log_transport_context* oldest = NULL;
+    int ret;
+    int polled = 0;
+    do {
+      if (polled) {
+        sched_yield();
+      }
+      ret = -1000;
+      polled = 0;
+      do {
+        int retval = transp->ret;
+        if ((retval > 0) && !transp->logMsg.entry.len) {
+          if (!transp->transport->read) {
+            retval = transp->ret = 0;
+          } else if ((logger_list_internal->mode & ANDROID_LOG_NONBLOCK) ||
+                     !transp->transport->poll) {
+            retval = android_transport_read(logger_list_internal, transp, &transp->logMsg);
+          } else {
+            int pollval = (*transp->transport->poll)(logger_list_internal, transp);
+            if (pollval <= 0) {
+              sched_yield();
+              pollval = (*transp->transport->poll)(logger_list_internal, transp);
+            }
+            polled = 1;
+            if (pollval < 0) {
+              if ((pollval == -EINTR) || (pollval == -EAGAIN)) {
+                return -EAGAIN;
+              }
+              retval = transp->ret = pollval;
+            } else if (pollval > 0) {
+              retval = android_transport_read(logger_list_internal, transp, &transp->logMsg);
+            }
+          }
+        }
+        if (ret < retval) {
+          ret = retval;
+        }
+        if ((transp->ret > 0) && transp->logMsg.entry.len &&
+            (!oldest || (oldest->logMsg.entry.sec > transp->logMsg.entry.sec) ||
+             ((oldest->logMsg.entry.sec == transp->logMsg.entry.sec) &&
+              (oldest->logMsg.entry.nsec > transp->logMsg.entry.nsec)))) {
+          oldest = transp;
+        }
+        transp = node_to_item(transp->node.next, struct android_log_transport_context, node);
+      } while (transp != node_to_item(&logger_list_internal->transport,
+                                      struct android_log_transport_context, node));
+      if (!oldest && (logger_list_internal->mode & ANDROID_LOG_NONBLOCK)) {
+        return (ret < 0) ? ret : -EAGAIN;
+      }
+      transp = node_to_item(logger_list_internal->transport.next,
+                            struct android_log_transport_context, node);
+    } while (!oldest && (ret > 0));
+    if (!oldest) {
+      return ret;
+    }
+    // ret is a positive value less than sizeof(struct log_msg)
+    ret = oldest->ret;
+    if (ret < oldest->logMsg.entry.hdr_size) {
+      // zero truncated header fields.
+      memset(
+          log_msg, 0,
+          (oldest->logMsg.entry.hdr_size > sizeof(oldest->logMsg) ? sizeof(oldest->logMsg)
+                                                                  : oldest->logMsg.entry.hdr_size));
+    }
+    memcpy(log_msg, &oldest->logMsg, ret);
+    oldest->logMsg.entry.len = 0; /* Mark it as copied */
+    return ret;
+  }
+
+  /* if only one, no need to copy into transport_context and merge-sort */
+  return android_transport_read(logger_list_internal, transp, log_msg);
+}
+
+/* Close all the logs */
+void android_logger_list_free(struct logger_list* logger_list) {
+  struct android_log_logger_list* logger_list_internal =
+      (struct android_log_logger_list*)logger_list;
+
+  if (logger_list_internal == NULL) {
+    return;
+  }
+
+  while (!list_empty(&logger_list_internal->transport)) {
+    struct listnode* node = list_head(&logger_list_internal->transport);
+    struct android_log_transport_context* transp =
+        node_to_item(node, struct android_log_transport_context, node);
+
+    if (transp->transport && transp->transport->close) {
+      (*transp->transport->close)(logger_list_internal, transp);
+    }
+    list_remove(&transp->node);
+    free(transp);
+  }
+
+  while (!list_empty(&logger_list_internal->logger)) {
+    struct listnode* node = list_head(&logger_list_internal->logger);
+    struct android_log_logger* logger = node_to_item(node, struct android_log_logger, node);
+    android_logger_free((struct logger*)logger);
+  }
+
+  free(logger_list_internal);
+}
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
deleted file mode 100644
index 2754e6e..0000000
--- a/liblog/logger_write.c
+++ /dev/null
@@ -1,727 +0,0 @@
-/*
- * Copyright (C) 2007-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 <stdatomic.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/time.h>
-
-#ifdef __BIONIC__
-#include <android/set_abort_message.h>
-#endif
-
-#include <log/event_tag_map.h>
-#include <log/log_transport.h>
-#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
-
-#include "config_read.h" /* __android_log_config_read_close() definition */
-#include "config_write.h"
-#include "log_portability.h"
-#include "logger.h"
-
-#define LOG_BUF_SIZE 1024
-
-static int __write_to_log_init(log_id_t, struct iovec* vec, size_t nr);
-static int (*write_to_log)(log_id_t, struct iovec* vec,
-                           size_t nr) = __write_to_log_init;
-
-/*
- * This is used by the C++ code to decide if it should write logs through
- * the C code.  Basically, if /dev/socket/logd is available, we're running in
- * the simulator rather than a desktop tool and want to use the device.
- */
-static enum {
-  kLogUninitialized,
-  kLogNotAvailable,
-  kLogAvailable
-} g_log_status = kLogUninitialized;
-
-static int check_log_uid_permissions() {
-#if defined(__ANDROID__)
-  uid_t uid = __android_log_uid();
-
-  /* Matches clientHasLogCredentials() in logd */
-  if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
-    uid = geteuid();
-    if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
-      gid_t gid = getgid();
-      if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
-        gid = getegid();
-        if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
-          int num_groups;
-          gid_t* groups;
-
-          num_groups = getgroups(0, NULL);
-          if (num_groups <= 0) {
-            return -EPERM;
-          }
-          groups = calloc(num_groups, sizeof(gid_t));
-          if (!groups) {
-            return -ENOMEM;
-          }
-          num_groups = getgroups(num_groups, groups);
-          while (num_groups > 0) {
-            if (groups[num_groups - 1] == AID_LOG) {
-              break;
-            }
-            --num_groups;
-          }
-          free(groups);
-          if (num_groups <= 0) {
-            return -EPERM;
-          }
-        }
-      }
-    }
-  }
-#endif
-  return 0;
-}
-
-static void __android_log_cache_available(
-    struct android_log_transport_write* node) {
-  size_t i;
-
-  if (node->logMask) {
-    return;
-  }
-
-  for (i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
-    if (node->write && (i != LOG_ID_KERNEL) &&
-        ((i != LOG_ID_SECURITY) || (check_log_uid_permissions() == 0)) &&
-        (!node->available || ((*node->available)(i) >= 0))) {
-      node->logMask |= 1 << i;
-    }
-  }
-}
-
-LIBLOG_ABI_PUBLIC int __android_log_dev_available() {
-  struct android_log_transport_write* node;
-
-  if (list_empty(&__android_log_transport_write)) {
-    return kLogUninitialized;
-  }
-
-  write_transport_for_each(node, &__android_log_transport_write) {
-    __android_log_cache_available(node);
-    if (node->logMask) {
-      return kLogAvailable;
-    }
-  }
-  return kLogNotAvailable;
-}
-
-#if defined(__ANDROID__)
-static atomic_uintptr_t tagMap;
-#endif
-
-/*
- * Release any logger resources. A new log write will immediately re-acquire.
- */
-LIBLOG_ABI_PUBLIC void __android_log_close() {
-  struct android_log_transport_write* transport;
-#if defined(__ANDROID__)
-  EventTagMap* m;
-#endif
-
-  __android_log_lock();
-
-  write_to_log = __write_to_log_init;
-
-  /*
-   * Threads that are actively writing at this point are not held back
-   * by a lock and are at risk of dropping the messages with a return code
-   * -EBADF. Prefer to return error code than add the overhead of a lock to
-   * each log writing call to guarantee delivery. In addition, anyone
-   * calling this is doing so to release the logging resources and shut down,
-   * for them to do so with outstanding log requests in other threads is a
-   * disengenuous use of this function.
-   */
-
-  write_transport_for_each(transport, &__android_log_persist_write) {
-    if (transport->close) {
-      (*transport->close)();
-    }
-  }
-
-  write_transport_for_each(transport, &__android_log_transport_write) {
-    if (transport->close) {
-      (*transport->close)();
-    }
-  }
-
-  __android_log_config_write_close();
-
-#if defined(__ANDROID__)
-  /*
-   * Additional risk here somewhat mitigated by immediately unlock flushing
-   * the processor cache. The multi-threaded race that we choose to accept,
-   * to minimize locking, is an atomic_load in a writer picking up a value
-   * just prior to entering this routine. There will be an use after free.
-   *
-   * Again, anyone calling this is doing so to release the logging resources
-   * is most probably going to quiesce then shut down; or to restart after
-   * a fork so the risk should be non-existent. For this reason we
-   * choose a mitigation stance for efficiency instead of incuring the cost
-   * of a lock for every log write.
-   */
-  m = (EventTagMap*)atomic_exchange(&tagMap, (uintptr_t)0);
-#endif
-
-  __android_log_unlock();
-
-#if defined(__ANDROID__)
-  if (m != (EventTagMap*)(uintptr_t)-1LL) android_closeEventTagMap(m);
-#endif
-}
-
-/* log_init_lock assumed */
-static int __write_to_log_initialize() {
-  struct android_log_transport_write* transport;
-  struct listnode* n;
-  int i = 0, ret = 0;
-
-  __android_log_config_write();
-  write_transport_for_each_safe(transport, n, &__android_log_transport_write) {
-    __android_log_cache_available(transport);
-    if (!transport->logMask) {
-      list_remove(&transport->node);
-      continue;
-    }
-    if (!transport->open || ((*transport->open)() < 0)) {
-      if (transport->close) {
-        (*transport->close)();
-      }
-      list_remove(&transport->node);
-      continue;
-    }
-    ++ret;
-  }
-  write_transport_for_each_safe(transport, n, &__android_log_persist_write) {
-    __android_log_cache_available(transport);
-    if (!transport->logMask) {
-      list_remove(&transport->node);
-      continue;
-    }
-    if (!transport->open || ((*transport->open)() < 0)) {
-      if (transport->close) {
-        (*transport->close)();
-      }
-      list_remove(&transport->node);
-      continue;
-    }
-    ++i;
-  }
-  if (!ret && !i) {
-    return -ENODEV;
-  }
-
-  return ret;
-}
-
-/*
- * Extract a 4-byte value from a byte stream. le32toh open coded
- */
-static inline uint32_t get4LE(const uint8_t* src) {
-  return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
-}
-
-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, 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;
-#if defined(__ANDROID__)
-  clock_gettime(android_log_clockid(), &ts);
-
-  if (log_id == LOG_ID_SECURITY) {
-    if (vec[0].iov_len < 4) {
-      errno = save_errno;
-      return -EINVAL;
-    }
-
-    ret = check_log_uid_permissions();
-    if (ret < 0) {
-      errno = save_errno;
-      return ret;
-    }
-    if (!__android_log_security()) {
-      /* If only we could reset downstream logd counter */
-      errno = save_errno;
-      return -EPERM;
-    }
-  } else if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
-    const char* tag;
-    size_t len;
-    EventTagMap *m, *f;
-
-    if (vec[0].iov_len < 4) {
-      errno = save_errno;
-      return -EINVAL;
-    }
-
-    tag = NULL;
-    len = 0;
-    f = NULL;
-    m = (EventTagMap*)atomic_load(&tagMap);
-
-    if (!m) {
-      ret = __android_log_trylock();
-      m = (EventTagMap*)atomic_load(&tagMap); /* trylock flush cache */
-      if (!m) {
-        m = android_openEventTagMap(NULL);
-        if (ret) { /* trylock failed, use local copy, mark for close */
-          f = m;
-        } else {
-          if (!m) { /* One chance to open map file */
-            m = (EventTagMap*)(uintptr_t)-1LL;
-          }
-          atomic_store(&tagMap, (uintptr_t)m);
-        }
-      }
-      if (!ret) { /* trylock succeeded, unlock */
-        __android_log_unlock();
-      }
-    }
-    if (m && (m != (EventTagMap*)(uintptr_t)-1LL)) {
-      tag = android_lookupEventTag_len(m, &len, get4LE(vec[0].iov_base));
-    }
-    ret = __android_log_is_loggable_len(ANDROID_LOG_INFO, tag, len,
-                                        ANDROID_LOG_VERBOSE);
-    if (f) { /* local copy marked for close */
-      android_closeEventTagMap(f);
-    }
-    if (!ret) {
-      errno = save_errno;
-      return -EPERM;
-    }
-  } else {
-    /* Validate the incoming tag, tag content can not split across iovec */
-    char prio = ANDROID_LOG_VERBOSE;
-    const char* tag = vec[0].iov_base;
-    size_t len = vec[0].iov_len;
-    if (!tag) {
-      len = 0;
-    }
-    if (len > 0) {
-      prio = *tag;
-      if (len > 1) {
-        --len;
-        ++tag;
-      } else {
-        len = vec[1].iov_len;
-        tag = ((const char*)vec[1].iov_base);
-        if (!tag) {
-          len = 0;
-        }
-      }
-    }
-    /* tag must be nul terminated */
-    if (tag && strnlen(tag, len) >= len) {
-      tag = NULL;
-    }
-
-    if (!__android_log_is_loggable_len(prio, tag, len - 1, ANDROID_LOG_VERBOSE)) {
-      errno = save_errno;
-      return -EPERM;
-    }
-  }
-#else
-  /* simulate clock_gettime(CLOCK_REALTIME, &ts); */
-  {
-    struct timeval tv;
-    gettimeofday(&tv, NULL);
-    ts.tv_sec = tv.tv_sec;
-    ts.tv_nsec = tv.tv_usec * 1000;
-  }
-#endif
-
-  ret = 0;
-  i = 1 << log_id;
-  write_transport_for_each(node, &__android_log_transport_write) {
-    if (node->logMask & i) {
-      ssize_t retval;
-      retval = (*node->write)(log_id, &ts, vec, nr);
-      if (ret >= 0) {
-        ret = retval;
-      }
-    }
-  }
-
-  write_transport_for_each(node, &__android_log_persist_write) {
-    if (node->logMask & i) {
-      (void)(*node->write)(log_id, &ts, vec, nr);
-    }
-  }
-
-  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) {
-    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;
-    }
-
-    write_to_log = __write_to_log_daemon;
-  }
-
-  __android_log_unlock();
-
-  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,
-                                          const char* msg) {
-  return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
-}
-
-LIBLOG_ABI_PUBLIC int __android_log_buf_write(int bufID, int prio,
-                                              const char* tag, const char* msg) {
-  struct iovec vec[3];
-  char tmp_tag[32];
-
-  if (!tag) tag = "";
-
-  /* XXX: This needs to go! */
-  if (bufID != LOG_ID_RADIO) {
-    switch (tag[0]) {
-      case 'H':
-        if (strcmp(tag + 1, "HTC_RIL" + 1)) break;
-        goto inform;
-      case 'R':
-        /* Any log tag with "RIL" as the prefix */
-        if (strncmp(tag + 1, "RIL" + 1, strlen("RIL") - 1)) break;
-        goto inform;
-      case 'Q':
-        /* Any log tag with "QC_RIL" as the prefix */
-        if (strncmp(tag + 1, "QC_RIL" + 1, strlen("QC_RIL") - 1)) break;
-        goto inform;
-      case 'I':
-        /* Any log tag with "IMS" as the prefix */
-        if (strncmp(tag + 1, "IMS" + 1, strlen("IMS") - 1)) break;
-        goto inform;
-      case 'A':
-        if (strcmp(tag + 1, "AT" + 1)) break;
-        goto inform;
-      case 'G':
-        if (strcmp(tag + 1, "GSM" + 1)) break;
-        goto inform;
-      case 'S':
-        if (strcmp(tag + 1, "STK" + 1) && strcmp(tag + 1, "SMS" + 1)) break;
-        goto inform;
-      case 'C':
-        if (strcmp(tag + 1, "CDMA" + 1)) break;
-        goto inform;
-      case 'P':
-        if (strcmp(tag + 1, "PHONE" + 1)) break;
-      /* FALLTHRU */
-      inform:
-        bufID = LOG_ID_RADIO;
-        snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
-        tag = tmp_tag;
-      /* FALLTHRU */
-      default:
-        break;
-    }
-  }
-
-#if __BIONIC__
-  if (prio == ANDROID_LOG_FATAL) {
-    android_set_abort_message(msg);
-  }
-#endif
-
-  vec[0].iov_base = (unsigned char*)&prio;
-  vec[0].iov_len = 1;
-  vec[1].iov_base = (void*)tag;
-  vec[1].iov_len = strlen(tag) + 1;
-  vec[2].iov_base = (void*)msg;
-  vec[2].iov_len = strlen(msg) + 1;
-
-  return write_to_log(bufID, vec, 3);
-}
-
-LIBLOG_ABI_PUBLIC int __android_log_vprint(int prio, const char* tag,
-                                           const char* fmt, va_list ap) {
-  char buf[LOG_BUF_SIZE];
-
-  vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
-
-  return __android_log_write(prio, tag, buf);
-}
-
-LIBLOG_ABI_PUBLIC int __android_log_print(int prio, const char* tag,
-                                          const char* fmt, ...) {
-  va_list ap;
-  char buf[LOG_BUF_SIZE];
-
-  va_start(ap, fmt);
-  vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
-  va_end(ap);
-
-  return __android_log_write(prio, tag, buf);
-}
-
-LIBLOG_ABI_PUBLIC int __android_log_buf_print(int bufID, int prio,
-                                              const char* tag, const char* fmt,
-                                              ...) {
-  va_list ap;
-  char buf[LOG_BUF_SIZE];
-
-  va_start(ap, fmt);
-  vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
-  va_end(ap);
-
-  return __android_log_buf_write(bufID, prio, tag, buf);
-}
-
-LIBLOG_ABI_PUBLIC void __android_log_assert(const char* cond, const char* tag,
-                                            const char* fmt, ...) {
-  char buf[LOG_BUF_SIZE];
-
-  if (fmt) {
-    va_list ap;
-    va_start(ap, fmt);
-    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
-    va_end(ap);
-  } else {
-    /* Msg not provided, log condition.  N.B. Do not use cond directly as
-     * format string as it could contain spurious '%' syntax (e.g.
-     * "%d" in "blocks%devs == 0").
-     */
-    if (cond)
-      snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond);
-    else
-      strcpy(buf, "Unspecified assertion failed");
-  }
-
-  // Log assertion failures to stderr for the benefit of "adb shell" users
-  // and gtests (http://b/23675822).
-  struct iovec iov[2] = {
-    { buf, strlen(buf) }, { (char*)"\n", 1 },
-  };
-  TEMP_FAILURE_RETRY(writev(2, iov, 2));
-
-  __android_log_write(ANDROID_LOG_FATAL, tag, buf);
-  abort(); /* abort so we have a chance to debug the situation */
-           /* NOTREACHED */
-}
-
-LIBLOG_ABI_PUBLIC int __android_log_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_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) {
-  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_SECURITY, vec, 2);
-}
-
-/*
- * Like __android_log_bwrite, but takes the type as well.  Doesn't work
- * for the general case where we're generating lists of stuff, but very
- * handy if we just want to dump an integer into the log.
- */
-LIBLOG_ABI_PUBLIC int __android_log_btwrite(int32_t tag, char type,
-                                            const void* payload, size_t len) {
-  struct iovec vec[3];
-
-  vec[0].iov_base = &tag;
-  vec[0].iov_len = sizeof(tag);
-  vec[1].iov_base = &type;
-  vec[1].iov_len = sizeof(type);
-  vec[2].iov_base = (void*)payload;
-  vec[2].iov_len = len;
-
-  return write_to_log(LOG_ID_EVENTS, vec, 3);
-}
-
-/*
- * Like __android_log_bwrite, but used for writing strings to the
- * event log.
- */
-LIBLOG_ABI_PUBLIC int __android_log_bswrite(int32_t tag, const char* payload) {
-  struct iovec vec[4];
-  char type = EVENT_TYPE_STRING;
-  uint32_t len = strlen(payload);
-
-  vec[0].iov_base = &tag;
-  vec[0].iov_len = sizeof(tag);
-  vec[1].iov_base = &type;
-  vec[1].iov_len = sizeof(type);
-  vec[2].iov_base = &len;
-  vec[2].iov_len = sizeof(len);
-  vec[3].iov_base = (void*)payload;
-  vec[3].iov_len = len;
-
-  return write_to_log(LOG_ID_EVENTS, vec, 4);
-}
-
-/*
- * Like __android_log_security_bwrite, but used for writing strings to the
- * security log.
- */
-LIBLOG_ABI_PUBLIC int __android_log_security_bswrite(int32_t tag,
-                                                     const char* payload) {
-  struct iovec vec[4];
-  char type = EVENT_TYPE_STRING;
-  uint32_t len = strlen(payload);
-
-  vec[0].iov_base = &tag;
-  vec[0].iov_len = sizeof(tag);
-  vec[1].iov_base = &type;
-  vec[1].iov_len = sizeof(type);
-  vec[2].iov_base = &len;
-  vec[2].iov_len = sizeof(len);
-  vec[3].iov_base = (void*)payload;
-  vec[3].iov_len = len;
-
-  return write_to_log(LOG_ID_SECURITY, vec, 4);
-}
-
-static int __write_to_log_null(log_id_t log_id, struct iovec* vec, size_t nr) {
-  size_t len, i;
-
-  if ((log_id < LOG_ID_MIN) || (log_id >= LOG_ID_MAX)) {
-    return -EINVAL;
-  }
-
-  for (len = i = 0; i < nr; ++i) {
-    len += vec[i].iov_len;
-  }
-  if (!len) {
-    return -EINVAL;
-  }
-  return len;
-}
-
-/* Following functions need access to our internal write_to_log status */
-
-LIBLOG_HIDDEN int __android_log_transport;
-
-LIBLOG_ABI_PUBLIC int android_set_log_transport(int transport_flag) {
-  int retval;
-
-  if (transport_flag < 0) {
-    return -EINVAL;
-  }
-
-  retval = LOGGER_NULL;
-
-  __android_log_lock();
-
-  if (transport_flag & LOGGER_NULL) {
-    write_to_log = __write_to_log_null;
-
-    __android_log_unlock();
-
-    return retval;
-  }
-
-  __android_log_transport &= LOGGER_LOCAL | LOGGER_LOGD | LOGGER_STDERR;
-
-  transport_flag &= LOGGER_LOCAL | LOGGER_LOGD | LOGGER_STDERR;
-
-  if (__android_log_transport != transport_flag) {
-    __android_log_transport = transport_flag;
-    __android_log_config_write_close();
-    __android_log_config_read_close();
-
-    write_to_log = __write_to_log_init;
-    /* generically we only expect these two values for write_to_log */
-  } else if ((write_to_log != __write_to_log_init) &&
-             (write_to_log != __write_to_log_daemon)) {
-    write_to_log = __write_to_log_init;
-  }
-
-  retval = __android_log_transport;
-
-  __android_log_unlock();
-
-  return retval;
-}
-
-LIBLOG_ABI_PUBLIC int android_get_log_transport() {
-  int ret = LOGGER_DEFAULT;
-
-  __android_log_lock();
-  if (write_to_log == __write_to_log_null) {
-    ret = LOGGER_NULL;
-  } else {
-    __android_log_transport &= LOGGER_LOCAL | LOGGER_LOGD | LOGGER_STDERR;
-    ret = __android_log_transport;
-    if ((write_to_log != __write_to_log_init) &&
-        (write_to_log != __write_to_log_daemon)) {
-      ret = -EINVAL;
-    }
-  }
-  __android_log_unlock();
-
-  return ret;
-}
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
new file mode 100644
index 0000000..7fa3f43
--- /dev/null
+++ b/liblog/logger_write.cpp
@@ -0,0 +1,706 @@
+/*
+ * Copyright (C) 2007-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 <stdatomic.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#ifdef __BIONIC__
+#include <android/set_abort_message.h>
+#endif
+
+#include <log/event_tag_map.h>
+#include <log/log_transport.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "config_read.h" /* __android_log_config_read_close() definition */
+#include "config_write.h"
+#include "log_portability.h"
+#include "logger.h"
+#include "uio.h"
+
+#define LOG_BUF_SIZE 1024
+
+static int __write_to_log_init(log_id_t, struct iovec* vec, size_t nr);
+static int (*write_to_log)(log_id_t, struct iovec* vec, size_t nr) = __write_to_log_init;
+
+/*
+ * This is used by the C++ code to decide if it should write logs through
+ * the C code.  Basically, if /dev/socket/logd is available, we're running in
+ * the simulator rather than a desktop tool and want to use the device.
+ */
+static enum { kLogUninitialized, kLogNotAvailable, kLogAvailable } g_log_status = kLogUninitialized;
+
+static int check_log_uid_permissions() {
+#if defined(__ANDROID__)
+  uid_t uid = __android_log_uid();
+
+  /* Matches clientHasLogCredentials() in logd */
+  if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
+    uid = geteuid();
+    if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
+      gid_t gid = getgid();
+      if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
+        gid = getegid();
+        if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
+          int num_groups;
+          gid_t* groups;
+
+          num_groups = getgroups(0, NULL);
+          if (num_groups <= 0) {
+            return -EPERM;
+          }
+          groups = static_cast<gid_t*>(calloc(num_groups, sizeof(gid_t)));
+          if (!groups) {
+            return -ENOMEM;
+          }
+          num_groups = getgroups(num_groups, groups);
+          while (num_groups > 0) {
+            if (groups[num_groups - 1] == AID_LOG) {
+              break;
+            }
+            --num_groups;
+          }
+          free(groups);
+          if (num_groups <= 0) {
+            return -EPERM;
+          }
+        }
+      }
+    }
+  }
+#endif
+  return 0;
+}
+
+static void __android_log_cache_available(struct android_log_transport_write* node) {
+  uint32_t i;
+
+  if (node->logMask) {
+    return;
+  }
+
+  for (i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+    if (node->write && (i != LOG_ID_KERNEL) &&
+        ((i != LOG_ID_SECURITY) || (check_log_uid_permissions() == 0)) &&
+        (!node->available || ((*node->available)(static_cast<log_id_t>(i)) >= 0))) {
+      node->logMask |= 1 << i;
+    }
+  }
+}
+
+extern "C" int __android_log_dev_available() {
+  struct android_log_transport_write* node;
+
+  if (list_empty(&__android_log_transport_write)) {
+    return kLogUninitialized;
+  }
+
+  write_transport_for_each(node, &__android_log_transport_write) {
+    __android_log_cache_available(node);
+    if (node->logMask) {
+      return kLogAvailable;
+    }
+  }
+  return kLogNotAvailable;
+}
+
+#if defined(__ANDROID__)
+static atomic_uintptr_t tagMap;
+#endif
+
+/*
+ * Release any logger resources. A new log write will immediately re-acquire.
+ */
+void __android_log_close() {
+  struct android_log_transport_write* transport;
+#if defined(__ANDROID__)
+  EventTagMap* m;
+#endif
+
+  __android_log_lock();
+
+  write_to_log = __write_to_log_init;
+
+  /*
+   * Threads that are actively writing at this point are not held back
+   * by a lock and are at risk of dropping the messages with a return code
+   * -EBADF. Prefer to return error code than add the overhead of a lock to
+   * each log writing call to guarantee delivery. In addition, anyone
+   * calling this is doing so to release the logging resources and shut down,
+   * for them to do so with outstanding log requests in other threads is a
+   * disengenuous use of this function.
+   */
+
+  write_transport_for_each(transport, &__android_log_persist_write) {
+    if (transport->close) {
+      (*transport->close)();
+    }
+  }
+
+  write_transport_for_each(transport, &__android_log_transport_write) {
+    if (transport->close) {
+      (*transport->close)();
+    }
+  }
+
+  __android_log_config_write_close();
+
+#if defined(__ANDROID__)
+  /*
+   * Additional risk here somewhat mitigated by immediately unlock flushing
+   * the processor cache. The multi-threaded race that we choose to accept,
+   * to minimize locking, is an atomic_load in a writer picking up a value
+   * just prior to entering this routine. There will be an use after free.
+   *
+   * Again, anyone calling this is doing so to release the logging resources
+   * is most probably going to quiesce then shut down; or to restart after
+   * a fork so the risk should be non-existent. For this reason we
+   * choose a mitigation stance for efficiency instead of incuring the cost
+   * of a lock for every log write.
+   */
+  m = (EventTagMap*)atomic_exchange(&tagMap, (uintptr_t)0);
+#endif
+
+  __android_log_unlock();
+
+#if defined(__ANDROID__)
+  if (m != (EventTagMap*)(uintptr_t)-1LL) android_closeEventTagMap(m);
+#endif
+}
+
+/* log_init_lock assumed */
+static int __write_to_log_initialize() {
+  struct android_log_transport_write* transport;
+  struct listnode* n;
+  int i = 0, ret = 0;
+
+  __android_log_config_write();
+  write_transport_for_each_safe(transport, n, &__android_log_transport_write) {
+    __android_log_cache_available(transport);
+    if (!transport->logMask) {
+      list_remove(&transport->node);
+      continue;
+    }
+    if (!transport->open || ((*transport->open)() < 0)) {
+      if (transport->close) {
+        (*transport->close)();
+      }
+      list_remove(&transport->node);
+      continue;
+    }
+    ++ret;
+  }
+  write_transport_for_each_safe(transport, n, &__android_log_persist_write) {
+    __android_log_cache_available(transport);
+    if (!transport->logMask) {
+      list_remove(&transport->node);
+      continue;
+    }
+    if (!transport->open || ((*transport->open)() < 0)) {
+      if (transport->close) {
+        (*transport->close)();
+      }
+      list_remove(&transport->node);
+      continue;
+    }
+    ++i;
+  }
+  if (!ret && !i) {
+    return -ENODEV;
+  }
+
+  return ret;
+}
+
+/*
+ * Extract a 4-byte value from a byte stream. le32toh open coded
+ */
+static inline uint32_t get4LE(const uint8_t* src) {
+  return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+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, 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;
+#if defined(__ANDROID__)
+  clock_gettime(android_log_clockid(), &ts);
+
+  if (log_id == LOG_ID_SECURITY) {
+    if (vec[0].iov_len < 4) {
+      errno = save_errno;
+      return -EINVAL;
+    }
+
+    ret = check_log_uid_permissions();
+    if (ret < 0) {
+      errno = save_errno;
+      return ret;
+    }
+    if (!__android_log_security()) {
+      /* If only we could reset downstream logd counter */
+      errno = save_errno;
+      return -EPERM;
+    }
+  } else if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
+    const char* tag;
+    size_t len;
+    EventTagMap *m, *f;
+
+    if (vec[0].iov_len < 4) {
+      errno = save_errno;
+      return -EINVAL;
+    }
+
+    tag = NULL;
+    len = 0;
+    f = NULL;
+    m = (EventTagMap*)atomic_load(&tagMap);
+
+    if (!m) {
+      ret = __android_log_trylock();
+      m = (EventTagMap*)atomic_load(&tagMap); /* trylock flush cache */
+      if (!m) {
+        m = android_openEventTagMap(NULL);
+        if (ret) { /* trylock failed, use local copy, mark for close */
+          f = m;
+        } else {
+          if (!m) { /* One chance to open map file */
+            m = (EventTagMap*)(uintptr_t)-1LL;
+          }
+          atomic_store(&tagMap, (uintptr_t)m);
+        }
+      }
+      if (!ret) { /* trylock succeeded, unlock */
+        __android_log_unlock();
+      }
+    }
+    if (m && (m != (EventTagMap*)(uintptr_t)-1LL)) {
+      tag = android_lookupEventTag_len(m, &len, get4LE(static_cast<uint8_t*>(vec[0].iov_base)));
+    }
+    ret = __android_log_is_loggable_len(ANDROID_LOG_INFO, tag, len, ANDROID_LOG_VERBOSE);
+    if (f) { /* local copy marked for close */
+      android_closeEventTagMap(f);
+    }
+    if (!ret) {
+      errno = save_errno;
+      return -EPERM;
+    }
+  } else {
+    /* Validate the incoming tag, tag content can not split across iovec */
+    char prio = ANDROID_LOG_VERBOSE;
+    const char* tag = static_cast<const char*>(vec[0].iov_base);
+    size_t len = vec[0].iov_len;
+    if (!tag) {
+      len = 0;
+    }
+    if (len > 0) {
+      prio = *tag;
+      if (len > 1) {
+        --len;
+        ++tag;
+      } else {
+        len = vec[1].iov_len;
+        tag = ((const char*)vec[1].iov_base);
+        if (!tag) {
+          len = 0;
+        }
+      }
+    }
+    /* tag must be nul terminated */
+    if (tag && strnlen(tag, len) >= len) {
+      tag = NULL;
+    }
+
+    if (!__android_log_is_loggable_len(prio, tag, len - 1, ANDROID_LOG_VERBOSE)) {
+      errno = save_errno;
+      return -EPERM;
+    }
+  }
+#else
+  /* simulate clock_gettime(CLOCK_REALTIME, &ts); */
+  {
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    ts.tv_sec = tv.tv_sec;
+    ts.tv_nsec = tv.tv_usec * 1000;
+  }
+#endif
+
+  ret = 0;
+  i = 1 << log_id;
+  write_transport_for_each(node, &__android_log_transport_write) {
+    if (node->logMask & i) {
+      ssize_t retval;
+      retval = (*node->write)(log_id, &ts, vec, nr);
+      if (ret >= 0) {
+        ret = retval;
+      }
+    }
+  }
+
+  write_transport_for_each(node, &__android_log_persist_write) {
+    if (node->logMask & i) {
+      (void)(*node->write)(log_id, &ts, vec, nr);
+    }
+  }
+
+  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) {
+    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;
+    }
+
+    write_to_log = __write_to_log_daemon;
+  }
+
+  __android_log_unlock();
+
+  ret = write_to_log(log_id, vec, nr);
+  errno = save_errno;
+  return ret;
+}
+
+int __android_log_write(int prio, const char* tag, const char* msg) {
+  return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
+}
+
+int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) {
+  struct iovec vec[3];
+  char tmp_tag[32];
+
+  if (!tag) tag = "";
+
+  /* XXX: This needs to go! */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wstring-plus-int"
+  if (bufID != LOG_ID_RADIO) {
+    switch (tag[0]) {
+      case 'H':
+        if (strcmp(tag + 1, "HTC_RIL" + 1)) break;
+        goto inform;
+      case 'R':
+        /* Any log tag with "RIL" as the prefix */
+        if (strncmp(tag + 1, "RIL" + 1, strlen("RIL") - 1)) break;
+        goto inform;
+      case 'Q':
+        /* Any log tag with "QC_RIL" as the prefix */
+        if (strncmp(tag + 1, "QC_RIL" + 1, strlen("QC_RIL") - 1)) break;
+        goto inform;
+      case 'I':
+        /* Any log tag with "IMS" as the prefix */
+        if (strncmp(tag + 1, "IMS" + 1, strlen("IMS") - 1)) break;
+        goto inform;
+      case 'A':
+        if (strcmp(tag + 1, "AT" + 1)) break;
+        goto inform;
+      case 'G':
+        if (strcmp(tag + 1, "GSM" + 1)) break;
+        goto inform;
+      case 'S':
+        if (strcmp(tag + 1, "STK" + 1) && strcmp(tag + 1, "SMS" + 1)) break;
+        goto inform;
+      case 'C':
+        if (strcmp(tag + 1, "CDMA" + 1)) break;
+        goto inform;
+      case 'P':
+        if (strcmp(tag + 1, "PHONE" + 1)) break;
+      /* FALLTHRU */
+      inform:
+        bufID = LOG_ID_RADIO;
+        snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
+        tag = tmp_tag;
+        [[fallthrough]];
+      default:
+        break;
+    }
+  }
+#pragma clang diagnostic pop
+
+#if __BIONIC__
+  if (prio == ANDROID_LOG_FATAL) {
+    android_set_abort_message(msg);
+  }
+#endif
+
+  vec[0].iov_base = (unsigned char*)&prio;
+  vec[0].iov_len = 1;
+  vec[1].iov_base = (void*)tag;
+  vec[1].iov_len = strlen(tag) + 1;
+  vec[2].iov_base = (void*)msg;
+  vec[2].iov_len = strlen(msg) + 1;
+
+  return write_to_log(static_cast<log_id_t>(bufID), vec, 3);
+}
+
+int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap) {
+  char buf[LOG_BUF_SIZE];
+
+  vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+
+  return __android_log_write(prio, tag, buf);
+}
+
+int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
+  va_list ap;
+  char buf[LOG_BUF_SIZE];
+
+  va_start(ap, fmt);
+  vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+  va_end(ap);
+
+  return __android_log_write(prio, tag, buf);
+}
+
+int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...) {
+  va_list ap;
+  char buf[LOG_BUF_SIZE];
+
+  va_start(ap, fmt);
+  vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+  va_end(ap);
+
+  return __android_log_buf_write(bufID, prio, tag, buf);
+}
+
+void __android_log_assert(const char* cond, const char* tag, const char* fmt, ...) {
+  char buf[LOG_BUF_SIZE];
+
+  if (fmt) {
+    va_list ap;
+    va_start(ap, fmt);
+    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+    va_end(ap);
+  } else {
+    /* Msg not provided, log condition.  N.B. Do not use cond directly as
+     * format string as it could contain spurious '%' syntax (e.g.
+     * "%d" in "blocks%devs == 0").
+     */
+    if (cond)
+      snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond);
+    else
+      strcpy(buf, "Unspecified assertion failed");
+  }
+
+  // Log assertion failures to stderr for the benefit of "adb shell" users
+  // and gtests (http://b/23675822).
+  TEMP_FAILURE_RETRY(write(2, buf, strlen(buf)));
+  TEMP_FAILURE_RETRY(write(2, "\n", 1));
+
+  __android_log_write(ANDROID_LOG_FATAL, tag, buf);
+  abort(); /* abort so we have a chance to debug the situation */
+           /* NOTREACHED */
+}
+
+int __android_log_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_EVENTS, vec, 2);
+}
+
+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);
+}
+
+int __android_log_security_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_SECURITY, vec, 2);
+}
+
+/*
+ * Like __android_log_bwrite, but takes the type as well.  Doesn't work
+ * for the general case where we're generating lists of stuff, but very
+ * handy if we just want to dump an integer into the log.
+ */
+int __android_log_btwrite(int32_t tag, char type, const void* payload, size_t len) {
+  struct iovec vec[3];
+
+  vec[0].iov_base = &tag;
+  vec[0].iov_len = sizeof(tag);
+  vec[1].iov_base = &type;
+  vec[1].iov_len = sizeof(type);
+  vec[2].iov_base = (void*)payload;
+  vec[2].iov_len = len;
+
+  return write_to_log(LOG_ID_EVENTS, vec, 3);
+}
+
+/*
+ * Like __android_log_bwrite, but used for writing strings to the
+ * event log.
+ */
+int __android_log_bswrite(int32_t tag, const char* payload) {
+  struct iovec vec[4];
+  char type = EVENT_TYPE_STRING;
+  uint32_t len = strlen(payload);
+
+  vec[0].iov_base = &tag;
+  vec[0].iov_len = sizeof(tag);
+  vec[1].iov_base = &type;
+  vec[1].iov_len = sizeof(type);
+  vec[2].iov_base = &len;
+  vec[2].iov_len = sizeof(len);
+  vec[3].iov_base = (void*)payload;
+  vec[3].iov_len = len;
+
+  return write_to_log(LOG_ID_EVENTS, vec, 4);
+}
+
+/*
+ * Like __android_log_security_bwrite, but used for writing strings to the
+ * security log.
+ */
+int __android_log_security_bswrite(int32_t tag, const char* payload) {
+  struct iovec vec[4];
+  char type = EVENT_TYPE_STRING;
+  uint32_t len = strlen(payload);
+
+  vec[0].iov_base = &tag;
+  vec[0].iov_len = sizeof(tag);
+  vec[1].iov_base = &type;
+  vec[1].iov_len = sizeof(type);
+  vec[2].iov_base = &len;
+  vec[2].iov_len = sizeof(len);
+  vec[3].iov_base = (void*)payload;
+  vec[3].iov_len = len;
+
+  return write_to_log(LOG_ID_SECURITY, vec, 4);
+}
+
+static int __write_to_log_null(log_id_t log_id, struct iovec* vec, size_t nr) {
+  size_t len, i;
+
+  if ((log_id < LOG_ID_MIN) || (log_id >= LOG_ID_MAX)) {
+    return -EINVAL;
+  }
+
+  for (len = i = 0; i < nr; ++i) {
+    len += vec[i].iov_len;
+  }
+  if (!len) {
+    return -EINVAL;
+  }
+  return len;
+}
+
+/* Following functions need access to our internal write_to_log status */
+
+int __android_log_transport;
+
+int android_set_log_transport(int transport_flag) {
+  int retval;
+
+  if (transport_flag < 0) {
+    return -EINVAL;
+  }
+
+  retval = LOGGER_NULL;
+
+  __android_log_lock();
+
+  if (transport_flag & LOGGER_NULL) {
+    write_to_log = __write_to_log_null;
+
+    __android_log_unlock();
+
+    return retval;
+  }
+
+  __android_log_transport &= LOGGER_LOGD | LOGGER_STDERR;
+
+  transport_flag &= LOGGER_LOGD | LOGGER_STDERR;
+
+  if (__android_log_transport != transport_flag) {
+    __android_log_transport = transport_flag;
+    __android_log_config_write_close();
+    __android_log_config_read_close();
+
+    write_to_log = __write_to_log_init;
+    /* generically we only expect these two values for write_to_log */
+  } else if ((write_to_log != __write_to_log_init) && (write_to_log != __write_to_log_daemon)) {
+    write_to_log = __write_to_log_init;
+  }
+
+  retval = __android_log_transport;
+
+  __android_log_unlock();
+
+  return retval;
+}
+
+int android_get_log_transport() {
+  int ret = LOGGER_DEFAULT;
+
+  __android_log_lock();
+  if (write_to_log == __write_to_log_null) {
+    ret = LOGGER_NULL;
+  } else {
+    __android_log_transport &= LOGGER_LOGD | LOGGER_STDERR;
+    ret = __android_log_transport;
+    if ((write_to_log != __write_to_log_init) && (write_to_log != __write_to_log_daemon)) {
+      ret = -EINVAL;
+    }
+  }
+  __android_log_unlock();
+
+  return ret;
+}
diff --git a/liblog/logprint.c b/liblog/logprint.c
deleted file mode 100644
index a2839bf..0000000
--- a/liblog/logprint.c
+++ /dev/null
@@ -1,1882 +0,0 @@
-/*
-**
-** Copyright 2006-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 _GNU_SOURCE /* for asprintf */
-#ifndef __MINGW32__
-#define HAVE_STRSEP
-#endif
-
-#include <assert.h>
-#include <ctype.h>
-#include <errno.h>
-#include <inttypes.h>
-#ifndef __MINGW32__
-#include <pwd.h>
-#endif
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/param.h>
-#include <sys/types.h>
-
-#include <cutils/list.h>
-#include <log/log.h>
-#include <log/logprint.h>
-
-#include "log_portability.h"
-
-#define MS_PER_NSEC 1000000
-#define US_PER_NSEC 1000
-
-#ifndef MIN
-#define MIN(a, b) (((a) < (b)) ? (a) : (b))
-#endif
-
-typedef struct FilterInfo_t {
-  char* mTag;
-  android_LogPriority mPri;
-  struct FilterInfo_t* p_next;
-} FilterInfo;
-
-struct AndroidLogFormat_t {
-  android_LogPriority global_pri;
-  FilterInfo* filters;
-  AndroidLogPrintFormat format;
-  bool colored_output;
-  bool usec_time_output;
-  bool nsec_time_output;
-  bool printable_output;
-  bool year_output;
-  bool zone_output;
-  bool epoch_output;
-  bool monotonic_output;
-  bool uid_output;
-  bool descriptive_output;
-};
-
-/*
- * API issues prevent us from exposing "descriptive" in AndroidLogFormat_t
- * during android_log_processBinaryLogBuffer(), so we break layering.
- */
-static bool descriptive_output = false;
-
-/*
- *  gnome-terminal color tags
- *    See http://misc.flogisoft.com/bash/tip_colors_and_formatting
- *    for ideas on how to set the forground color of the text for xterm.
- *    The color manipulation character stream is defined as:
- *      ESC [ 3 8 ; 5 ; <color#> m
- */
-#define ANDROID_COLOR_BLUE 75
-#define ANDROID_COLOR_DEFAULT 231
-#define ANDROID_COLOR_GREEN 40
-#define ANDROID_COLOR_ORANGE 166
-#define ANDROID_COLOR_RED 196
-#define ANDROID_COLOR_YELLOW 226
-
-static FilterInfo* filterinfo_new(const char* tag, android_LogPriority pri) {
-  FilterInfo* p_ret;
-
-  p_ret = (FilterInfo*)calloc(1, sizeof(FilterInfo));
-  p_ret->mTag = strdup(tag);
-  p_ret->mPri = pri;
-
-  return p_ret;
-}
-
-/* balance to above, filterinfo_free left unimplemented */
-
-/*
- * Note: also accepts 0-9 priorities
- * returns ANDROID_LOG_UNKNOWN if the character is unrecognized
- */
-static android_LogPriority filterCharToPri(char c) {
-  android_LogPriority pri;
-
-  c = tolower(c);
-
-  if (c >= '0' && c <= '9') {
-    if (c >= ('0' + ANDROID_LOG_SILENT)) {
-      pri = ANDROID_LOG_VERBOSE;
-    } else {
-      pri = (android_LogPriority)(c - '0');
-    }
-  } else if (c == 'v') {
-    pri = ANDROID_LOG_VERBOSE;
-  } else if (c == 'd') {
-    pri = ANDROID_LOG_DEBUG;
-  } else if (c == 'i') {
-    pri = ANDROID_LOG_INFO;
-  } else if (c == 'w') {
-    pri = ANDROID_LOG_WARN;
-  } else if (c == 'e') {
-    pri = ANDROID_LOG_ERROR;
-  } else if (c == 'f') {
-    pri = ANDROID_LOG_FATAL;
-  } else if (c == 's') {
-    pri = ANDROID_LOG_SILENT;
-  } else if (c == '*') {
-    pri = ANDROID_LOG_DEFAULT;
-  } else {
-    pri = ANDROID_LOG_UNKNOWN;
-  }
-
-  return pri;
-}
-
-static char filterPriToChar(android_LogPriority pri) {
-  switch (pri) {
-    /* clang-format off */
-    case ANDROID_LOG_VERBOSE: return 'V';
-    case ANDROID_LOG_DEBUG:   return 'D';
-    case ANDROID_LOG_INFO:    return 'I';
-    case ANDROID_LOG_WARN:    return 'W';
-    case ANDROID_LOG_ERROR:   return 'E';
-    case ANDROID_LOG_FATAL:   return 'F';
-    case ANDROID_LOG_SILENT:  return 'S';
-
-    case ANDROID_LOG_DEFAULT:
-    case ANDROID_LOG_UNKNOWN:
-    default:                  return '?';
-    /* clang-format on */
-  }
-}
-
-static int colorFromPri(android_LogPriority pri) {
-  switch (pri) {
-    /* clang-format off */
-    case ANDROID_LOG_VERBOSE: return ANDROID_COLOR_DEFAULT;
-    case ANDROID_LOG_DEBUG:   return ANDROID_COLOR_BLUE;
-    case ANDROID_LOG_INFO:    return ANDROID_COLOR_GREEN;
-    case ANDROID_LOG_WARN:    return ANDROID_COLOR_ORANGE;
-    case ANDROID_LOG_ERROR:   return ANDROID_COLOR_RED;
-    case ANDROID_LOG_FATAL:   return ANDROID_COLOR_RED;
-    case ANDROID_LOG_SILENT:  return ANDROID_COLOR_DEFAULT;
-
-    case ANDROID_LOG_DEFAULT:
-    case ANDROID_LOG_UNKNOWN:
-    default:                  return ANDROID_COLOR_DEFAULT;
-    /* clang-format on */
-  }
-}
-
-static android_LogPriority filterPriForTag(AndroidLogFormat* p_format,
-                                           const char* tag) {
-  FilterInfo* p_curFilter;
-
-  for (p_curFilter = p_format->filters; p_curFilter != NULL;
-       p_curFilter = p_curFilter->p_next) {
-    if (0 == strcmp(tag, p_curFilter->mTag)) {
-      if (p_curFilter->mPri == ANDROID_LOG_DEFAULT) {
-        return p_format->global_pri;
-      } else {
-        return p_curFilter->mPri;
-      }
-    }
-  }
-
-  return p_format->global_pri;
-}
-
-/**
- * returns 1 if this log line should be printed based on its priority
- * and tag, and 0 if it should not
- */
-LIBLOG_ABI_PUBLIC int android_log_shouldPrintLine(AndroidLogFormat* p_format,
-                                                  const char* tag,
-                                                  android_LogPriority pri) {
-  return pri >= filterPriForTag(p_format, tag);
-}
-
-LIBLOG_ABI_PUBLIC AndroidLogFormat* android_log_format_new() {
-  AndroidLogFormat* p_ret;
-
-  p_ret = calloc(1, sizeof(AndroidLogFormat));
-
-  p_ret->global_pri = ANDROID_LOG_VERBOSE;
-  p_ret->format = FORMAT_BRIEF;
-  p_ret->colored_output = false;
-  p_ret->usec_time_output = false;
-  p_ret->nsec_time_output = false;
-  p_ret->printable_output = false;
-  p_ret->year_output = false;
-  p_ret->zone_output = false;
-  p_ret->epoch_output = false;
-#ifdef __ANDROID__
-  p_ret->monotonic_output = android_log_clockid() == CLOCK_MONOTONIC;
-#else
-  p_ret->monotonic_output = false;
-#endif
-  p_ret->uid_output = false;
-  p_ret->descriptive_output = false;
-  descriptive_output = false;
-
-  return p_ret;
-}
-
-static list_declare(convertHead);
-
-LIBLOG_ABI_PUBLIC void android_log_format_free(AndroidLogFormat* p_format) {
-  FilterInfo *p_info, *p_info_old;
-
-  p_info = p_format->filters;
-
-  while (p_info != NULL) {
-    p_info_old = p_info;
-    p_info = p_info->p_next;
-
-    free(p_info_old);
-  }
-
-  free(p_format);
-
-  /* Free conversion resource, can always be reconstructed */
-  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);
-  }
-}
-
-LIBLOG_ABI_PUBLIC int android_log_setPrintFormat(AndroidLogFormat* p_format,
-                                                 AndroidLogPrintFormat format) {
-  switch (format) {
-    case FORMAT_MODIFIER_COLOR:
-      p_format->colored_output = true;
-      return 0;
-    case FORMAT_MODIFIER_TIME_USEC:
-      p_format->usec_time_output = true;
-      return 0;
-    case FORMAT_MODIFIER_TIME_NSEC:
-      p_format->nsec_time_output = true;
-      return 0;
-    case FORMAT_MODIFIER_PRINTABLE:
-      p_format->printable_output = true;
-      return 0;
-    case FORMAT_MODIFIER_YEAR:
-      p_format->year_output = true;
-      return 0;
-    case FORMAT_MODIFIER_ZONE:
-      p_format->zone_output = !p_format->zone_output;
-      return 0;
-    case FORMAT_MODIFIER_EPOCH:
-      p_format->epoch_output = true;
-      return 0;
-    case FORMAT_MODIFIER_MONOTONIC:
-      p_format->monotonic_output = true;
-      return 0;
-    case FORMAT_MODIFIER_UID:
-      p_format->uid_output = true;
-      return 0;
-    case FORMAT_MODIFIER_DESCRIPT:
-      p_format->descriptive_output = true;
-      descriptive_output = true;
-      return 0;
-    default:
-      break;
-  }
-  p_format->format = format;
-  return 1;
-}
-
-static const char tz[] = "TZ";
-static const char utc[] = "UTC";
-
-/**
- * Returns FORMAT_OFF on invalid string
- */
-LIBLOG_ABI_PUBLIC AndroidLogPrintFormat
-android_log_formatFromString(const char* formatString) {
-  static AndroidLogPrintFormat format;
-
-  /* clang-format off */
-  if (!strcmp(formatString, "brief")) format = FORMAT_BRIEF;
-  else if (!strcmp(formatString, "process")) format = FORMAT_PROCESS;
-  else if (!strcmp(formatString, "tag")) format = FORMAT_TAG;
-  else if (!strcmp(formatString, "thread")) format = FORMAT_THREAD;
-  else if (!strcmp(formatString, "raw")) format = FORMAT_RAW;
-  else if (!strcmp(formatString, "time")) format = FORMAT_TIME;
-  else if (!strcmp(formatString, "threadtime")) format = FORMAT_THREADTIME;
-  else if (!strcmp(formatString, "long")) format = FORMAT_LONG;
-  else if (!strcmp(formatString, "color")) format = FORMAT_MODIFIER_COLOR;
-  else if (!strcmp(formatString, "colour")) format = FORMAT_MODIFIER_COLOR;
-  else if (!strcmp(formatString, "usec")) format = FORMAT_MODIFIER_TIME_USEC;
-  else if (!strcmp(formatString, "nsec")) format = FORMAT_MODIFIER_TIME_NSEC;
-  else if (!strcmp(formatString, "printable")) format = FORMAT_MODIFIER_PRINTABLE;
-  else if (!strcmp(formatString, "year")) format = FORMAT_MODIFIER_YEAR;
-  else if (!strcmp(formatString, "zone")) format = FORMAT_MODIFIER_ZONE;
-  else if (!strcmp(formatString, "epoch")) format = FORMAT_MODIFIER_EPOCH;
-  else if (!strcmp(formatString, "monotonic")) format = FORMAT_MODIFIER_MONOTONIC;
-  else if (!strcmp(formatString, "uid")) format = FORMAT_MODIFIER_UID;
-  else if (!strcmp(formatString, "descriptive")) format = FORMAT_MODIFIER_DESCRIPT;
-  /* clang-format on */
-
-#ifndef __MINGW32__
-  else {
-    extern char* tzname[2];
-    static const char gmt[] = "GMT";
-    char* cp = getenv(tz);
-    if (cp) {
-      cp = strdup(cp);
-    }
-    setenv(tz, formatString, 1);
-    /*
-     * Run tzset here to determine if the timezone is legitimate. If the
-     * zone is GMT, check if that is what was asked for, if not then
-     * did not match any on the system; report an error to caller.
-     */
-    tzset();
-    if (!tzname[0] ||
-        ((!strcmp(tzname[0], utc) || !strcmp(tzname[0], gmt)) /* error? */
-         && strcasecmp(formatString, utc) &&
-         strcasecmp(formatString, gmt))) { /* ok */
-      if (cp) {
-        setenv(tz, cp, 1);
-      } else {
-        unsetenv(tz);
-      }
-      tzset();
-      format = FORMAT_OFF;
-    } else {
-      format = FORMAT_MODIFIER_ZONE;
-    }
-    free(cp);
-  }
-#endif
-
-  return format;
-}
-
-/**
- * filterExpression: a single filter expression
- * eg "AT:d"
- *
- * returns 0 on success and -1 on invalid expression
- *
- * Assumes single threaded execution
- */
-
-LIBLOG_ABI_PUBLIC int android_log_addFilterRule(AndroidLogFormat* p_format,
-                                                const char* filterExpression) {
-  size_t tagNameLength;
-  android_LogPriority pri = ANDROID_LOG_DEFAULT;
-
-  tagNameLength = strcspn(filterExpression, ":");
-
-  if (tagNameLength == 0) {
-    goto error;
-  }
-
-  if (filterExpression[tagNameLength] == ':') {
-    pri = filterCharToPri(filterExpression[tagNameLength + 1]);
-
-    if (pri == ANDROID_LOG_UNKNOWN) {
-      goto error;
-    }
-  }
-
-  if (0 == strncmp("*", filterExpression, tagNameLength)) {
-    /*
-     * This filter expression refers to the global filter
-     * The default level for this is DEBUG if the priority
-     * is unspecified
-     */
-    if (pri == ANDROID_LOG_DEFAULT) {
-      pri = ANDROID_LOG_DEBUG;
-    }
-
-    p_format->global_pri = pri;
-  } else {
-    /*
-     * for filter expressions that don't refer to the global
-     * filter, the default is verbose if the priority is unspecified
-     */
-    if (pri == ANDROID_LOG_DEFAULT) {
-      pri = ANDROID_LOG_VERBOSE;
-    }
-
-    char* tagName;
-
-/*
- * Presently HAVE_STRNDUP is never defined, so the second case is always taken
- * Darwin doesn't have strndup, everything else does
- */
-#ifdef HAVE_STRNDUP
-    tagName = strndup(filterExpression, tagNameLength);
-#else
-    /* a few extra bytes copied... */
-    tagName = strdup(filterExpression);
-    tagName[tagNameLength] = '\0';
-#endif /*HAVE_STRNDUP*/
-
-    FilterInfo* p_fi = filterinfo_new(tagName, pri);
-    free(tagName);
-
-    p_fi->p_next = p_format->filters;
-    p_format->filters = p_fi;
-  }
-
-  return 0;
-error:
-  return -1;
-}
-
-#ifndef HAVE_STRSEP
-/* KISS replacement helper for below */
-static char* strsep(char** stringp, const char* delim) {
-  char* token;
-  char* ret = *stringp;
-
-  if (!ret || !*ret) {
-    return NULL;
-  }
-  token = strpbrk(ret, delim);
-  if (token) {
-    *token = '\0';
-    ++token;
-  } else {
-    token = ret + strlen(ret);
-  }
-  *stringp = token;
-  return ret;
-}
-#endif
-
-/**
- * filterString: a comma/whitespace-separated set of filter expressions
- *
- * eg "AT:d *:i"
- *
- * returns 0 on success and -1 on invalid expression
- *
- * Assumes single threaded execution
- *
- */
-LIBLOG_ABI_PUBLIC int android_log_addFilterString(AndroidLogFormat* p_format,
-                                                  const char* filterString) {
-  char* filterStringCopy = strdup(filterString);
-  char* p_cur = filterStringCopy;
-  char* p_ret;
-  int err;
-
-  /* Yes, I'm using strsep */
-  while (NULL != (p_ret = strsep(&p_cur, " \t,"))) {
-    /* ignore whitespace-only entries */
-    if (p_ret[0] != '\0') {
-      err = android_log_addFilterRule(p_format, p_ret);
-
-      if (err < 0) {
-        goto error;
-      }
-    }
-  }
-
-  free(filterStringCopy);
-  return 0;
-error:
-  free(filterStringCopy);
-  return -1;
-}
-
-/**
- * Splits a wire-format buffer into an AndroidLogEntry
- * entry allocated by caller. Pointers will point directly into buf
- *
- * Returns 0 on success and -1 on invalid wire format (entry will be
- * in unspecified state)
- */
-LIBLOG_ABI_PUBLIC int android_log_processLogBuffer(struct logger_entry* buf,
-                                                   AndroidLogEntry* entry) {
-  entry->message = NULL;
-  entry->messageLen = 0;
-
-  entry->tv_sec = buf->sec;
-  entry->tv_nsec = buf->nsec;
-  entry->uid = -1;
-  entry->pid = buf->pid;
-  entry->tid = buf->tid;
-
-  /*
-   * format: <priority:1><tag:N>\0<message:N>\0
-   *
-   * tag str
-   *   starts at buf->msg+1
-   * msg
-   *   starts at buf->msg+1+len(tag)+1
-   *
-   * The message may have been truncated by the kernel log driver.
-   * When that happens, we must null-terminate the message ourselves.
-   */
-  if (buf->len < 3) {
-    /*
-     * An well-formed entry must consist of at least a priority
-     * and two null characters
-     */
-    fprintf(stderr, "+++ LOG: entry too small\n");
-    return -1;
-  }
-
-  int msgStart = -1;
-  int msgEnd = -1;
-
-  int i;
-  char* msg = buf->msg;
-  struct logger_entry_v2* buf2 = (struct logger_entry_v2*)buf;
-  if (buf2->hdr_size) {
-    if ((buf2->hdr_size < sizeof(((struct log_msg*)NULL)->entry_v1)) ||
-        (buf2->hdr_size > sizeof(((struct log_msg*)NULL)->entry))) {
-      fprintf(stderr, "+++ LOG: entry illegal hdr_size\n");
-      return -1;
-    }
-    msg = ((char*)buf2) + buf2->hdr_size;
-    if (buf2->hdr_size >= sizeof(struct logger_entry_v4)) {
-      entry->uid = ((struct logger_entry_v4*)buf)->uid;
-    }
-  }
-  for (i = 1; i < buf->len; i++) {
-    if (msg[i] == '\0') {
-      if (msgStart == -1) {
-        msgStart = i + 1;
-      } else {
-        msgEnd = i;
-        break;
-      }
-    }
-  }
-
-  if (msgStart == -1) {
-    /* +++ LOG: malformed log message, DYB */
-    for (i = 1; i < buf->len; i++) {
-      /* odd characters in tag? */
-      if ((msg[i] <= ' ') || (msg[i] == ':') || (msg[i] >= 0x7f)) {
-        msg[i] = '\0';
-        msgStart = i + 1;
-        break;
-      }
-    }
-    if (msgStart == -1) {
-      msgStart = buf->len - 1; /* All tag, no message, print truncates */
-    }
-  }
-  if (msgEnd == -1) {
-    /* incoming message not null-terminated; force it */
-    msgEnd = buf->len - 1; /* may result in msgEnd < msgStart */
-    msg[msgEnd] = '\0';
-  }
-
-  entry->priority = msg[0];
-  entry->tag = msg + 1;
-  entry->tagLen = msgStart - 1;
-  entry->message = msg + msgStart;
-  entry->messageLen = (msgEnd < msgStart) ? 0 : (msgEnd - msgStart);
-
-  return 0;
-}
-
-/*
- * Extract a 4-byte value from a byte stream.
- */
-static inline uint32_t get4LE(const uint8_t* src) {
-  return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
-}
-
-/*
- * Extract an 8-byte value from a byte stream.
- */
-static inline uint64_t get8LE(const uint8_t* src) {
-  uint32_t low, high;
-
-  low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
-  high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
-  return ((uint64_t)high << 32) | (uint64_t)low;
-}
-
-static bool findChar(const char** cp, size_t* len, int c) {
-  while ((*len) && isspace(*(*cp))) {
-    ++(*cp);
-    --(*len);
-  }
-  if (c == INT_MAX) return *len;
-  if ((*len) && (*(*cp) == c)) {
-    ++(*cp);
-    --(*len);
-    return true;
-  }
-  return false;
-}
-
-/*
- * Recursively convert binary log data to printable form.
- *
- * This needs to be recursive because you can have lists of lists.
- *
- * If we run out of room, we stop processing immediately.  It's important
- * for us to check for space on every output element to avoid producing
- * garbled output.
- *
- * Returns 0 on success, 1 on buffer full, -1 on failure.
- */
-enum objectType {
-  TYPE_OBJECTS = '1',
-  TYPE_BYTES = '2',
-  TYPE_MILLISECONDS = '3',
-  TYPE_ALLOCATIONS = '4',
-  TYPE_ID = '5',
-  TYPE_PERCENT = '6',
-  TYPE_MONOTONIC = 's'
-};
-
-static int android_log_printBinaryEvent(const unsigned char** pEventData,
-                                        size_t* pEventDataLen, char** pOutBuf,
-                                        size_t* pOutBufLen, const char** fmtStr,
-                                        size_t* fmtLen) {
-  const unsigned char* eventData = *pEventData;
-  size_t eventDataLen = *pEventDataLen;
-  char* outBuf = *pOutBuf;
-  char* outBufSave = outBuf;
-  size_t outBufLen = *pOutBufLen;
-  size_t outBufLenSave = outBufLen;
-  unsigned char type;
-  size_t outCount = 0;
-  int result = 0;
-  const char* cp;
-  size_t len;
-  int64_t lval;
-
-  if (eventDataLen < 1) return -1;
-
-  type = *eventData++;
-  eventDataLen--;
-
-  cp = NULL;
-  len = 0;
-  if (fmtStr && *fmtStr && fmtLen && *fmtLen && **fmtStr) {
-    cp = *fmtStr;
-    len = *fmtLen;
-  }
-  /*
-   * event.logtag format specification:
-   *
-   * 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
-   * s: Number of seconds (monotonic time)
-   * Default value for data of type int/long is 2 (bytes).
-   */
-  if (!cp || !findChar(&cp, &len, '(')) {
-    len = 0;
-  } else {
-    char* outBufLastSpace = NULL;
-
-    findChar(&cp, &len, INT_MAX);
-    while (len && *cp && (*cp != '|') && (*cp != ')')) {
-      if (outBufLen <= 0) {
-        /* halt output */
-        goto no_room;
-      }
-      outBufLastSpace = isspace(*cp) ? outBuf : NULL;
-      *outBuf = *cp;
-      ++outBuf;
-      ++cp;
-      --outBufLen;
-      --len;
-    }
-    if (outBufLastSpace) {
-      outBufLen += outBuf - outBufLastSpace;
-      outBuf = outBufLastSpace;
-    }
-    if (outBufLen <= 0) {
-      /* halt output */
-      goto no_room;
-    }
-    if (outBufSave != outBuf) {
-      *outBuf = '=';
-      ++outBuf;
-      --outBufLen;
-    }
-
-    if (findChar(&cp, &len, '|') && findChar(&cp, &len, INT_MAX)) {
-      static const unsigned char typeTable[] = {
-        EVENT_TYPE_INT, EVENT_TYPE_LONG, EVENT_TYPE_STRING, EVENT_TYPE_LIST,
-        EVENT_TYPE_FLOAT
-      };
-
-      if ((*cp >= '1') &&
-          (*cp < (char)('1' + (sizeof(typeTable) / sizeof(typeTable[0])))) &&
-          (type != typeTable[(size_t)(*cp - '1')]))
-        len = 0;
-
-      if (len) {
-        ++cp;
-        --len;
-      } else {
-        /* reset the format */
-        outBuf = outBufSave;
-        outBufLen = outBufLenSave;
-      }
-    }
-  }
-  outCount = 0;
-  lval = 0;
-  switch (type) {
-    case EVENT_TYPE_INT:
-      /* 32-bit signed int */
-      {
-        int32_t ival;
-
-        if (eventDataLen < 4) return -1;
-        ival = get4LE(eventData);
-        eventData += 4;
-        eventDataLen -= 4;
-
-        lval = ival;
-      }
-      goto pr_lval;
-    case EVENT_TYPE_LONG:
-      /* 64-bit signed long */
-      if (eventDataLen < 8) return -1;
-      lval = get8LE(eventData);
-      eventData += 8;
-      eventDataLen -= 8;
-    pr_lval:
-      outCount = snprintf(outBuf, outBufLen, "%" PRId64, lval);
-      if (outCount < outBufLen) {
-        outBuf += outCount;
-        outBufLen -= outCount;
-      } else {
-        /* halt output */
-        goto no_room;
-      }
-      break;
-    case EVENT_TYPE_FLOAT:
-      /* float */
-      {
-        uint32_t ival;
-        float fval;
-
-        if (eventDataLen < 4) return -1;
-        ival = get4LE(eventData);
-        fval = *(float*)&ival;
-        eventData += 4;
-        eventDataLen -= 4;
-
-        outCount = snprintf(outBuf, outBufLen, "%f", fval);
-        if (outCount < outBufLen) {
-          outBuf += outCount;
-          outBufLen -= outCount;
-        } else {
-          /* halt output */
-          goto no_room;
-        }
-      }
-      break;
-    case EVENT_TYPE_STRING:
-      /* UTF-8 chars, not NULL-terminated */
-      {
-        unsigned int strLen;
-
-        if (eventDataLen < 4) return -1;
-        strLen = get4LE(eventData);
-        eventData += 4;
-        eventDataLen -= 4;
-
-        if (eventDataLen < strLen) {
-          result = -1; /* mark truncated */
-          strLen = eventDataLen;
-        }
-
-        if (cp && (strLen == 0)) {
-          /* reset the format if no content */
-          outBuf = outBufSave;
-          outBufLen = outBufLenSave;
-        }
-        if (strLen < outBufLen) {
-          memcpy(outBuf, eventData, strLen);
-          outBuf += strLen;
-          outBufLen -= strLen;
-        } else {
-          if (outBufLen > 0) {
-            /* copy what we can */
-            memcpy(outBuf, eventData, outBufLen);
-            outBuf += outBufLen;
-            outBufLen -= outBufLen;
-          }
-          if (!result) result = 1; /* if not truncated, return no room */
-        }
-        eventData += strLen;
-        eventDataLen -= strLen;
-        if (result != 0) goto bail;
-        break;
-      }
-    case EVENT_TYPE_LIST:
-      /* N items, all different types */
-      {
-        unsigned char count;
-        int i;
-
-        if (eventDataLen < 1) return -1;
-
-        count = *eventData++;
-        eventDataLen--;
-
-        if (outBufLen <= 0) goto no_room;
-
-        *outBuf++ = '[';
-        outBufLen--;
-
-        for (i = 0; i < count; i++) {
-          result = android_log_printBinaryEvent(
-              &eventData, &eventDataLen, &outBuf, &outBufLen, fmtStr, fmtLen);
-          if (result != 0) goto bail;
-
-          if (i < (count - 1)) {
-            if (outBufLen <= 0) goto no_room;
-            *outBuf++ = ',';
-            outBufLen--;
-          }
-        }
-
-        if (outBufLen <= 0) goto no_room;
-
-        *outBuf++ = ']';
-        outBufLen--;
-      }
-      break;
-    default:
-      fprintf(stderr, "Unknown binary event type %d\n", type);
-      return -1;
-  }
-  if (cp && len) {
-    if (findChar(&cp, &len, '|') && findChar(&cp, &len, INT_MAX)) {
-      switch (*cp) {
-        case TYPE_OBJECTS:
-          outCount = 0;
-          /* outCount = snprintf(outBuf, outBufLen, " objects"); */
-          break;
-        case TYPE_BYTES:
-          if ((lval != 0) && ((lval % 1024) == 0)) {
-            /* repaint with multiplier */
-            static const char suffixTable[] = { 'K', 'M', 'G', 'T' };
-            size_t idx = 0;
-            outBuf -= outCount;
-            outBufLen += outCount;
-            do {
-              lval /= 1024;
-              if ((lval % 1024) != 0) break;
-            } while (++idx <
-                     ((sizeof(suffixTable) / sizeof(suffixTable[0])) - 1));
-            outCount = snprintf(outBuf, outBufLen, "%" PRId64 "%cB", lval,
-                                suffixTable[idx]);
-          } else {
-            outCount = snprintf(outBuf, outBufLen, "B");
-          }
-          break;
-        case TYPE_MILLISECONDS:
-          if (((lval <= -1000) || (1000 <= lval)) &&
-              (outBufLen || (outBuf[-1] == '0'))) {
-            /* repaint as (fractional) seconds, possibly saving space */
-            if (outBufLen) outBuf[0] = outBuf[-1];
-            outBuf[-1] = outBuf[-2];
-            outBuf[-2] = outBuf[-3];
-            outBuf[-3] = '.';
-            while ((outBufLen == 0) || (*outBuf == '0')) {
-              --outBuf;
-              ++outBufLen;
-            }
-            if (*outBuf != '.') {
-              ++outBuf;
-              --outBufLen;
-            }
-            outCount = snprintf(outBuf, outBufLen, "s");
-          } else {
-            outCount = snprintf(outBuf, outBufLen, "ms");
-          }
-          break;
-        case TYPE_MONOTONIC: {
-          static const uint64_t minute = 60;
-          static const uint64_t hour = 60 * minute;
-          static const uint64_t day = 24 * hour;
-
-          /* Repaint as unsigned seconds, minutes, hours ... */
-          outBuf -= outCount;
-          outBufLen += outCount;
-          uint64_t val = lval;
-          if (val >= day) {
-            outCount = snprintf(outBuf, outBufLen, "%" PRIu64 "d ", val / day);
-            if (outCount >= outBufLen) break;
-            outBuf += outCount;
-            outBufLen -= outCount;
-            val = (val % day) + day;
-          }
-          if (val >= minute) {
-            if (val >= hour) {
-              outCount = snprintf(outBuf, outBufLen, "%" PRIu64 ":",
-                                  (val / hour) % (day / hour));
-              if (outCount >= outBufLen) break;
-              outBuf += outCount;
-              outBufLen -= outCount;
-            }
-            outCount =
-                snprintf(outBuf, outBufLen,
-                         (val >= hour) ? "%02" PRIu64 ":" : "%" PRIu64 ":",
-                         (val / minute) % (hour / minute));
-            if (outCount >= outBufLen) break;
-            outBuf += outCount;
-            outBufLen -= outCount;
-          }
-          outCount = snprintf(outBuf, outBufLen,
-                              (val >= minute) ? "%02" PRIu64 : "%" PRIu64 "s",
-                              val % minute);
-        } break;
-        case TYPE_ALLOCATIONS:
-          outCount = 0;
-          /* outCount = snprintf(outBuf, outBufLen, " allocations"); */
-          break;
-        case TYPE_ID:
-          outCount = 0;
-          break;
-        case TYPE_PERCENT:
-          outCount = snprintf(outBuf, outBufLen, "%%");
-          break;
-        default: /* ? */
-          outCount = 0;
-          break;
-      }
-      ++cp;
-      --len;
-      if (outCount < outBufLen) {
-        outBuf += outCount;
-        outBufLen -= outCount;
-      } else if (outCount) {
-        /* halt output */
-        goto no_room;
-      }
-    }
-    if (!findChar(&cp, &len, ')')) len = 0;
-    if (!findChar(&cp, &len, ',')) len = 0;
-  }
-
-bail:
-  *pEventData = eventData;
-  *pEventDataLen = eventDataLen;
-  *pOutBuf = outBuf;
-  *pOutBufLen = outBufLen;
-  if (cp) {
-    *fmtStr = cp;
-    *fmtLen = len;
-  }
-  return result;
-
-no_room:
-  result = 1;
-  goto bail;
-}
-
-/**
- * Convert a binary log entry to ASCII form.
- *
- * For convenience we mimic the processLogBuffer API.  There is no
- * pre-defined output length for the binary data, since we're free to format
- * it however we choose, which means we can't really use a fixed-size buffer
- * here.
- */
-LIBLOG_ABI_PUBLIC int android_log_processBinaryLogBuffer(
-    struct logger_entry* buf, AndroidLogEntry* entry,
-    const EventTagMap* map __unused, /* only on !__ANDROID__ */
-    char* messageBuf, int messageBufLen) {
-  size_t inCount;
-  uint32_t tagIndex;
-  const unsigned char* eventData;
-
-  entry->message = NULL;
-  entry->messageLen = 0;
-
-  entry->tv_sec = buf->sec;
-  entry->tv_nsec = buf->nsec;
-  entry->priority = ANDROID_LOG_INFO;
-  entry->uid = -1;
-  entry->pid = buf->pid;
-  entry->tid = buf->tid;
-
-  /*
-   * Pull the tag out, fill in some additional details based on incoming
-   * buffer version (v3 adds lid, v4 adds uid).
-   */
-  eventData = (const unsigned char*)buf->msg;
-  struct logger_entry_v2* buf2 = (struct logger_entry_v2*)buf;
-  if (buf2->hdr_size) {
-    if ((buf2->hdr_size < sizeof(((struct log_msg*)NULL)->entry_v1)) ||
-        (buf2->hdr_size > sizeof(((struct log_msg*)NULL)->entry))) {
-      fprintf(stderr, "+++ LOG: entry illegal hdr_size\n");
-      return -1;
-    }
-    eventData = ((unsigned char*)buf2) + buf2->hdr_size;
-    if ((buf2->hdr_size >= sizeof(struct logger_entry_v3)) &&
-        (((struct logger_entry_v3*)buf)->lid == LOG_ID_SECURITY)) {
-      entry->priority = ANDROID_LOG_WARN;
-    }
-    if (buf2->hdr_size >= sizeof(struct logger_entry_v4)) {
-      entry->uid = ((struct logger_entry_v4*)buf)->uid;
-    }
-  }
-  inCount = buf->len;
-  if (inCount < 4) return -1;
-  tagIndex = get4LE(eventData);
-  eventData += 4;
-  inCount -= 4;
-
-  entry->tagLen = 0;
-  entry->tag = NULL;
-#ifdef __ANDROID__
-  if (map != NULL) {
-    entry->tag = android_lookupEventTag_len(map, &entry->tagLen, tagIndex);
-  }
-#endif
-
-  /*
-   * If we don't have a map, or didn't find the tag number in the map,
-   * stuff a generated tag value into the start of the output buffer and
-   * shift the buffer pointers down.
-   */
-  if (entry->tag == NULL) {
-    size_t tagLen;
-
-    tagLen = snprintf(messageBuf, messageBufLen, "[%" PRIu32 "]", tagIndex);
-    if (tagLen >= (size_t)messageBufLen) {
-      tagLen = messageBufLen - 1;
-    }
-    entry->tag = messageBuf;
-    entry->tagLen = tagLen;
-    messageBuf += tagLen + 1;
-    messageBufLen -= tagLen + 1;
-  }
-
-  /*
-   * Format the event log data into the buffer.
-   */
-  const char* fmtStr = NULL;
-  size_t fmtLen = 0;
-#ifdef __ANDROID__
-  if (descriptive_output && map) {
-    fmtStr = android_lookupEventFormat_len(map, &fmtLen, tagIndex);
-  }
-#endif
-
-  char* outBuf = messageBuf;
-  size_t outRemaining = messageBufLen - 1; /* leave one for nul byte */
-  int result = 0;
-
-  if ((inCount > 0) || fmtLen) {
-    result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
-                                          &outRemaining, &fmtStr, &fmtLen);
-  }
-  if ((result == 1) && fmtStr) {
-    /* We overflowed :-(, let's repaint the line w/o format dressings */
-    eventData = (const unsigned char*)buf->msg;
-    if (buf2->hdr_size) {
-      eventData = ((unsigned char*)buf2) + buf2->hdr_size;
-    }
-    eventData += 4;
-    outBuf = messageBuf;
-    outRemaining = messageBufLen - 1;
-    result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
-                                          &outRemaining, NULL, NULL);
-  }
-  if (result < 0) {
-    fprintf(stderr, "Binary log entry conversion failed\n");
-  }
-  if (result) {
-    if (!outRemaining) {
-      /* make space to leave an indicator */
-      --outBuf;
-      ++outRemaining;
-    }
-    *outBuf++ = (result < 0) ? '!' : '^'; /* Error or Truncation? */
-    outRemaining--;
-    /* pretend we ate all the data to prevent log stutter */
-    inCount = 0;
-    if (result > 0) result = 0;
-  }
-
-  /* eat the silly terminating '\n' */
-  if (inCount == 1 && *eventData == '\n') {
-    eventData++;
-    inCount--;
-  }
-
-  if (inCount != 0) {
-    fprintf(stderr, "Warning: leftover binary log data (%zu bytes)\n", inCount);
-  }
-
-  /*
-   * Terminate the buffer.  The NUL byte does not count as part of
-   * entry->messageLen.
-   */
-  *outBuf = '\0';
-  entry->messageLen = outBuf - messageBuf;
-  assert(entry->messageLen == (messageBufLen - 1) - outRemaining);
-
-  entry->message = messageBuf;
-
-  return result;
-}
-
-/*
- * One utf8 character at a time
- *
- * Returns the length of the utf8 character in the buffer,
- * or -1 if illegal or truncated
- *
- * Open coded from libutils/Unicode.cpp, borrowed from utf8_length(),
- * can not remove from here because of library circular dependencies.
- * Expect one-day utf8_character_length with the same signature could
- * _also_ be part of libutils/Unicode.cpp if its usefullness needs to
- * propagate globally.
- */
-LIBLOG_WEAK ssize_t utf8_character_length(const char* src, size_t len) {
-  const char* cur = src;
-  const char first_char = *cur++;
-  static const uint32_t kUnicodeMaxCodepoint = 0x0010FFFF;
-  int32_t mask, to_ignore_mask;
-  size_t num_to_read;
-  uint32_t utf32;
-
-  if ((first_char & 0x80) == 0) { /* ASCII */
-    return first_char ? 1 : -1;
-  }
-
-  /*
-   * (UTF-8's character must not be like 10xxxxxx,
-   *  but 110xxxxx, 1110xxxx, ... or 1111110x)
-   */
-  if ((first_char & 0x40) == 0) {
-    return -1;
-  }
-
-  for (utf32 = 1, num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80;
-       num_to_read < 5 && (first_char & mask);
-       num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
-    if (num_to_read > len) {
-      return -1;
-    }
-    if ((*cur & 0xC0) != 0x80) { /* can not be 10xxxxxx? */
-      return -1;
-    }
-    utf32 = (utf32 << 6) + (*cur++ & 0b00111111);
-  }
-  /* "first_char" must be (110xxxxx - 11110xxx) */
-  if (num_to_read >= 5) {
-    return -1;
-  }
-  to_ignore_mask |= mask;
-  utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1));
-  if (utf32 > kUnicodeMaxCodepoint) {
-    return -1;
-  }
-  return num_to_read;
-}
-
-/*
- * Convert to printable from message to p buffer, return string length. If p is
- * NULL, do not copy, but still return the expected string length.
- */
-static size_t convertPrintable(char* p, const char* message, size_t messageLen) {
-  char* begin = p;
-  bool print = p != NULL;
-
-  while (messageLen) {
-    char buf[6];
-    ssize_t len = sizeof(buf) - 1;
-    if ((size_t)len > messageLen) {
-      len = messageLen;
-    }
-    len = utf8_character_length(message, len);
-
-    if (len < 0) {
-      snprintf(buf, sizeof(buf),
-               ((messageLen > 1) && isdigit(message[1])) ? "\\%03o" : "\\%o",
-               *message & 0377);
-      len = 1;
-    } else {
-      buf[0] = '\0';
-      if (len == 1) {
-        if (*message == '\a') {
-          strcpy(buf, "\\a");
-        } else if (*message == '\b') {
-          strcpy(buf, "\\b");
-        } else if (*message == '\t') {
-          strcpy(buf, "\t"); /* Do not escape tabs */
-        } else if (*message == '\v') {
-          strcpy(buf, "\\v");
-        } else if (*message == '\f') {
-          strcpy(buf, "\\f");
-        } else if (*message == '\r') {
-          strcpy(buf, "\\r");
-        } else if (*message == '\\') {
-          strcpy(buf, "\\\\");
-        } else if ((*message < ' ') || (*message & 0x80)) {
-          snprintf(buf, sizeof(buf), "\\%o", *message & 0377);
-        }
-      }
-      if (!buf[0]) {
-        strncpy(buf, message, len);
-        buf[len] = '\0';
-      }
-    }
-    if (print) {
-      strcpy(p, buf);
-    }
-    p += strlen(buf);
-    message += len;
-    messageLen -= len;
-  }
-  return p - begin;
-}
-
-static char* readSeconds(char* e, struct timespec* t) {
-  unsigned long multiplier;
-  char* p;
-  t->tv_sec = strtoul(e, &p, 10);
-  if (*p != '.') {
-    return NULL;
-  }
-  t->tv_nsec = 0;
-  multiplier = NS_PER_SEC;
-  while (isdigit(*++p) && (multiplier /= 10)) {
-    t->tv_nsec += (*p - '0') * multiplier;
-  }
-  return p;
-}
-
-static struct timespec* sumTimespec(struct timespec* left,
-                                    struct timespec* right) {
-  left->tv_nsec += right->tv_nsec;
-  left->tv_sec += right->tv_sec;
-  if (left->tv_nsec >= (long)NS_PER_SEC) {
-    left->tv_nsec -= NS_PER_SEC;
-    left->tv_sec += 1;
-  }
-  return left;
-}
-
-static struct timespec* subTimespec(struct timespec* result,
-                                    struct timespec* left,
-                                    struct timespec* right) {
-  result->tv_nsec = left->tv_nsec - right->tv_nsec;
-  result->tv_sec = left->tv_sec - right->tv_sec;
-  if (result->tv_nsec < 0) {
-    result->tv_nsec += NS_PER_SEC;
-    result->tv_sec -= 1;
-  }
-  return result;
-}
-
-static long long nsecTimespec(struct timespec* now) {
-  return (long long)now->tv_sec * NS_PER_SEC + now->tv_nsec;
-}
-
-#ifdef __ANDROID__
-static void convertMonotonic(struct timespec* result,
-                             const AndroidLogEntry* entry) {
-  struct listnode* node;
-  struct conversionList {
-    struct listnode node; /* first */
-    struct timespec time;
-    struct timespec convert;
-  } * list, *next;
-  struct timespec time, convert;
-
-  /* If we do not have a conversion list, build one up */
-  if (list_empty(&convertHead)) {
-    bool suspended_pending = false;
-    struct timespec suspended_monotonic = { 0, 0 };
-    struct timespec suspended_diff = { 0, 0 };
-
-    /*
-     * Read dmesg for _some_ synchronization markers and insert
-     * Anything in the Android Logger before the dmesg logging span will
-     * be highly suspect regarding the monotonic time calculations.
-     */
-    FILE* p = popen("/system/bin/dmesg", "re");
-    if (p) {
-      char* line = NULL;
-      size_t len = 0;
-      while (getline(&line, &len, p) > 0) {
-        static const char suspend[] = "PM: suspend entry ";
-        static const char resume[] = "PM: suspend exit ";
-        static const char healthd[] = "healthd";
-        static const char battery[] = ": battery ";
-        static const char suspended[] = "Suspended for ";
-        struct timespec monotonic;
-        struct tm tm;
-        char *cp, *e = line;
-        bool add_entry = true;
-
-        if (*e == '<') {
-          while (*e && (*e != '>')) {
-            ++e;
-          }
-          if (*e != '>') {
-            continue;
-          }
-        }
-        if (*e != '[') {
-          continue;
-        }
-        while (*++e == ' ') {
-          ;
-        }
-        e = readSeconds(e, &monotonic);
-        if (!e || (*e != ']')) {
-          continue;
-        }
-
-        if ((e = strstr(e, suspend))) {
-          e += sizeof(suspend) - 1;
-        } else if ((e = strstr(line, resume))) {
-          e += sizeof(resume) - 1;
-        } else if (((e = strstr(line, healthd))) &&
-                   ((e = strstr(e + sizeof(healthd) - 1, battery)))) {
-          /* NB: healthd is roughly 150us late, worth the price to
-           * deal with ntp-induced or hardware clock drift. */
-          e += sizeof(battery) - 1;
-        } else if ((e = strstr(line, suspended))) {
-          e += sizeof(suspended) - 1;
-          e = readSeconds(e, &time);
-          if (!e) {
-            continue;
-          }
-          add_entry = false;
-          suspended_pending = true;
-          suspended_monotonic = monotonic;
-          suspended_diff = time;
-        } else {
-          continue;
-        }
-        if (add_entry) {
-          /* look for "????-??-?? ??:??:??.????????? UTC" */
-          cp = strstr(e, " UTC");
-          if (!cp || ((cp - e) < 29) || (cp[-10] != '.')) {
-            continue;
-          }
-          e = cp - 29;
-          cp = readSeconds(cp - 10, &time);
-          if (!cp) {
-            continue;
-          }
-          cp = strptime(e, "%Y-%m-%d %H:%M:%S.", &tm);
-          if (!cp) {
-            continue;
-          }
-          cp = getenv(tz);
-          if (cp) {
-            cp = strdup(cp);
-          }
-          setenv(tz, utc, 1);
-          time.tv_sec = mktime(&tm);
-          if (cp) {
-            setenv(tz, cp, 1);
-            free(cp);
-          } else {
-            unsetenv(tz);
-          }
-          list = calloc(1, sizeof(struct conversionList));
-          list_init(&list->node);
-          list->time = time;
-          subTimespec(&list->convert, &time, &monotonic);
-          list_add_tail(&convertHead, &list->node);
-        }
-        if (suspended_pending && !list_empty(&convertHead)) {
-          list = node_to_item(list_tail(&convertHead), struct conversionList,
-                              node);
-          if (subTimespec(&time, subTimespec(&time, &list->time, &list->convert),
-                          &suspended_monotonic)
-                  ->tv_sec > 0) {
-            /* resume, what is convert factor before? */
-            subTimespec(&convert, &list->convert, &suspended_diff);
-          } else {
-            /* suspend */
-            convert = list->convert;
-          }
-          time = suspended_monotonic;
-          sumTimespec(&time, &convert);
-          /* breakpoint just before sleep */
-          list = calloc(1, sizeof(struct conversionList));
-          list_init(&list->node);
-          list->time = time;
-          list->convert = convert;
-          list_add_tail(&convertHead, &list->node);
-          /* breakpoint just after sleep */
-          list = calloc(1, sizeof(struct conversionList));
-          list_init(&list->node);
-          list->time = time;
-          sumTimespec(&list->time, &suspended_diff);
-          list->convert = convert;
-          sumTimespec(&list->convert, &suspended_diff);
-          list_add_tail(&convertHead, &list->node);
-          suspended_pending = false;
-        }
-      }
-      pclose(p);
-    }
-    /* last entry is our current time conversion */
-    list = calloc(1, sizeof(struct conversionList));
-    list_init(&list->node);
-    clock_gettime(CLOCK_REALTIME, &list->time);
-    clock_gettime(CLOCK_MONOTONIC, &convert);
-    clock_gettime(CLOCK_MONOTONIC, &time);
-    /* Correct for instant clock_gettime latency (syscall or ~30ns) */
-    subTimespec(&time, &convert, subTimespec(&time, &time, &convert));
-    /* Calculate conversion factor */
-    subTimespec(&list->convert, &list->time, &time);
-    list_add_tail(&convertHead, &list->node);
-    if (suspended_pending) {
-      /* manufacture a suspend @ point before */
-      subTimespec(&convert, &list->convert, &suspended_diff);
-      time = suspended_monotonic;
-      sumTimespec(&time, &convert);
-      /* breakpoint just after sleep */
-      list = calloc(1, sizeof(struct conversionList));
-      list_init(&list->node);
-      list->time = time;
-      sumTimespec(&list->time, &suspended_diff);
-      list->convert = convert;
-      sumTimespec(&list->convert, &suspended_diff);
-      list_add_head(&convertHead, &list->node);
-      /* breakpoint just before sleep */
-      list = calloc(1, sizeof(struct conversionList));
-      list_init(&list->node);
-      list->time = time;
-      list->convert = convert;
-      list_add_head(&convertHead, &list->node);
-    }
-  }
-
-  /* Find the breakpoint in the conversion list */
-  list = node_to_item(list_head(&convertHead), struct conversionList, node);
-  next = NULL;
-  list_for_each(node, &convertHead) {
-    next = node_to_item(node, struct conversionList, node);
-    if (entry->tv_sec < next->time.tv_sec) {
-      break;
-    } else if (entry->tv_sec == next->time.tv_sec) {
-      if (entry->tv_nsec < next->time.tv_nsec) {
-        break;
-      }
-    }
-    list = next;
-  }
-
-  /* blend time from one breakpoint to the next */
-  convert = list->convert;
-  if (next) {
-    unsigned long long total, run;
-
-    total = nsecTimespec(subTimespec(&time, &next->time, &list->time));
-    time.tv_sec = entry->tv_sec;
-    time.tv_nsec = entry->tv_nsec;
-    run = nsecTimespec(subTimespec(&time, &time, &list->time));
-    if (run < total) {
-      long long crun;
-
-      float f = nsecTimespec(subTimespec(&time, &next->convert, &convert));
-      f *= run;
-      f /= total;
-      crun = f;
-      convert.tv_sec += crun / (long long)NS_PER_SEC;
-      if (crun < 0) {
-        convert.tv_nsec -= (-crun) % NS_PER_SEC;
-        if (convert.tv_nsec < 0) {
-          convert.tv_nsec += NS_PER_SEC;
-          convert.tv_sec -= 1;
-        }
-      } else {
-        convert.tv_nsec += crun % NS_PER_SEC;
-        if (convert.tv_nsec >= (long)NS_PER_SEC) {
-          convert.tv_nsec -= NS_PER_SEC;
-          convert.tv_sec += 1;
-        }
-      }
-    }
-  }
-
-  /* Apply the correction factor */
-  result->tv_sec = entry->tv_sec;
-  result->tv_nsec = entry->tv_nsec;
-  subTimespec(result, result, &convert);
-}
-#endif
-
-/**
- * Formats a log message into a buffer
- *
- * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
- * If return value != defaultBuffer, caller must call free()
- * Returns NULL on malloc error
- */
-
-LIBLOG_ABI_PUBLIC char* android_log_formatLogLine(AndroidLogFormat* p_format,
-                                                  char* defaultBuffer,
-                                                  size_t defaultBufferSize,
-                                                  const AndroidLogEntry* entry,
-                                                  size_t* p_outLength) {
-#if !defined(_WIN32)
-  struct tm tmBuf;
-#endif
-  struct tm* ptm;
-  /* good margin, 23+nul for msec, 26+nul for usec, 29+nul to nsec */
-  char timeBuf[64];
-  char prefixBuf[128], suffixBuf[128];
-  char priChar;
-  int prefixSuffixIsHeaderFooter = 0;
-  char* ret;
-  time_t now;
-  unsigned long nsec;
-
-  priChar = filterPriToChar(entry->priority);
-  size_t prefixLen = 0, suffixLen = 0;
-  size_t len;
-
-  /*
-   * Get the current date/time in pretty form
-   *
-   * It's often useful when examining a log with "less" to jump to
-   * a specific point in the file by searching for the date/time stamp.
-   * For this reason it's very annoying to have regexp meta characters
-   * in the time stamp.  Don't use forward slashes, parenthesis,
-   * brackets, asterisks, or other special chars here.
-   *
-   * The caller may have affected the timezone environment, this is
-   * expected to be sensitive to that.
-   */
-  now = entry->tv_sec;
-  nsec = entry->tv_nsec;
-#if __ANDROID__
-  if (p_format->monotonic_output) {
-    /* prevent convertMonotonic from being called if logd is monotonic */
-    if (android_log_clockid() != CLOCK_MONOTONIC) {
-      struct timespec time;
-      convertMonotonic(&time, entry);
-      now = time.tv_sec;
-      nsec = time.tv_nsec;
-    }
-  }
-#endif
-  if (now < 0) {
-    nsec = NS_PER_SEC - nsec;
-  }
-  if (p_format->epoch_output || p_format->monotonic_output) {
-    ptm = NULL;
-    snprintf(timeBuf, sizeof(timeBuf),
-             p_format->monotonic_output ? "%6lld" : "%19lld", (long long)now);
-  } else {
-#if !defined(_WIN32)
-    ptm = localtime_r(&now, &tmBuf);
-#else
-    ptm = localtime(&now);
-#endif
-    strftime(timeBuf, sizeof(timeBuf),
-             &"%Y-%m-%d %H:%M:%S"[p_format->year_output ? 0 : 3], ptm);
-  }
-  len = strlen(timeBuf);
-  if (p_format->nsec_time_output) {
-    len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%09ld", nsec);
-  } else if (p_format->usec_time_output) {
-    len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%06ld",
-                    nsec / US_PER_NSEC);
-  } else {
-    len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%03ld",
-                    nsec / MS_PER_NSEC);
-  }
-  if (p_format->zone_output && ptm) {
-    strftime(timeBuf + len, sizeof(timeBuf) - len, " %z", ptm);
-  }
-
-  /*
-   * Construct a buffer containing the log header and log message.
-   */
-  if (p_format->colored_output) {
-    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));
-  }
-
-  char uid[16];
-  uid[0] = '\0';
-  if (p_format->uid_output) {
-    if (entry->uid >= 0) {
-/*
- * This code is Android specific, bionic guarantees that
- * calls to non-reentrant getpwuid() are thread safe.
- */
-#if !defined(__MINGW32__)
-#if (FAKE_LOG_DEVICE == 0)
-#ifndef __BIONIC__
-#warning \
-    "This code assumes that getpwuid is thread safe, only true with Bionic!"
-#endif
-#endif
-      struct passwd* pwd = getpwuid(entry->uid);
-      if (pwd && (strlen(pwd->pw_name) <= 5)) {
-        snprintf(uid, sizeof(uid), "%5s:", pwd->pw_name);
-      } else
-#endif
-      {
-        /* Not worth parsing package list, names all longer than 5 */
-        snprintf(uid, sizeof(uid), "%5d:", entry->uid);
-      }
-    } else {
-      snprintf(uid, sizeof(uid), "      ");
-    }
-  }
-
-  switch (p_format->format) {
-    case FORMAT_TAG:
-      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
-                     "%c/%-8.*s: ", priChar, (int)entry->tagLen, entry->tag);
-      strcpy(suffixBuf + suffixLen, "\n");
-      ++suffixLen;
-      break;
-    case FORMAT_PROCESS:
-      len = snprintf(suffixBuf + suffixLen, sizeof(suffixBuf) - suffixLen,
-                     "  (%.*s)\n", (int)entry->tagLen, entry->tag);
-      suffixLen += MIN(len, sizeof(suffixBuf) - suffixLen);
-      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
-                     "%c(%s%5d) ", priChar, uid, entry->pid);
-      break;
-    case FORMAT_THREAD:
-      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
-                     "%c(%s%5d:%5d) ", priChar, uid, entry->pid, entry->tid);
-      strcpy(suffixBuf + suffixLen, "\n");
-      ++suffixLen;
-      break;
-    case FORMAT_RAW:
-      prefixBuf[prefixLen] = 0;
-      len = 0;
-      strcpy(suffixBuf + suffixLen, "\n");
-      ++suffixLen;
-      break;
-    case FORMAT_TIME:
-      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
-                     "%s %c/%-8.*s(%s%5d): ", timeBuf, priChar,
-                     (int)entry->tagLen, entry->tag, uid, entry->pid);
-      strcpy(suffixBuf + suffixLen, "\n");
-      ++suffixLen;
-      break;
-    case FORMAT_THREADTIME:
-      ret = strchr(uid, ':');
-      if (ret) {
-        *ret = ' ';
-      }
-      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
-                     "%s %s%5d %5d %c %-8.*s: ", timeBuf, uid, entry->pid,
-                     entry->tid, priChar, (int)entry->tagLen, entry->tag);
-      strcpy(suffixBuf + suffixLen, "\n");
-      ++suffixLen;
-      break;
-    case FORMAT_LONG:
-      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
-                     "[ %s %s%5d:%5d %c/%-8.*s ]\n", timeBuf, uid, entry->pid,
-                     entry->tid, priChar, (int)entry->tagLen, entry->tag);
-      strcpy(suffixBuf + suffixLen, "\n\n");
-      suffixLen += 2;
-      prefixSuffixIsHeaderFooter = 1;
-      break;
-    case FORMAT_BRIEF:
-    default:
-      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
-                     "%c/%-8.*s(%s%5d): ", priChar, (int)entry->tagLen,
-                     entry->tag, uid, entry->pid);
-      strcpy(suffixBuf + suffixLen, "\n");
-      ++suffixLen;
-      break;
-  }
-
-  /* snprintf has a weird return value.   It returns what would have been
-   * written given a large enough buffer.  In the case that the prefix is
-   * longer then our buffer(128), it messes up the calculations below
-   * possibly causing heap corruption.  To avoid this we double check and
-   * set the length at the maximum (size minus null byte)
-   */
-  prefixLen += len;
-  if (prefixLen >= sizeof(prefixBuf)) {
-    prefixLen = sizeof(prefixBuf) - 1;
-    prefixBuf[sizeof(prefixBuf) - 1] = '\0';
-  }
-  if (suffixLen >= sizeof(suffixBuf)) {
-    suffixLen = sizeof(suffixBuf) - 1;
-    suffixBuf[sizeof(suffixBuf) - 2] = '\n';
-    suffixBuf[sizeof(suffixBuf) - 1] = '\0';
-  }
-
-  /* the following code is tragically unreadable */
-
-  size_t numLines;
-  char* p;
-  size_t bufferSize;
-  const char* pm;
-
-  if (prefixSuffixIsHeaderFooter) {
-    /* we're just wrapping message with a header/footer */
-    numLines = 1;
-  } else {
-    pm = entry->message;
-    numLines = 0;
-
-    /*
-     * The line-end finding here must match the line-end finding
-     * in for ( ... numLines...) loop below
-     */
-    while (pm < (entry->message + entry->messageLen)) {
-      if (*pm++ == '\n') numLines++;
-    }
-    /* plus one line for anything not newline-terminated at the end */
-    if (pm > entry->message && *(pm - 1) != '\n') numLines++;
-  }
-
-  /*
-   * this is an upper bound--newlines in message may be counted
-   * extraneously
-   */
-  bufferSize = (numLines * (prefixLen + suffixLen)) + 1;
-  if (p_format->printable_output) {
-    /* Calculate extra length to convert non-printable to printable */
-    bufferSize += convertPrintable(NULL, entry->message, entry->messageLen);
-  } else {
-    bufferSize += entry->messageLen;
-  }
-
-  if (defaultBufferSize >= bufferSize) {
-    ret = defaultBuffer;
-  } else {
-    ret = (char*)malloc(bufferSize);
-
-    if (ret == NULL) {
-      return ret;
-    }
-  }
-
-  ret[0] = '\0'; /* to start strcat off */
-
-  p = ret;
-  pm = entry->message;
-
-  if (prefixSuffixIsHeaderFooter) {
-    strcat(p, prefixBuf);
-    p += prefixLen;
-    if (p_format->printable_output) {
-      p += convertPrintable(p, entry->message, entry->messageLen);
-    } else {
-      strncat(p, entry->message, entry->messageLen);
-      p += entry->messageLen;
-    }
-    strcat(p, suffixBuf);
-    p += suffixLen;
-  } else {
-    do {
-      const char* lineStart;
-      size_t lineLen;
-      lineStart = pm;
-
-      /* Find the next end-of-line in message */
-      while (pm < (entry->message + entry->messageLen) && *pm != '\n') pm++;
-      lineLen = pm - lineStart;
-
-      strcat(p, prefixBuf);
-      p += prefixLen;
-      if (p_format->printable_output) {
-        p += convertPrintable(p, lineStart, lineLen);
-      } else {
-        strncat(p, lineStart, lineLen);
-        p += lineLen;
-      }
-      strcat(p, suffixBuf);
-      p += suffixLen;
-
-      if (*pm == '\n') pm++;
-    } while (pm < (entry->message + entry->messageLen));
-  }
-
-  if (p_outLength != NULL) {
-    *p_outLength = p - ret;
-  }
-
-  return ret;
-}
-
-/**
- * Either print or do not print log line, based on filter
- *
- * Returns count bytes written
- */
-
-LIBLOG_ABI_PUBLIC int android_log_printLogLine(AndroidLogFormat* p_format,
-                                               int fd,
-                                               const AndroidLogEntry* entry) {
-  int ret;
-  char defaultBuffer[512];
-  char* outBuffer = NULL;
-  size_t totalLen;
-
-  outBuffer = android_log_formatLogLine(
-      p_format, defaultBuffer, sizeof(defaultBuffer), entry, &totalLen);
-
-  if (!outBuffer) return -1;
-
-  do {
-    ret = write(fd, outBuffer, totalLen);
-  } while (ret < 0 && errno == EINTR);
-
-  if (ret < 0) {
-    fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
-    ret = 0;
-    goto done;
-  }
-
-  if (((size_t)ret) < totalLen) {
-    fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret, (int)totalLen);
-    goto done;
-  }
-
-done:
-  if (outBuffer != defaultBuffer) {
-    free(outBuffer);
-  }
-
-  return ret;
-}
diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp
new file mode 100644
index 0000000..6b5ea4c
--- /dev/null
+++ b/liblog/logprint.cpp
@@ -0,0 +1,1796 @@
+/*
+**
+** Copyright 2006-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.
+*/
+
+#ifndef __MINGW32__
+#define HAVE_STRSEP
+#endif
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#ifndef __MINGW32__
+#include <pwd.h>
+#endif
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <wchar.h>
+
+#include <cutils/list.h>
+#include <log/log.h>
+#include <log/logprint.h>
+
+#include "log_portability.h"
+
+#define MS_PER_NSEC 1000000
+#define US_PER_NSEC 1000
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+typedef struct FilterInfo_t {
+  char* mTag;
+  android_LogPriority mPri;
+  struct FilterInfo_t* p_next;
+} FilterInfo;
+
+struct AndroidLogFormat_t {
+  android_LogPriority global_pri;
+  FilterInfo* filters;
+  AndroidLogPrintFormat format;
+  bool colored_output;
+  bool usec_time_output;
+  bool nsec_time_output;
+  bool printable_output;
+  bool year_output;
+  bool zone_output;
+  bool epoch_output;
+  bool monotonic_output;
+  bool uid_output;
+  bool descriptive_output;
+};
+
+/*
+ * API issues prevent us from exposing "descriptive" in AndroidLogFormat_t
+ * during android_log_processBinaryLogBuffer(), so we break layering.
+ */
+static bool descriptive_output = false;
+
+/*
+ *  gnome-terminal color tags
+ *    See http://misc.flogisoft.com/bash/tip_colors_and_formatting
+ *    for ideas on how to set the forground color of the text for xterm.
+ *    The color manipulation character stream is defined as:
+ *      ESC [ 3 8 ; 5 ; <color#> m
+ */
+#define ANDROID_COLOR_BLUE 75
+#define ANDROID_COLOR_DEFAULT 231
+#define ANDROID_COLOR_GREEN 40
+#define ANDROID_COLOR_ORANGE 166
+#define ANDROID_COLOR_RED 196
+#define ANDROID_COLOR_YELLOW 226
+
+static FilterInfo* filterinfo_new(const char* tag, android_LogPriority pri) {
+  FilterInfo* p_ret;
+
+  p_ret = (FilterInfo*)calloc(1, sizeof(FilterInfo));
+  p_ret->mTag = strdup(tag);
+  p_ret->mPri = pri;
+
+  return p_ret;
+}
+
+/* balance to above, filterinfo_free left unimplemented */
+
+/*
+ * Note: also accepts 0-9 priorities
+ * returns ANDROID_LOG_UNKNOWN if the character is unrecognized
+ */
+static android_LogPriority filterCharToPri(char c) {
+  android_LogPriority pri;
+
+  c = tolower(c);
+
+  if (c >= '0' && c <= '9') {
+    if (c >= ('0' + ANDROID_LOG_SILENT)) {
+      pri = ANDROID_LOG_VERBOSE;
+    } else {
+      pri = (android_LogPriority)(c - '0');
+    }
+  } else if (c == 'v') {
+    pri = ANDROID_LOG_VERBOSE;
+  } else if (c == 'd') {
+    pri = ANDROID_LOG_DEBUG;
+  } else if (c == 'i') {
+    pri = ANDROID_LOG_INFO;
+  } else if (c == 'w') {
+    pri = ANDROID_LOG_WARN;
+  } else if (c == 'e') {
+    pri = ANDROID_LOG_ERROR;
+  } else if (c == 'f') {
+    pri = ANDROID_LOG_FATAL;
+  } else if (c == 's') {
+    pri = ANDROID_LOG_SILENT;
+  } else if (c == '*') {
+    pri = ANDROID_LOG_DEFAULT;
+  } else {
+    pri = ANDROID_LOG_UNKNOWN;
+  }
+
+  return pri;
+}
+
+static char filterPriToChar(android_LogPriority pri) {
+  switch (pri) {
+    /* clang-format off */
+    case ANDROID_LOG_VERBOSE: return 'V';
+    case ANDROID_LOG_DEBUG:   return 'D';
+    case ANDROID_LOG_INFO:    return 'I';
+    case ANDROID_LOG_WARN:    return 'W';
+    case ANDROID_LOG_ERROR:   return 'E';
+    case ANDROID_LOG_FATAL:   return 'F';
+    case ANDROID_LOG_SILENT:  return 'S';
+
+    case ANDROID_LOG_DEFAULT:
+    case ANDROID_LOG_UNKNOWN:
+    default:                  return '?';
+      /* clang-format on */
+  }
+}
+
+static int colorFromPri(android_LogPriority pri) {
+  switch (pri) {
+    /* clang-format off */
+    case ANDROID_LOG_VERBOSE: return ANDROID_COLOR_DEFAULT;
+    case ANDROID_LOG_DEBUG:   return ANDROID_COLOR_BLUE;
+    case ANDROID_LOG_INFO:    return ANDROID_COLOR_GREEN;
+    case ANDROID_LOG_WARN:    return ANDROID_COLOR_ORANGE;
+    case ANDROID_LOG_ERROR:   return ANDROID_COLOR_RED;
+    case ANDROID_LOG_FATAL:   return ANDROID_COLOR_RED;
+    case ANDROID_LOG_SILENT:  return ANDROID_COLOR_DEFAULT;
+
+    case ANDROID_LOG_DEFAULT:
+    case ANDROID_LOG_UNKNOWN:
+    default:                  return ANDROID_COLOR_DEFAULT;
+      /* clang-format on */
+  }
+}
+
+static android_LogPriority filterPriForTag(AndroidLogFormat* p_format, const char* tag) {
+  FilterInfo* p_curFilter;
+
+  for (p_curFilter = p_format->filters; p_curFilter != NULL; p_curFilter = p_curFilter->p_next) {
+    if (0 == strcmp(tag, p_curFilter->mTag)) {
+      if (p_curFilter->mPri == ANDROID_LOG_DEFAULT) {
+        return p_format->global_pri;
+      } else {
+        return p_curFilter->mPri;
+      }
+    }
+  }
+
+  return p_format->global_pri;
+}
+
+/**
+ * returns 1 if this log line should be printed based on its priority
+ * and tag, and 0 if it should not
+ */
+int android_log_shouldPrintLine(AndroidLogFormat* p_format, const char* tag,
+                                android_LogPriority pri) {
+  return pri >= filterPriForTag(p_format, tag);
+}
+
+AndroidLogFormat* android_log_format_new() {
+  AndroidLogFormat* p_ret;
+
+  p_ret = static_cast<AndroidLogFormat*>(calloc(1, sizeof(AndroidLogFormat)));
+
+  p_ret->global_pri = ANDROID_LOG_VERBOSE;
+  p_ret->format = FORMAT_BRIEF;
+  p_ret->colored_output = false;
+  p_ret->usec_time_output = false;
+  p_ret->nsec_time_output = false;
+  p_ret->printable_output = false;
+  p_ret->year_output = false;
+  p_ret->zone_output = false;
+  p_ret->epoch_output = false;
+#ifdef __ANDROID__
+  p_ret->monotonic_output = android_log_clockid() == CLOCK_MONOTONIC;
+#else
+  p_ret->monotonic_output = false;
+#endif
+  p_ret->uid_output = false;
+  p_ret->descriptive_output = false;
+  descriptive_output = false;
+
+  return p_ret;
+}
+
+static list_declare(convertHead);
+
+void android_log_format_free(AndroidLogFormat* p_format) {
+  FilterInfo *p_info, *p_info_old;
+
+  p_info = p_format->filters;
+
+  while (p_info != NULL) {
+    p_info_old = p_info;
+    p_info = p_info->p_next;
+
+    free(p_info_old);
+  }
+
+  free(p_format);
+
+  /* Free conversion resource, can always be reconstructed */
+  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);
+  }
+}
+
+int android_log_setPrintFormat(AndroidLogFormat* p_format, AndroidLogPrintFormat format) {
+  switch (format) {
+    case FORMAT_MODIFIER_COLOR:
+      p_format->colored_output = true;
+      return 0;
+    case FORMAT_MODIFIER_TIME_USEC:
+      p_format->usec_time_output = true;
+      return 0;
+    case FORMAT_MODIFIER_TIME_NSEC:
+      p_format->nsec_time_output = true;
+      return 0;
+    case FORMAT_MODIFIER_PRINTABLE:
+      p_format->printable_output = true;
+      return 0;
+    case FORMAT_MODIFIER_YEAR:
+      p_format->year_output = true;
+      return 0;
+    case FORMAT_MODIFIER_ZONE:
+      p_format->zone_output = !p_format->zone_output;
+      return 0;
+    case FORMAT_MODIFIER_EPOCH:
+      p_format->epoch_output = true;
+      return 0;
+    case FORMAT_MODIFIER_MONOTONIC:
+      p_format->monotonic_output = true;
+      return 0;
+    case FORMAT_MODIFIER_UID:
+      p_format->uid_output = true;
+      return 0;
+    case FORMAT_MODIFIER_DESCRIPT:
+      p_format->descriptive_output = true;
+      descriptive_output = true;
+      return 0;
+    default:
+      break;
+  }
+  p_format->format = format;
+  return 1;
+}
+
+static const char tz[] = "TZ";
+static const char utc[] = "UTC";
+
+/**
+ * Returns FORMAT_OFF on invalid string
+ */
+AndroidLogPrintFormat android_log_formatFromString(const char* formatString) {
+  static AndroidLogPrintFormat format;
+
+  /* clang-format off */
+  if (!strcmp(formatString, "brief")) format = FORMAT_BRIEF;
+  else if (!strcmp(formatString, "process")) format = FORMAT_PROCESS;
+  else if (!strcmp(formatString, "tag")) format = FORMAT_TAG;
+  else if (!strcmp(formatString, "thread")) format = FORMAT_THREAD;
+  else if (!strcmp(formatString, "raw")) format = FORMAT_RAW;
+  else if (!strcmp(formatString, "time")) format = FORMAT_TIME;
+  else if (!strcmp(formatString, "threadtime")) format = FORMAT_THREADTIME;
+  else if (!strcmp(formatString, "long")) format = FORMAT_LONG;
+  else if (!strcmp(formatString, "color")) format = FORMAT_MODIFIER_COLOR;
+  else if (!strcmp(formatString, "colour")) format = FORMAT_MODIFIER_COLOR;
+  else if (!strcmp(formatString, "usec")) format = FORMAT_MODIFIER_TIME_USEC;
+  else if (!strcmp(formatString, "nsec")) format = FORMAT_MODIFIER_TIME_NSEC;
+  else if (!strcmp(formatString, "printable")) format = FORMAT_MODIFIER_PRINTABLE;
+  else if (!strcmp(formatString, "year")) format = FORMAT_MODIFIER_YEAR;
+  else if (!strcmp(formatString, "zone")) format = FORMAT_MODIFIER_ZONE;
+  else if (!strcmp(formatString, "epoch")) format = FORMAT_MODIFIER_EPOCH;
+  else if (!strcmp(formatString, "monotonic")) format = FORMAT_MODIFIER_MONOTONIC;
+  else if (!strcmp(formatString, "uid")) format = FORMAT_MODIFIER_UID;
+  else if (!strcmp(formatString, "descriptive")) format = FORMAT_MODIFIER_DESCRIPT;
+    /* clang-format on */
+
+#ifndef __MINGW32__
+  else {
+    extern char* tzname[2];
+    static const char gmt[] = "GMT";
+    char* cp = getenv(tz);
+    if (cp) {
+      cp = strdup(cp);
+    }
+    setenv(tz, formatString, 1);
+    /*
+     * Run tzset here to determine if the timezone is legitimate. If the
+     * zone is GMT, check if that is what was asked for, if not then
+     * did not match any on the system; report an error to caller.
+     */
+    tzset();
+    if (!tzname[0] ||
+        ((!strcmp(tzname[0], utc) || !strcmp(tzname[0], gmt))                  /* error? */
+         && strcasecmp(formatString, utc) && strcasecmp(formatString, gmt))) { /* ok */
+      if (cp) {
+        setenv(tz, cp, 1);
+      } else {
+        unsetenv(tz);
+      }
+      tzset();
+      format = FORMAT_OFF;
+    } else {
+      format = FORMAT_MODIFIER_ZONE;
+    }
+    free(cp);
+  }
+#endif
+
+  return format;
+}
+
+/**
+ * filterExpression: a single filter expression
+ * eg "AT:d"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ */
+
+int android_log_addFilterRule(AndroidLogFormat* p_format, const char* filterExpression) {
+  size_t tagNameLength;
+  android_LogPriority pri = ANDROID_LOG_DEFAULT;
+
+  tagNameLength = strcspn(filterExpression, ":");
+
+  if (tagNameLength == 0) {
+    goto error;
+  }
+
+  if (filterExpression[tagNameLength] == ':') {
+    pri = filterCharToPri(filterExpression[tagNameLength + 1]);
+
+    if (pri == ANDROID_LOG_UNKNOWN) {
+      goto error;
+    }
+  }
+
+  if (0 == strncmp("*", filterExpression, tagNameLength)) {
+    /*
+     * This filter expression refers to the global filter
+     * The default level for this is DEBUG if the priority
+     * is unspecified
+     */
+    if (pri == ANDROID_LOG_DEFAULT) {
+      pri = ANDROID_LOG_DEBUG;
+    }
+
+    p_format->global_pri = pri;
+  } else {
+    /*
+     * for filter expressions that don't refer to the global
+     * filter, the default is verbose if the priority is unspecified
+     */
+    if (pri == ANDROID_LOG_DEFAULT) {
+      pri = ANDROID_LOG_VERBOSE;
+    }
+
+    char* tagName;
+
+/*
+ * Presently HAVE_STRNDUP is never defined, so the second case is always taken
+ * Darwin doesn't have strndup, everything else does
+ */
+#ifdef HAVE_STRNDUP
+    tagName = strndup(filterExpression, tagNameLength);
+#else
+    /* a few extra bytes copied... */
+    tagName = strdup(filterExpression);
+    tagName[tagNameLength] = '\0';
+#endif /*HAVE_STRNDUP*/
+
+    FilterInfo* p_fi = filterinfo_new(tagName, pri);
+    free(tagName);
+
+    p_fi->p_next = p_format->filters;
+    p_format->filters = p_fi;
+  }
+
+  return 0;
+error:
+  return -1;
+}
+
+#ifndef HAVE_STRSEP
+/* KISS replacement helper for below */
+static char* strsep(char** stringp, const char* delim) {
+  char* token;
+  char* ret = *stringp;
+
+  if (!ret || !*ret) {
+    return NULL;
+  }
+  token = strpbrk(ret, delim);
+  if (token) {
+    *token = '\0';
+    ++token;
+  } else {
+    token = ret + strlen(ret);
+  }
+  *stringp = token;
+  return ret;
+}
+#endif
+
+/**
+ * filterString: a comma/whitespace-separated set of filter expressions
+ *
+ * eg "AT:d *:i"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ *
+ */
+int android_log_addFilterString(AndroidLogFormat* p_format, const char* filterString) {
+  char* filterStringCopy = strdup(filterString);
+  char* p_cur = filterStringCopy;
+  char* p_ret;
+  int err;
+
+  /* Yes, I'm using strsep */
+  while (NULL != (p_ret = strsep(&p_cur, " \t,"))) {
+    /* ignore whitespace-only entries */
+    if (p_ret[0] != '\0') {
+      err = android_log_addFilterRule(p_format, p_ret);
+
+      if (err < 0) {
+        goto error;
+      }
+    }
+  }
+
+  free(filterStringCopy);
+  return 0;
+error:
+  free(filterStringCopy);
+  return -1;
+}
+
+/**
+ * Splits a wire-format buffer into an AndroidLogEntry
+ * entry allocated by caller. Pointers will point directly into buf
+ *
+ * Returns 0 on success and -1 on invalid wire format (entry will be
+ * in unspecified state)
+ */
+int android_log_processLogBuffer(struct logger_entry* buf, AndroidLogEntry* entry) {
+  entry->message = NULL;
+  entry->messageLen = 0;
+
+  entry->tv_sec = buf->sec;
+  entry->tv_nsec = buf->nsec;
+  entry->uid = -1;
+  entry->pid = buf->pid;
+  entry->tid = buf->tid;
+
+  /*
+   * format: <priority:1><tag:N>\0<message:N>\0
+   *
+   * tag str
+   *   starts at buf->msg+1
+   * msg
+   *   starts at buf->msg+1+len(tag)+1
+   *
+   * The message may have been truncated by the kernel log driver.
+   * When that happens, we must null-terminate the message ourselves.
+   */
+  if (buf->len < 3) {
+    /*
+     * An well-formed entry must consist of at least a priority
+     * and two null characters
+     */
+    fprintf(stderr, "+++ LOG: entry too small\n");
+    return -1;
+  }
+
+  int msgStart = -1;
+  int msgEnd = -1;
+
+  int i;
+  char* msg = buf->msg;
+  struct logger_entry_v2* buf2 = (struct logger_entry_v2*)buf;
+  if (buf2->hdr_size) {
+    if ((buf2->hdr_size < sizeof(((struct log_msg*)NULL)->entry_v1)) ||
+        (buf2->hdr_size > sizeof(((struct log_msg*)NULL)->entry))) {
+      fprintf(stderr, "+++ LOG: entry illegal hdr_size\n");
+      return -1;
+    }
+    msg = ((char*)buf2) + buf2->hdr_size;
+    if (buf2->hdr_size >= sizeof(struct logger_entry_v4)) {
+      entry->uid = ((struct logger_entry_v4*)buf)->uid;
+    }
+  }
+  for (i = 1; i < buf->len; i++) {
+    if (msg[i] == '\0') {
+      if (msgStart == -1) {
+        msgStart = i + 1;
+      } else {
+        msgEnd = i;
+        break;
+      }
+    }
+  }
+
+  if (msgStart == -1) {
+    /* +++ LOG: malformed log message, DYB */
+    for (i = 1; i < buf->len; i++) {
+      /* odd characters in tag? */
+      if ((msg[i] <= ' ') || (msg[i] == ':') || (msg[i] >= 0x7f)) {
+        msg[i] = '\0';
+        msgStart = i + 1;
+        break;
+      }
+    }
+    if (msgStart == -1) {
+      msgStart = buf->len - 1; /* All tag, no message, print truncates */
+    }
+  }
+  if (msgEnd == -1) {
+    /* incoming message not null-terminated; force it */
+    msgEnd = buf->len - 1; /* may result in msgEnd < msgStart */
+    msg[msgEnd] = '\0';
+  }
+
+  entry->priority = static_cast<android_LogPriority>(msg[0]);
+  entry->tag = msg + 1;
+  entry->tagLen = msgStart - 1;
+  entry->message = msg + msgStart;
+  entry->messageLen = (msgEnd < msgStart) ? 0 : (msgEnd - msgStart);
+
+  return 0;
+}
+
+/*
+ * Extract a 4-byte value from a byte stream.
+ */
+static inline uint32_t get4LE(const uint8_t* src) {
+  return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+/*
+ * Extract an 8-byte value from a byte stream.
+ */
+static inline uint64_t get8LE(const uint8_t* src) {
+  uint32_t low, high;
+
+  low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+  high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
+  return ((uint64_t)high << 32) | (uint64_t)low;
+}
+
+static bool findChar(const char** cp, size_t* len, int c) {
+  while ((*len) && isspace(*(*cp))) {
+    ++(*cp);
+    --(*len);
+  }
+  if (c == INT_MAX) return *len;
+  if ((*len) && (*(*cp) == c)) {
+    ++(*cp);
+    --(*len);
+    return true;
+  }
+  return false;
+}
+
+/*
+ * Recursively convert binary log data to printable form.
+ *
+ * This needs to be recursive because you can have lists of lists.
+ *
+ * If we run out of room, we stop processing immediately.  It's important
+ * for us to check for space on every output element to avoid producing
+ * garbled output.
+ *
+ * Returns 0 on success, 1 on buffer full, -1 on failure.
+ */
+enum objectType {
+  TYPE_OBJECTS = '1',
+  TYPE_BYTES = '2',
+  TYPE_MILLISECONDS = '3',
+  TYPE_ALLOCATIONS = '4',
+  TYPE_ID = '5',
+  TYPE_PERCENT = '6',
+  TYPE_MONOTONIC = 's'
+};
+
+static int android_log_printBinaryEvent(const unsigned char** pEventData, size_t* pEventDataLen,
+                                        char** pOutBuf, size_t* pOutBufLen, const char** fmtStr,
+                                        size_t* fmtLen) {
+  const unsigned char* eventData = *pEventData;
+  size_t eventDataLen = *pEventDataLen;
+  char* outBuf = *pOutBuf;
+  char* outBufSave = outBuf;
+  size_t outBufLen = *pOutBufLen;
+  size_t outBufLenSave = outBufLen;
+  unsigned char type;
+  size_t outCount = 0;
+  int result = 0;
+  const char* cp;
+  size_t len;
+  int64_t lval;
+
+  if (eventDataLen < 1) return -1;
+
+  type = *eventData++;
+  eventDataLen--;
+
+  cp = NULL;
+  len = 0;
+  if (fmtStr && *fmtStr && fmtLen && *fmtLen && **fmtStr) {
+    cp = *fmtStr;
+    len = *fmtLen;
+  }
+  /*
+   * event.logtag format specification:
+   *
+   * 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
+   * s: Number of seconds (monotonic time)
+   * Default value for data of type int/long is 2 (bytes).
+   */
+  if (!cp || !findChar(&cp, &len, '(')) {
+    len = 0;
+  } else {
+    char* outBufLastSpace = NULL;
+
+    findChar(&cp, &len, INT_MAX);
+    while (len && *cp && (*cp != '|') && (*cp != ')')) {
+      if (outBufLen <= 0) {
+        /* halt output */
+        goto no_room;
+      }
+      outBufLastSpace = isspace(*cp) ? outBuf : NULL;
+      *outBuf = *cp;
+      ++outBuf;
+      ++cp;
+      --outBufLen;
+      --len;
+    }
+    if (outBufLastSpace) {
+      outBufLen += outBuf - outBufLastSpace;
+      outBuf = outBufLastSpace;
+    }
+    if (outBufLen <= 0) {
+      /* halt output */
+      goto no_room;
+    }
+    if (outBufSave != outBuf) {
+      *outBuf = '=';
+      ++outBuf;
+      --outBufLen;
+    }
+
+    if (findChar(&cp, &len, '|') && findChar(&cp, &len, INT_MAX)) {
+      static const unsigned char typeTable[] = {EVENT_TYPE_INT, EVENT_TYPE_LONG, EVENT_TYPE_STRING,
+                                                EVENT_TYPE_LIST, EVENT_TYPE_FLOAT};
+
+      if ((*cp >= '1') && (*cp < (char)('1' + (sizeof(typeTable) / sizeof(typeTable[0])))) &&
+          (type != typeTable[(size_t)(*cp - '1')]))
+        len = 0;
+
+      if (len) {
+        ++cp;
+        --len;
+      } else {
+        /* reset the format */
+        outBuf = outBufSave;
+        outBufLen = outBufLenSave;
+      }
+    }
+  }
+  outCount = 0;
+  lval = 0;
+  switch (type) {
+    case EVENT_TYPE_INT:
+      /* 32-bit signed int */
+      {
+        int32_t ival;
+
+        if (eventDataLen < 4) return -1;
+        ival = get4LE(eventData);
+        eventData += 4;
+        eventDataLen -= 4;
+
+        lval = ival;
+      }
+      goto pr_lval;
+    case EVENT_TYPE_LONG:
+      /* 64-bit signed long */
+      if (eventDataLen < 8) return -1;
+      lval = get8LE(eventData);
+      eventData += 8;
+      eventDataLen -= 8;
+    pr_lval:
+      outCount = snprintf(outBuf, outBufLen, "%" PRId64, lval);
+      if (outCount < outBufLen) {
+        outBuf += outCount;
+        outBufLen -= outCount;
+      } else {
+        /* halt output */
+        goto no_room;
+      }
+      break;
+    case EVENT_TYPE_FLOAT:
+      /* float */
+      {
+        uint32_t ival;
+        float fval;
+
+        if (eventDataLen < 4) return -1;
+        ival = get4LE(eventData);
+        fval = *(float*)&ival;
+        eventData += 4;
+        eventDataLen -= 4;
+
+        outCount = snprintf(outBuf, outBufLen, "%f", fval);
+        if (outCount < outBufLen) {
+          outBuf += outCount;
+          outBufLen -= outCount;
+        } else {
+          /* halt output */
+          goto no_room;
+        }
+      }
+      break;
+    case EVENT_TYPE_STRING:
+      /* UTF-8 chars, not NULL-terminated */
+      {
+        unsigned int strLen;
+
+        if (eventDataLen < 4) return -1;
+        strLen = get4LE(eventData);
+        eventData += 4;
+        eventDataLen -= 4;
+
+        if (eventDataLen < strLen) {
+          result = -1; /* mark truncated */
+          strLen = eventDataLen;
+        }
+
+        if (cp && (strLen == 0)) {
+          /* reset the format if no content */
+          outBuf = outBufSave;
+          outBufLen = outBufLenSave;
+        }
+        if (strLen < outBufLen) {
+          memcpy(outBuf, eventData, strLen);
+          outBuf += strLen;
+          outBufLen -= strLen;
+        } else {
+          if (outBufLen > 0) {
+            /* copy what we can */
+            memcpy(outBuf, eventData, outBufLen);
+            outBuf += outBufLen;
+            outBufLen -= outBufLen;
+          }
+          if (!result) result = 1; /* if not truncated, return no room */
+        }
+        eventData += strLen;
+        eventDataLen -= strLen;
+        if (result != 0) goto bail;
+        break;
+      }
+    case EVENT_TYPE_LIST:
+      /* N items, all different types */
+      {
+        unsigned char count;
+        int i;
+
+        if (eventDataLen < 1) return -1;
+
+        count = *eventData++;
+        eventDataLen--;
+
+        if (outBufLen <= 0) goto no_room;
+
+        *outBuf++ = '[';
+        outBufLen--;
+
+        for (i = 0; i < count; i++) {
+          result = android_log_printBinaryEvent(&eventData, &eventDataLen, &outBuf, &outBufLen,
+                                                fmtStr, fmtLen);
+          if (result != 0) goto bail;
+
+          if (i < (count - 1)) {
+            if (outBufLen <= 0) goto no_room;
+            *outBuf++ = ',';
+            outBufLen--;
+          }
+        }
+
+        if (outBufLen <= 0) goto no_room;
+
+        *outBuf++ = ']';
+        outBufLen--;
+      }
+      break;
+    default:
+      fprintf(stderr, "Unknown binary event type %d\n", type);
+      return -1;
+  }
+  if (cp && len) {
+    if (findChar(&cp, &len, '|') && findChar(&cp, &len, INT_MAX)) {
+      switch (*cp) {
+        case TYPE_OBJECTS:
+          outCount = 0;
+          /* outCount = snprintf(outBuf, outBufLen, " objects"); */
+          break;
+        case TYPE_BYTES:
+          if ((lval != 0) && ((lval % 1024) == 0)) {
+            /* repaint with multiplier */
+            static const char suffixTable[] = {'K', 'M', 'G', 'T'};
+            size_t idx = 0;
+            outBuf -= outCount;
+            outBufLen += outCount;
+            do {
+              lval /= 1024;
+              if ((lval % 1024) != 0) break;
+            } while (++idx < ((sizeof(suffixTable) / sizeof(suffixTable[0])) - 1));
+            outCount = snprintf(outBuf, outBufLen, "%" PRId64 "%cB", lval, suffixTable[idx]);
+          } else {
+            outCount = snprintf(outBuf, outBufLen, "B");
+          }
+          break;
+        case TYPE_MILLISECONDS:
+          if (((lval <= -1000) || (1000 <= lval)) && (outBufLen || (outBuf[-1] == '0'))) {
+            /* repaint as (fractional) seconds, possibly saving space */
+            if (outBufLen) outBuf[0] = outBuf[-1];
+            outBuf[-1] = outBuf[-2];
+            outBuf[-2] = outBuf[-3];
+            outBuf[-3] = '.';
+            while ((outBufLen == 0) || (*outBuf == '0')) {
+              --outBuf;
+              ++outBufLen;
+            }
+            if (*outBuf != '.') {
+              ++outBuf;
+              --outBufLen;
+            }
+            outCount = snprintf(outBuf, outBufLen, "s");
+          } else {
+            outCount = snprintf(outBuf, outBufLen, "ms");
+          }
+          break;
+        case TYPE_MONOTONIC: {
+          static const uint64_t minute = 60;
+          static const uint64_t hour = 60 * minute;
+          static const uint64_t day = 24 * hour;
+
+          /* Repaint as unsigned seconds, minutes, hours ... */
+          outBuf -= outCount;
+          outBufLen += outCount;
+          uint64_t val = lval;
+          if (val >= day) {
+            outCount = snprintf(outBuf, outBufLen, "%" PRIu64 "d ", val / day);
+            if (outCount >= outBufLen) break;
+            outBuf += outCount;
+            outBufLen -= outCount;
+            val = (val % day) + day;
+          }
+          if (val >= minute) {
+            if (val >= hour) {
+              outCount = snprintf(outBuf, outBufLen, "%" PRIu64 ":", (val / hour) % (day / hour));
+              if (outCount >= outBufLen) break;
+              outBuf += outCount;
+              outBufLen -= outCount;
+            }
+            outCount =
+                snprintf(outBuf, outBufLen, (val >= hour) ? "%02" PRIu64 ":" : "%" PRIu64 ":",
+                         (val / minute) % (hour / minute));
+            if (outCount >= outBufLen) break;
+            outBuf += outCount;
+            outBufLen -= outCount;
+          }
+          outCount = snprintf(outBuf, outBufLen, (val >= minute) ? "%02" PRIu64 : "%" PRIu64 "s",
+                              val % minute);
+        } break;
+        case TYPE_ALLOCATIONS:
+          outCount = 0;
+          /* outCount = snprintf(outBuf, outBufLen, " allocations"); */
+          break;
+        case TYPE_ID:
+          outCount = 0;
+          break;
+        case TYPE_PERCENT:
+          outCount = snprintf(outBuf, outBufLen, "%%");
+          break;
+        default: /* ? */
+          outCount = 0;
+          break;
+      }
+      ++cp;
+      --len;
+      if (outCount < outBufLen) {
+        outBuf += outCount;
+        outBufLen -= outCount;
+      } else if (outCount) {
+        /* halt output */
+        goto no_room;
+      }
+    }
+    if (!findChar(&cp, &len, ')')) len = 0;
+    if (!findChar(&cp, &len, ',')) len = 0;
+  }
+
+bail:
+  *pEventData = eventData;
+  *pEventDataLen = eventDataLen;
+  *pOutBuf = outBuf;
+  *pOutBufLen = outBufLen;
+  if (cp) {
+    *fmtStr = cp;
+    *fmtLen = len;
+  }
+  return result;
+
+no_room:
+  result = 1;
+  goto bail;
+}
+
+/**
+ * Convert a binary log entry to ASCII form.
+ *
+ * For convenience we mimic the processLogBuffer API.  There is no
+ * pre-defined output length for the binary data, since we're free to format
+ * it however we choose, which means we can't really use a fixed-size buffer
+ * here.
+ */
+int android_log_processBinaryLogBuffer(
+    struct logger_entry* buf, AndroidLogEntry* entry,
+    [[maybe_unused]] const EventTagMap* map, /* only on !__ANDROID__ */
+    char* messageBuf, int messageBufLen) {
+  size_t inCount;
+  uint32_t tagIndex;
+  const unsigned char* eventData;
+
+  entry->message = NULL;
+  entry->messageLen = 0;
+
+  entry->tv_sec = buf->sec;
+  entry->tv_nsec = buf->nsec;
+  entry->priority = ANDROID_LOG_INFO;
+  entry->uid = -1;
+  entry->pid = buf->pid;
+  entry->tid = buf->tid;
+
+  /*
+   * Pull the tag out, fill in some additional details based on incoming
+   * buffer version (v3 adds lid, v4 adds uid).
+   */
+  eventData = (const unsigned char*)buf->msg;
+  struct logger_entry_v2* buf2 = (struct logger_entry_v2*)buf;
+  if (buf2->hdr_size) {
+    if ((buf2->hdr_size < sizeof(((struct log_msg*)NULL)->entry_v1)) ||
+        (buf2->hdr_size > sizeof(((struct log_msg*)NULL)->entry))) {
+      fprintf(stderr, "+++ LOG: entry illegal hdr_size\n");
+      return -1;
+    }
+    eventData = ((unsigned char*)buf2) + buf2->hdr_size;
+    if ((buf2->hdr_size >= sizeof(struct logger_entry_v3)) &&
+        (((struct logger_entry_v3*)buf)->lid == LOG_ID_SECURITY)) {
+      entry->priority = ANDROID_LOG_WARN;
+    }
+    if (buf2->hdr_size >= sizeof(struct logger_entry_v4)) {
+      entry->uid = ((struct logger_entry_v4*)buf)->uid;
+    }
+  }
+  inCount = buf->len;
+  if (inCount < 4) return -1;
+  tagIndex = get4LE(eventData);
+  eventData += 4;
+  inCount -= 4;
+
+  entry->tagLen = 0;
+  entry->tag = NULL;
+#ifdef __ANDROID__
+  if (map != NULL) {
+    entry->tag = android_lookupEventTag_len(map, &entry->tagLen, tagIndex);
+  }
+#endif
+
+  /*
+   * If we don't have a map, or didn't find the tag number in the map,
+   * stuff a generated tag value into the start of the output buffer and
+   * shift the buffer pointers down.
+   */
+  if (entry->tag == NULL) {
+    size_t tagLen;
+
+    tagLen = snprintf(messageBuf, messageBufLen, "[%" PRIu32 "]", tagIndex);
+    if (tagLen >= (size_t)messageBufLen) {
+      tagLen = messageBufLen - 1;
+    }
+    entry->tag = messageBuf;
+    entry->tagLen = tagLen;
+    messageBuf += tagLen + 1;
+    messageBufLen -= tagLen + 1;
+  }
+
+  /*
+   * Format the event log data into the buffer.
+   */
+  const char* fmtStr = NULL;
+  size_t fmtLen = 0;
+#ifdef __ANDROID__
+  if (descriptive_output && map) {
+    fmtStr = android_lookupEventFormat_len(map, &fmtLen, tagIndex);
+  }
+#endif
+
+  char* outBuf = messageBuf;
+  size_t outRemaining = messageBufLen - 1; /* leave one for nul byte */
+  int result = 0;
+
+  if ((inCount > 0) || fmtLen) {
+    result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf, &outRemaining, &fmtStr,
+                                          &fmtLen);
+  }
+  if ((result == 1) && fmtStr) {
+    /* We overflowed :-(, let's repaint the line w/o format dressings */
+    eventData = (const unsigned char*)buf->msg;
+    if (buf2->hdr_size) {
+      eventData = ((unsigned char*)buf2) + buf2->hdr_size;
+    }
+    eventData += 4;
+    outBuf = messageBuf;
+    outRemaining = messageBufLen - 1;
+    result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf, &outRemaining, NULL, NULL);
+  }
+  if (result < 0) {
+    fprintf(stderr, "Binary log entry conversion failed\n");
+  }
+  if (result) {
+    if (!outRemaining) {
+      /* make space to leave an indicator */
+      --outBuf;
+      ++outRemaining;
+    }
+    *outBuf++ = (result < 0) ? '!' : '^'; /* Error or Truncation? */
+    outRemaining--;
+    /* pretend we ate all the data to prevent log stutter */
+    inCount = 0;
+    if (result > 0) result = 0;
+  }
+
+  /* eat the silly terminating '\n' */
+  if (inCount == 1 && *eventData == '\n') {
+    eventData++;
+    inCount--;
+  }
+
+  if (inCount != 0) {
+    fprintf(stderr, "Warning: leftover binary log data (%zu bytes)\n", inCount);
+  }
+
+  /*
+   * Terminate the buffer.  The NUL byte does not count as part of
+   * entry->messageLen.
+   */
+  *outBuf = '\0';
+  entry->messageLen = outBuf - messageBuf;
+  assert(entry->messageLen == (messageBufLen - 1) - outRemaining);
+
+  entry->message = messageBuf;
+
+  return result;
+}
+
+/*
+ * Convert to printable from message to p buffer, return string length. If p is
+ * NULL, do not copy, but still return the expected string length.
+ */
+size_t convertPrintable(char* p, const char* message, size_t messageLen) {
+  char* begin = p;
+  bool print = p != NULL;
+  mbstate_t mb_state = {};
+
+  while (messageLen) {
+    char buf[6];
+    ssize_t len = sizeof(buf) - 1;
+    if ((size_t)len > messageLen) {
+      len = messageLen;
+    }
+    len = mbrtowc(nullptr, message, len, &mb_state);
+
+    if (len < 0) {
+      snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(*message));
+      len = 1;
+    } else {
+      buf[0] = '\0';
+      if (len == 1) {
+        if (*message == '\a') {
+          strcpy(buf, "\\a");
+        } else if (*message == '\b') {
+          strcpy(buf, "\\b");
+        } else if (*message == '\t') {
+          strcpy(buf, "\t"); /* Do not escape tabs */
+        } else if (*message == '\v') {
+          strcpy(buf, "\\v");
+        } else if (*message == '\f') {
+          strcpy(buf, "\\f");
+        } else if (*message == '\r') {
+          strcpy(buf, "\\r");
+        } else if (*message == '\\') {
+          strcpy(buf, "\\\\");
+        } else if ((*message < ' ') || (*message & 0x80)) {
+          snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(*message));
+        }
+      }
+      if (!buf[0]) {
+        strncpy(buf, message, len);
+        buf[len] = '\0';
+      }
+    }
+    if (print) {
+      strcpy(p, buf);
+    }
+    p += strlen(buf);
+    message += len;
+    messageLen -= len;
+  }
+  return p - begin;
+}
+
+static char* readSeconds(char* e, struct timespec* t) {
+  unsigned long multiplier;
+  char* p;
+  t->tv_sec = strtoul(e, &p, 10);
+  if (*p != '.') {
+    return NULL;
+  }
+  t->tv_nsec = 0;
+  multiplier = NS_PER_SEC;
+  while (isdigit(*++p) && (multiplier /= 10)) {
+    t->tv_nsec += (*p - '0') * multiplier;
+  }
+  return p;
+}
+
+static struct timespec* sumTimespec(struct timespec* left, struct timespec* right) {
+  left->tv_nsec += right->tv_nsec;
+  left->tv_sec += right->tv_sec;
+  if (left->tv_nsec >= (long)NS_PER_SEC) {
+    left->tv_nsec -= NS_PER_SEC;
+    left->tv_sec += 1;
+  }
+  return left;
+}
+
+static struct timespec* subTimespec(struct timespec* result, struct timespec* left,
+                                    struct timespec* right) {
+  result->tv_nsec = left->tv_nsec - right->tv_nsec;
+  result->tv_sec = left->tv_sec - right->tv_sec;
+  if (result->tv_nsec < 0) {
+    result->tv_nsec += NS_PER_SEC;
+    result->tv_sec -= 1;
+  }
+  return result;
+}
+
+static long long nsecTimespec(struct timespec* now) {
+  return (long long)now->tv_sec * NS_PER_SEC + now->tv_nsec;
+}
+
+#ifdef __ANDROID__
+static void convertMonotonic(struct timespec* result, const AndroidLogEntry* entry) {
+  struct listnode* node;
+  struct conversionList {
+    struct listnode node; /* first */
+    struct timespec time;
+    struct timespec convert;
+  } * list, *next;
+  struct timespec time, convert;
+
+  /* If we do not have a conversion list, build one up */
+  if (list_empty(&convertHead)) {
+    bool suspended_pending = false;
+    struct timespec suspended_monotonic = {0, 0};
+    struct timespec suspended_diff = {0, 0};
+
+    /*
+     * Read dmesg for _some_ synchronization markers and insert
+     * Anything in the Android Logger before the dmesg logging span will
+     * be highly suspect regarding the monotonic time calculations.
+     */
+    FILE* p = popen("/system/bin/dmesg", "re");
+    if (p) {
+      char* line = NULL;
+      size_t len = 0;
+      while (getline(&line, &len, p) > 0) {
+        static const char suspend[] = "PM: suspend entry ";
+        static const char resume[] = "PM: suspend exit ";
+        static const char healthd[] = "healthd";
+        static const char battery[] = ": battery ";
+        static const char suspended[] = "Suspended for ";
+        struct timespec monotonic;
+        struct tm tm;
+        char *cp, *e = line;
+        bool add_entry = true;
+
+        if (*e == '<') {
+          while (*e && (*e != '>')) {
+            ++e;
+          }
+          if (*e != '>') {
+            continue;
+          }
+        }
+        if (*e != '[') {
+          continue;
+        }
+        while (*++e == ' ') {
+          ;
+        }
+        e = readSeconds(e, &monotonic);
+        if (!e || (*e != ']')) {
+          continue;
+        }
+
+        if ((e = strstr(e, suspend))) {
+          e += sizeof(suspend) - 1;
+        } else if ((e = strstr(line, resume))) {
+          e += sizeof(resume) - 1;
+        } else if (((e = strstr(line, healthd))) &&
+                   ((e = strstr(e + sizeof(healthd) - 1, battery)))) {
+          /* NB: healthd is roughly 150us late, worth the price to
+           * deal with ntp-induced or hardware clock drift. */
+          e += sizeof(battery) - 1;
+        } else if ((e = strstr(line, suspended))) {
+          e += sizeof(suspended) - 1;
+          e = readSeconds(e, &time);
+          if (!e) {
+            continue;
+          }
+          add_entry = false;
+          suspended_pending = true;
+          suspended_monotonic = monotonic;
+          suspended_diff = time;
+        } else {
+          continue;
+        }
+        if (add_entry) {
+          /* look for "????-??-?? ??:??:??.????????? UTC" */
+          cp = strstr(e, " UTC");
+          if (!cp || ((cp - e) < 29) || (cp[-10] != '.')) {
+            continue;
+          }
+          e = cp - 29;
+          cp = readSeconds(cp - 10, &time);
+          if (!cp) {
+            continue;
+          }
+          cp = strptime(e, "%Y-%m-%d %H:%M:%S.", &tm);
+          if (!cp) {
+            continue;
+          }
+          cp = getenv(tz);
+          if (cp) {
+            cp = strdup(cp);
+          }
+          setenv(tz, utc, 1);
+          time.tv_sec = mktime(&tm);
+          if (cp) {
+            setenv(tz, cp, 1);
+            free(cp);
+          } else {
+            unsetenv(tz);
+          }
+          list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
+          list_init(&list->node);
+          list->time = time;
+          subTimespec(&list->convert, &time, &monotonic);
+          list_add_tail(&convertHead, &list->node);
+        }
+        if (suspended_pending && !list_empty(&convertHead)) {
+          list = node_to_item(list_tail(&convertHead), struct conversionList, node);
+          if (subTimespec(&time, subTimespec(&time, &list->time, &list->convert),
+                          &suspended_monotonic)
+                  ->tv_sec > 0) {
+            /* resume, what is convert factor before? */
+            subTimespec(&convert, &list->convert, &suspended_diff);
+          } else {
+            /* suspend */
+            convert = list->convert;
+          }
+          time = suspended_monotonic;
+          sumTimespec(&time, &convert);
+          /* breakpoint just before sleep */
+          list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
+          list_init(&list->node);
+          list->time = time;
+          list->convert = convert;
+          list_add_tail(&convertHead, &list->node);
+          /* breakpoint just after sleep */
+          list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
+          list_init(&list->node);
+          list->time = time;
+          sumTimespec(&list->time, &suspended_diff);
+          list->convert = convert;
+          sumTimespec(&list->convert, &suspended_diff);
+          list_add_tail(&convertHead, &list->node);
+          suspended_pending = false;
+        }
+      }
+      pclose(p);
+    }
+    /* last entry is our current time conversion */
+    list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
+    list_init(&list->node);
+    clock_gettime(CLOCK_REALTIME, &list->time);
+    clock_gettime(CLOCK_MONOTONIC, &convert);
+    clock_gettime(CLOCK_MONOTONIC, &time);
+    /* Correct for instant clock_gettime latency (syscall or ~30ns) */
+    subTimespec(&time, &convert, subTimespec(&time, &time, &convert));
+    /* Calculate conversion factor */
+    subTimespec(&list->convert, &list->time, &time);
+    list_add_tail(&convertHead, &list->node);
+    if (suspended_pending) {
+      /* manufacture a suspend @ point before */
+      subTimespec(&convert, &list->convert, &suspended_diff);
+      time = suspended_monotonic;
+      sumTimespec(&time, &convert);
+      /* breakpoint just after sleep */
+      list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
+      list_init(&list->node);
+      list->time = time;
+      sumTimespec(&list->time, &suspended_diff);
+      list->convert = convert;
+      sumTimespec(&list->convert, &suspended_diff);
+      list_add_head(&convertHead, &list->node);
+      /* breakpoint just before sleep */
+      list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
+      list_init(&list->node);
+      list->time = time;
+      list->convert = convert;
+      list_add_head(&convertHead, &list->node);
+    }
+  }
+
+  /* Find the breakpoint in the conversion list */
+  list = node_to_item(list_head(&convertHead), struct conversionList, node);
+  next = NULL;
+  list_for_each(node, &convertHead) {
+    next = node_to_item(node, struct conversionList, node);
+    if (entry->tv_sec < next->time.tv_sec) {
+      break;
+    } else if (entry->tv_sec == next->time.tv_sec) {
+      if (entry->tv_nsec < next->time.tv_nsec) {
+        break;
+      }
+    }
+    list = next;
+  }
+
+  /* blend time from one breakpoint to the next */
+  convert = list->convert;
+  if (next) {
+    unsigned long long total, run;
+
+    total = nsecTimespec(subTimespec(&time, &next->time, &list->time));
+    time.tv_sec = entry->tv_sec;
+    time.tv_nsec = entry->tv_nsec;
+    run = nsecTimespec(subTimespec(&time, &time, &list->time));
+    if (run < total) {
+      long long crun;
+
+      float f = nsecTimespec(subTimespec(&time, &next->convert, &convert));
+      f *= run;
+      f /= total;
+      crun = f;
+      convert.tv_sec += crun / (long long)NS_PER_SEC;
+      if (crun < 0) {
+        convert.tv_nsec -= (-crun) % NS_PER_SEC;
+        if (convert.tv_nsec < 0) {
+          convert.tv_nsec += NS_PER_SEC;
+          convert.tv_sec -= 1;
+        }
+      } else {
+        convert.tv_nsec += crun % NS_PER_SEC;
+        if (convert.tv_nsec >= (long)NS_PER_SEC) {
+          convert.tv_nsec -= NS_PER_SEC;
+          convert.tv_sec += 1;
+        }
+      }
+    }
+  }
+
+  /* Apply the correction factor */
+  result->tv_sec = entry->tv_sec;
+  result->tv_nsec = entry->tv_nsec;
+  subTimespec(result, result, &convert);
+}
+#endif
+
+/**
+ * Formats a log message into a buffer
+ *
+ * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
+ * If return value != defaultBuffer, caller must call free()
+ * Returns NULL on malloc error
+ */
+
+char* android_log_formatLogLine(AndroidLogFormat* p_format, char* defaultBuffer,
+                                size_t defaultBufferSize, const AndroidLogEntry* entry,
+                                size_t* p_outLength) {
+#if !defined(_WIN32)
+  struct tm tmBuf;
+#endif
+  struct tm* ptm;
+  /* good margin, 23+nul for msec, 26+nul for usec, 29+nul to nsec */
+  char timeBuf[64];
+  char prefixBuf[128], suffixBuf[128];
+  char priChar;
+  int prefixSuffixIsHeaderFooter = 0;
+  char* ret;
+  time_t now;
+  unsigned long nsec;
+
+  priChar = filterPriToChar(entry->priority);
+  size_t prefixLen = 0, suffixLen = 0;
+  size_t len;
+
+  /*
+   * Get the current date/time in pretty form
+   *
+   * It's often useful when examining a log with "less" to jump to
+   * a specific point in the file by searching for the date/time stamp.
+   * For this reason it's very annoying to have regexp meta characters
+   * in the time stamp.  Don't use forward slashes, parenthesis,
+   * brackets, asterisks, or other special chars here.
+   *
+   * The caller may have affected the timezone environment, this is
+   * expected to be sensitive to that.
+   */
+  now = entry->tv_sec;
+  nsec = entry->tv_nsec;
+#if __ANDROID__
+  if (p_format->monotonic_output) {
+    /* prevent convertMonotonic from being called if logd is monotonic */
+    if (android_log_clockid() != CLOCK_MONOTONIC) {
+      struct timespec time;
+      convertMonotonic(&time, entry);
+      now = time.tv_sec;
+      nsec = time.tv_nsec;
+    }
+  }
+#endif
+  if (now < 0) {
+    nsec = NS_PER_SEC - nsec;
+  }
+  if (p_format->epoch_output || p_format->monotonic_output) {
+    ptm = NULL;
+    snprintf(timeBuf, sizeof(timeBuf), p_format->monotonic_output ? "%6lld" : "%19lld",
+             (long long)now);
+  } else {
+#if !defined(_WIN32)
+    ptm = localtime_r(&now, &tmBuf);
+#else
+    ptm = localtime(&now);
+#endif
+    strftime(timeBuf, sizeof(timeBuf), &"%Y-%m-%d %H:%M:%S"[p_format->year_output ? 0 : 3], ptm);
+  }
+  len = strlen(timeBuf);
+  if (p_format->nsec_time_output) {
+    len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%09ld", nsec);
+  } else if (p_format->usec_time_output) {
+    len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%06ld", nsec / US_PER_NSEC);
+  } else {
+    len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%03ld", nsec / MS_PER_NSEC);
+  }
+  if (p_format->zone_output && ptm) {
+    strftime(timeBuf + len, sizeof(timeBuf) - len, " %z", ptm);
+  }
+
+  /*
+   * Construct a buffer containing the log header and log message.
+   */
+  if (p_format->colored_output) {
+    prefixLen =
+        snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[38;5;%dm", colorFromPri(entry->priority));
+    prefixLen = MIN(prefixLen, sizeof(prefixBuf));
+
+    const char suffixContents[] = "\x1B[0m";
+    strcpy(suffixBuf, suffixContents);
+    suffixLen = strlen(suffixContents);
+  }
+
+  char uid[16];
+  uid[0] = '\0';
+  if (p_format->uid_output) {
+    if (entry->uid >= 0) {
+/*
+ * This code is Android specific, bionic guarantees that
+ * calls to non-reentrant getpwuid() are thread safe.
+ */
+#if !defined(__MINGW32__)
+#if (FAKE_LOG_DEVICE == 0)
+#ifndef __BIONIC__
+#warning "This code assumes that getpwuid is thread safe, only true with Bionic!"
+#endif
+#endif
+      struct passwd* pwd = getpwuid(entry->uid);
+      if (pwd && (strlen(pwd->pw_name) <= 5)) {
+        snprintf(uid, sizeof(uid), "%5s:", pwd->pw_name);
+      } else
+#endif
+      {
+        /* Not worth parsing package list, names all longer than 5 */
+        snprintf(uid, sizeof(uid), "%5d:", entry->uid);
+      }
+    } else {
+      snprintf(uid, sizeof(uid), "      ");
+    }
+  }
+
+  switch (p_format->format) {
+    case FORMAT_TAG:
+      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c/%-8.*s: ", priChar,
+                     (int)entry->tagLen, entry->tag);
+      strcpy(suffixBuf + suffixLen, "\n");
+      ++suffixLen;
+      break;
+    case FORMAT_PROCESS:
+      len = snprintf(suffixBuf + suffixLen, sizeof(suffixBuf) - suffixLen, "  (%.*s)\n",
+                     (int)entry->tagLen, entry->tag);
+      suffixLen += MIN(len, sizeof(suffixBuf) - suffixLen);
+      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c(%s%5d) ", priChar,
+                     uid, entry->pid);
+      break;
+    case FORMAT_THREAD:
+      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c(%s%5d:%5d) ",
+                     priChar, uid, entry->pid, entry->tid);
+      strcpy(suffixBuf + suffixLen, "\n");
+      ++suffixLen;
+      break;
+    case FORMAT_RAW:
+      prefixBuf[prefixLen] = 0;
+      len = 0;
+      strcpy(suffixBuf + suffixLen, "\n");
+      ++suffixLen;
+      break;
+    case FORMAT_TIME:
+      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+                     "%s %c/%-8.*s(%s%5d): ", timeBuf, priChar, (int)entry->tagLen, entry->tag, uid,
+                     entry->pid);
+      strcpy(suffixBuf + suffixLen, "\n");
+      ++suffixLen;
+      break;
+    case FORMAT_THREADTIME:
+      ret = strchr(uid, ':');
+      if (ret) {
+        *ret = ' ';
+      }
+      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+                     "%s %s%5d %5d %c %-8.*s: ", timeBuf, uid, entry->pid, entry->tid, priChar,
+                     (int)entry->tagLen, entry->tag);
+      strcpy(suffixBuf + suffixLen, "\n");
+      ++suffixLen;
+      break;
+    case FORMAT_LONG:
+      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+                     "[ %s %s%5d:%5d %c/%-8.*s ]\n", timeBuf, uid, entry->pid, entry->tid, priChar,
+                     (int)entry->tagLen, entry->tag);
+      strcpy(suffixBuf + suffixLen, "\n\n");
+      suffixLen += 2;
+      prefixSuffixIsHeaderFooter = 1;
+      break;
+    case FORMAT_BRIEF:
+    default:
+      len =
+          snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+                   "%c/%-8.*s(%s%5d): ", priChar, (int)entry->tagLen, entry->tag, uid, entry->pid);
+      strcpy(suffixBuf + suffixLen, "\n");
+      ++suffixLen;
+      break;
+  }
+
+  /* snprintf has a weird return value.   It returns what would have been
+   * written given a large enough buffer.  In the case that the prefix is
+   * longer then our buffer(128), it messes up the calculations below
+   * possibly causing heap corruption.  To avoid this we double check and
+   * set the length at the maximum (size minus null byte)
+   */
+  prefixLen += len;
+  if (prefixLen >= sizeof(prefixBuf)) {
+    prefixLen = sizeof(prefixBuf) - 1;
+    prefixBuf[sizeof(prefixBuf) - 1] = '\0';
+  }
+  if (suffixLen >= sizeof(suffixBuf)) {
+    suffixLen = sizeof(suffixBuf) - 1;
+    suffixBuf[sizeof(suffixBuf) - 2] = '\n';
+    suffixBuf[sizeof(suffixBuf) - 1] = '\0';
+  }
+
+  /* the following code is tragically unreadable */
+
+  size_t numLines;
+  char* p;
+  size_t bufferSize;
+  const char* pm;
+
+  if (prefixSuffixIsHeaderFooter) {
+    /* we're just wrapping message with a header/footer */
+    numLines = 1;
+  } else {
+    pm = entry->message;
+    numLines = 0;
+
+    /*
+     * The line-end finding here must match the line-end finding
+     * in for ( ... numLines...) loop below
+     */
+    while (pm < (entry->message + entry->messageLen)) {
+      if (*pm++ == '\n') numLines++;
+    }
+    /* plus one line for anything not newline-terminated at the end */
+    if (pm > entry->message && *(pm - 1) != '\n') numLines++;
+  }
+
+  /*
+   * this is an upper bound--newlines in message may be counted
+   * extraneously
+   */
+  bufferSize = (numLines * (prefixLen + suffixLen)) + 1;
+  if (p_format->printable_output) {
+    /* Calculate extra length to convert non-printable to printable */
+    bufferSize += convertPrintable(NULL, entry->message, entry->messageLen);
+  } else {
+    bufferSize += entry->messageLen;
+  }
+
+  if (defaultBufferSize >= bufferSize) {
+    ret = defaultBuffer;
+  } else {
+    ret = (char*)malloc(bufferSize);
+
+    if (ret == NULL) {
+      return ret;
+    }
+  }
+
+  ret[0] = '\0'; /* to start strcat off */
+
+  p = ret;
+  pm = entry->message;
+
+  if (prefixSuffixIsHeaderFooter) {
+    strcat(p, prefixBuf);
+    p += prefixLen;
+    if (p_format->printable_output) {
+      p += convertPrintable(p, entry->message, entry->messageLen);
+    } else {
+      strncat(p, entry->message, entry->messageLen);
+      p += entry->messageLen;
+    }
+    strcat(p, suffixBuf);
+    p += suffixLen;
+  } else {
+    do {
+      const char* lineStart;
+      size_t lineLen;
+      lineStart = pm;
+
+      /* Find the next end-of-line in message */
+      while (pm < (entry->message + entry->messageLen) && *pm != '\n') pm++;
+      lineLen = pm - lineStart;
+
+      strcat(p, prefixBuf);
+      p += prefixLen;
+      if (p_format->printable_output) {
+        p += convertPrintable(p, lineStart, lineLen);
+      } else {
+        strncat(p, lineStart, lineLen);
+        p += lineLen;
+      }
+      strcat(p, suffixBuf);
+      p += suffixLen;
+
+      if (*pm == '\n') pm++;
+    } while (pm < (entry->message + entry->messageLen));
+  }
+
+  if (p_outLength != NULL) {
+    *p_outLength = p - ret;
+  }
+
+  return ret;
+}
+
+/**
+ * Either print or do not print log line, based on filter
+ *
+ * Returns count bytes written
+ */
+
+int android_log_printLogLine(AndroidLogFormat* p_format, int fd, const AndroidLogEntry* entry) {
+  int ret;
+  char defaultBuffer[512];
+  char* outBuffer = NULL;
+  size_t totalLen;
+
+  outBuffer =
+      android_log_formatLogLine(p_format, defaultBuffer, sizeof(defaultBuffer), entry, &totalLen);
+
+  if (!outBuffer) return -1;
+
+  do {
+    ret = write(fd, outBuffer, totalLen);
+  } while (ret < 0 && errno == EINTR);
+
+  if (ret < 0) {
+    fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
+    ret = 0;
+    goto done;
+  }
+
+  if (((size_t)ret) < totalLen) {
+    fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret, (int)totalLen);
+    goto done;
+  }
+
+done:
+  if (outBuffer != defaultBuffer) {
+    free(outBuffer);
+  }
+
+  return ret;
+}
diff --git a/liblog/pmsg_reader.c b/liblog/pmsg_reader.c
deleted file mode 100644
index c3ed8a2..0000000
--- a/liblog/pmsg_reader.c
+++ /dev/null
@@ -1,599 +0,0 @@
-/*
- * Copyright (C) 2007-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 <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
-#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
-
-#include "config_read.h"
-#include "logger.h"
-
-static int pmsgAvailable(log_id_t logId);
-static int pmsgVersion(struct android_log_logger* logger,
-                       struct android_log_transport_context* transp);
-static int pmsgRead(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp,
-                    struct log_msg* log_msg);
-static void pmsgClose(struct android_log_logger_list* logger_list,
-                      struct android_log_transport_context* transp);
-static int pmsgClear(struct android_log_logger* logger,
-                     struct android_log_transport_context* transp);
-
-LIBLOG_HIDDEN struct android_log_transport_read pmsgLoggerRead = {
-  .node = { &pmsgLoggerRead.node, &pmsgLoggerRead.node },
-  .name = "pmsg",
-  .available = pmsgAvailable,
-  .version = pmsgVersion,
-  .read = pmsgRead,
-  .poll = NULL,
-  .close = pmsgClose,
-  .clear = pmsgClear,
-  .setSize = NULL,
-  .getSize = NULL,
-  .getReadableSize = NULL,
-  .getPrune = NULL,
-  .setPrune = NULL,
-  .getStats = NULL,
-};
-
-static int pmsgAvailable(log_id_t logId) {
-  if (logId > LOG_ID_SECURITY) {
-    return -EINVAL;
-  }
-  if (access("/dev/pmsg0", W_OK) == 0) {
-    return 0;
-  }
-  return -EBADF;
-}
-
-/* Determine the credentials of the caller */
-static bool uid_has_log_permission(uid_t uid) {
-  return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT) ||
-         (uid == AID_LOGD);
-}
-
-static uid_t get_best_effective_uid() {
-  uid_t euid;
-  uid_t uid;
-  gid_t gid;
-  ssize_t i;
-  static uid_t last_uid = (uid_t)-1;
-
-  if (last_uid != (uid_t)-1) {
-    return last_uid;
-  }
-  uid = __android_log_uid();
-  if (uid_has_log_permission(uid)) {
-    return last_uid = uid;
-  }
-  euid = geteuid();
-  if (uid_has_log_permission(euid)) {
-    return last_uid = euid;
-  }
-  gid = getgid();
-  if (uid_has_log_permission(gid)) {
-    return last_uid = gid;
-  }
-  gid = getegid();
-  if (uid_has_log_permission(gid)) {
-    return last_uid = gid;
-  }
-  i = getgroups((size_t)0, NULL);
-  if (i > 0) {
-    gid_t list[i];
-
-    getgroups(i, list);
-    while (--i >= 0) {
-      if (uid_has_log_permission(list[i])) {
-        return last_uid = list[i];
-      }
-    }
-  }
-  return last_uid = uid;
-}
-
-static int pmsgClear(struct android_log_logger* logger __unused,
-                     struct android_log_transport_context* transp __unused) {
-  if (uid_has_log_permission(get_best_effective_uid())) {
-    return unlink("/sys/fs/pstore/pmsg-ramoops-0");
-  }
-  errno = EPERM;
-  return -1;
-}
-
-/*
- * returns the logger version
- */
-static int pmsgVersion(struct android_log_logger* logger __unused,
-                       struct android_log_transport_context* transp __unused) {
-  return 4;
-}
-
-static int pmsgRead(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp,
-                    struct log_msg* log_msg) {
-  ssize_t ret;
-  off_t current, next;
-  uid_t uid;
-  struct android_log_logger* logger;
-  struct __attribute__((__packed__)) {
-    android_pmsg_log_header_t p;
-    android_log_header_t l;
-    uint8_t prio;
-  } buf;
-  static uint8_t preread_count;
-  bool is_system;
-
-  memset(log_msg, 0, sizeof(*log_msg));
-
-  if (atomic_load(&transp->context.fd) <= 0) {
-    int i, fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY | O_CLOEXEC);
-
-    if (fd < 0) {
-      return -errno;
-    }
-    if (fd == 0) { /* Argggg */
-      fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY | O_CLOEXEC);
-      close(0);
-      if (fd < 0) {
-        return -errno;
-      }
-    }
-    i = atomic_exchange(&transp->context.fd, fd);
-    if ((i > 0) && (i != fd)) {
-      close(i);
-    }
-    preread_count = 0;
-  }
-
-  while (1) {
-    int fd;
-
-    if (preread_count < sizeof(buf)) {
-      fd = atomic_load(&transp->context.fd);
-      if (fd <= 0) {
-        return -EBADF;
-      }
-      ret = TEMP_FAILURE_RETRY(
-          read(fd, &buf.p.magic + preread_count, sizeof(buf) - preread_count));
-      if (ret < 0) {
-        return -errno;
-      }
-      preread_count += ret;
-    }
-    if (preread_count != sizeof(buf)) {
-      return preread_count ? -EIO : -EAGAIN;
-    }
-    if ((buf.p.magic != LOGGER_MAGIC) || (buf.p.len <= sizeof(buf)) ||
-        (buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD)) ||
-        (buf.l.id >= LOG_ID_MAX) || (buf.l.realtime.tv_nsec >= NS_PER_SEC) ||
-        ((buf.l.id != LOG_ID_EVENTS) && (buf.l.id != LOG_ID_SECURITY) &&
-         ((buf.prio == ANDROID_LOG_UNKNOWN) ||
-          (buf.prio == ANDROID_LOG_DEFAULT) ||
-          (buf.prio >= ANDROID_LOG_SILENT)))) {
-      do {
-        memmove(&buf.p.magic, &buf.p.magic + 1, --preread_count);
-      } while (preread_count && (buf.p.magic != LOGGER_MAGIC));
-      continue;
-    }
-    preread_count = 0;
-
-    if ((transp->logMask & (1 << buf.l.id)) &&
-        ((!logger_list->start.tv_sec && !logger_list->start.tv_nsec) ||
-         ((logger_list->start.tv_sec <= buf.l.realtime.tv_sec) &&
-          ((logger_list->start.tv_sec != buf.l.realtime.tv_sec) ||
-           (logger_list->start.tv_nsec <= buf.l.realtime.tv_nsec)))) &&
-        (!logger_list->pid || (logger_list->pid == buf.p.pid))) {
-      uid = get_best_effective_uid();
-      is_system = uid_has_log_permission(uid);
-      if (is_system || (uid == buf.p.uid)) {
-        char* msg = is_system ? log_msg->entry_v4.msg : log_msg->entry_v3.msg;
-        *msg = buf.prio;
-        fd = atomic_load(&transp->context.fd);
-        if (fd <= 0) {
-          return -EBADF;
-        }
-        ret = TEMP_FAILURE_RETRY(
-            read(fd, msg + sizeof(buf.prio), buf.p.len - sizeof(buf)));
-        if (ret < 0) {
-          return -errno;
-        }
-        if (ret != (ssize_t)(buf.p.len - sizeof(buf))) {
-          return -EIO;
-        }
-
-        log_msg->entry_v4.len = buf.p.len - sizeof(buf) + sizeof(buf.prio);
-        log_msg->entry_v4.hdr_size =
-            is_system ? sizeof(log_msg->entry_v4) : sizeof(log_msg->entry_v3);
-        log_msg->entry_v4.pid = buf.p.pid;
-        log_msg->entry_v4.tid = buf.l.tid;
-        log_msg->entry_v4.sec = buf.l.realtime.tv_sec;
-        log_msg->entry_v4.nsec = buf.l.realtime.tv_nsec;
-        log_msg->entry_v4.lid = buf.l.id;
-        if (is_system) {
-          log_msg->entry_v4.uid = buf.p.uid;
-        }
-
-        return ret + sizeof(buf.prio) + log_msg->entry_v4.hdr_size;
-      }
-    }
-
-    fd = atomic_load(&transp->context.fd);
-    if (fd <= 0) {
-      return -EBADF;
-    }
-    current = TEMP_FAILURE_RETRY(lseek(fd, (off_t)0, SEEK_CUR));
-    if (current < 0) {
-      return -errno;
-    }
-    fd = atomic_load(&transp->context.fd);
-    if (fd <= 0) {
-      return -EBADF;
-    }
-    next = TEMP_FAILURE_RETRY(
-        lseek(fd, (off_t)(buf.p.len - sizeof(buf)), SEEK_CUR));
-    if (next < 0) {
-      return -errno;
-    }
-    if ((next - current) != (ssize_t)(buf.p.len - sizeof(buf))) {
-      return -EIO;
-    }
-  }
-}
-
-static void pmsgClose(struct android_log_logger_list* logger_list __unused,
-                      struct android_log_transport_context* transp) {
-  int fd = atomic_exchange(&transp->context.fd, 0);
-  if (fd > 0) {
-    close(fd);
-  }
-}
-
-LIBLOG_ABI_PRIVATE ssize_t
-__android_log_pmsg_file_read(log_id_t logId, char prio, const char* prefix,
-                             __android_log_pmsg_file_read_fn fn, void* arg) {
-  ssize_t ret;
-  struct android_log_logger_list logger_list;
-  struct android_log_transport_context transp;
-  struct content {
-    struct listnode node;
-    union {
-      struct logger_entry_v4 entry;
-      struct logger_entry_v4 entry_v4;
-      struct logger_entry_v3 entry_v3;
-      struct logger_entry_v2 entry_v2;
-      struct logger_entry entry_v1;
-    };
-  } * content;
-  struct names {
-    struct listnode node;
-    struct listnode content;
-    log_id_t id;
-    char prio;
-    char name[];
-  } * names;
-  struct listnode name_list;
-  struct listnode *node, *n;
-  size_t len, prefix_len;
-
-  if (!fn) {
-    return -EINVAL;
-  }
-
-  /* Add just enough clues in logger_list and transp to make API function */
-  memset(&logger_list, 0, sizeof(logger_list));
-  memset(&transp, 0, sizeof(transp));
-
-  logger_list.mode =
-      ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK | ANDROID_LOG_RDONLY;
-  transp.logMask = (unsigned)-1;
-  if (logId != LOG_ID_ANY) {
-    transp.logMask = (1 << logId);
-  }
-  transp.logMask &=
-      ~((1 << LOG_ID_KERNEL) | (1 << LOG_ID_EVENTS) | (1 << LOG_ID_SECURITY));
-  if (!transp.logMask) {
-    return -EINVAL;
-  }
-
-  /* Initialize name list */
-  list_init(&name_list);
-
-  ret = SSIZE_MAX;
-
-  /* Validate incoming prefix, shift until it contains only 0 or 1 : or / */
-  prefix_len = 0;
-  if (prefix) {
-    const char *prev = NULL, *last = NULL, *cp = prefix;
-    while ((cp = strpbrk(cp, "/:"))) {
-      prev = last;
-      last = cp;
-      cp = cp + 1;
-    }
-    if (prev) {
-      prefix = prev + 1;
-    }
-    prefix_len = strlen(prefix);
-  }
-
-  /* Read the file content */
-  while (pmsgRead(&logger_list, &transp, &transp.logMsg) > 0) {
-    char* cp;
-    size_t hdr_size = transp.logMsg.entry.hdr_size
-                          ? transp.logMsg.entry.hdr_size
-                          : sizeof(transp.logMsg.entry_v1);
-    char* msg = (char*)&transp.logMsg + hdr_size;
-    char* split = NULL;
-
-    if ((hdr_size < sizeof(transp.logMsg.entry_v1)) ||
-        (hdr_size > sizeof(transp.logMsg.entry))) {
-      continue;
-    }
-    /* Check for invalid sequence number */
-    if ((transp.logMsg.entry.nsec % ANDROID_LOG_PMSG_FILE_SEQUENCE) ||
-        ((transp.logMsg.entry.nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >=
-         ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE)) {
-      continue;
-    }
-
-    /* Determine if it has <dirbase>:<filebase> format for tag */
-    len = transp.logMsg.entry.len - sizeof(prio);
-    for (cp = msg + sizeof(prio); *cp && isprint(*cp) && !isspace(*cp) && --len;
-         ++cp) {
-      if (*cp == ':') {
-        if (split) {
-          break;
-        }
-        split = cp;
-      }
-    }
-    if (*cp || !split) {
-      continue;
-    }
-
-    /* Filters */
-    if (prefix_len && strncmp(msg + sizeof(prio), prefix, prefix_len)) {
-      size_t offset;
-      /*
-       *   Allow : to be a synonym for /
-       * Things we do dealing with const char * and do not alloc
-       */
-      split = strchr(prefix, ':');
-      if (split) {
-        continue;
-      }
-      split = strchr(prefix, '/');
-      if (!split) {
-        continue;
-      }
-      offset = split - prefix;
-      if ((msg[offset + sizeof(prio)] != ':') ||
-          strncmp(msg + sizeof(prio), prefix, offset)) {
-        continue;
-      }
-      ++offset;
-      if ((prefix_len > offset) && strncmp(&msg[offset + sizeof(prio)],
-                                           split + 1, prefix_len - offset)) {
-        continue;
-      }
-    }
-
-    if ((prio != ANDROID_LOG_ANY) && (*msg < prio)) {
-      continue;
-    }
-
-    /* check if there is an existing entry */
-    list_for_each(node, &name_list) {
-      names = node_to_item(node, struct names, node);
-      if (!strcmp(names->name, msg + sizeof(prio)) &&
-          (names->id == transp.logMsg.entry.lid) && (names->prio == *msg)) {
-        break;
-      }
-    }
-
-    /* We do not have an existing entry, create and add one */
-    if (node == &name_list) {
-      static const char numbers[] = "0123456789";
-      unsigned long long nl;
-
-      len = strlen(msg + sizeof(prio)) + 1;
-      names = calloc(1, sizeof(*names) + len);
-      if (!names) {
-        ret = -ENOMEM;
-        break;
-      }
-      strcpy(names->name, msg + sizeof(prio));
-      names->id = transp.logMsg.entry.lid;
-      names->prio = *msg;
-      list_init(&names->content);
-      /*
-       * Insert in reverse numeric _then_ alpha sorted order as
-       * representative of log rotation:
-       *
-       *   log.10
-       *   klog.10
-       *   . . .
-       *   log.2
-       *   klog.2
-       *   log.1
-       *   klog.1
-       *   log
-       *   klog
-       *
-       * thus when we present the content, we are provided the oldest
-       * first, which when 'refreshed' could spill off the end of the
-       * pmsg FIFO but retaining the newest data for last with best
-       * chances to survive.
-       */
-      nl = 0;
-      cp = strpbrk(names->name, numbers);
-      if (cp) {
-        nl = strtoull(cp, NULL, 10);
-      }
-      list_for_each_reverse(node, &name_list) {
-        struct names* a_name = node_to_item(node, struct names, node);
-        const char* r = a_name->name;
-        int compare = 0;
-
-        unsigned long long nr = 0;
-        cp = strpbrk(r, numbers);
-        if (cp) {
-          nr = strtoull(cp, NULL, 10);
-        }
-        if (nr != nl) {
-          compare = (nl > nr) ? 1 : -1;
-        }
-        if (compare == 0) {
-          compare = strcmp(names->name, r);
-        }
-        if (compare <= 0) {
-          break;
-        }
-      }
-      list_add_head(node, &names->node);
-    }
-
-    /* Remove any file fragments that match our sequence number */
-    list_for_each_safe(node, n, &names->content) {
-      content = node_to_item(node, struct content, node);
-      if (transp.logMsg.entry.nsec == content->entry.nsec) {
-        list_remove(&content->node);
-        free(content);
-      }
-    }
-
-    /* Add content */
-    content =
-        calloc(1, sizeof(content->node) + hdr_size + transp.logMsg.entry.len);
-    if (!content) {
-      ret = -ENOMEM;
-      break;
-    }
-    memcpy(&content->entry, &transp.logMsg.entry,
-           hdr_size + transp.logMsg.entry.len);
-
-    /* Insert in sequence number sorted order, to ease reconstruction */
-    list_for_each_reverse(node, &names->content) {
-      if ((node_to_item(node, struct content, node))->entry.nsec <
-          transp.logMsg.entry.nsec) {
-        break;
-      }
-    }
-    list_add_head(node, &content->node);
-  }
-  pmsgClose(&logger_list, &transp);
-
-  /* Progress through all the collected files */
-  list_for_each_safe(node, n, &name_list) {
-    struct listnode *content_node, *m;
-    char* buf;
-    size_t sequence, tag_len;
-
-    names = node_to_item(node, struct names, node);
-
-    /* Construct content into a linear buffer */
-    buf = NULL;
-    len = 0;
-    sequence = 0;
-    tag_len = strlen(names->name) + sizeof(char); /* tag + nul */
-    list_for_each_safe(content_node, m, &names->content) {
-      ssize_t add_len;
-
-      content = node_to_item(content_node, struct content, node);
-      add_len = content->entry.len - tag_len - sizeof(prio);
-      if (add_len <= 0) {
-        list_remove(content_node);
-        free(content);
-        continue;
-      }
-
-      if (!buf) {
-        buf = malloc(sizeof(char));
-        if (!buf) {
-          ret = -ENOMEM;
-          list_remove(content_node);
-          free(content);
-          continue;
-        }
-        *buf = '\0';
-      }
-
-      /* Missing sequence numbers */
-      while (sequence < content->entry.nsec) {
-        /* plus space for enforced nul */
-        buf = realloc(buf, len + sizeof(char) + sizeof(char));
-        if (!buf) {
-          break;
-        }
-        buf[len] = '\f'; /* Mark missing content with a form feed */
-        buf[++len] = '\0';
-        sequence += ANDROID_LOG_PMSG_FILE_SEQUENCE;
-      }
-      if (!buf) {
-        ret = -ENOMEM;
-        list_remove(content_node);
-        free(content);
-        continue;
-      }
-      /* plus space for enforced nul */
-      buf = realloc(buf, len + add_len + sizeof(char));
-      if (!buf) {
-        ret = -ENOMEM;
-        list_remove(content_node);
-        free(content);
-        continue;
-      }
-      memcpy(buf + len,
-             (char*)&content->entry + content->entry.hdr_size + tag_len +
-                 sizeof(prio),
-             add_len);
-      len += add_len;
-      buf[len] = '\0'; /* enforce trailing hidden nul */
-      sequence = content->entry.nsec + ANDROID_LOG_PMSG_FILE_SEQUENCE;
-
-      list_remove(content_node);
-      free(content);
-    }
-    if (buf) {
-      if (len) {
-        /* Buffer contains enforced trailing nul just beyond length */
-        ssize_t r;
-        *strchr(names->name, ':') = '/'; /* Convert back to filename */
-        r = (*fn)(names->id, names->prio, names->name, buf, len, arg);
-        if ((ret >= 0) && (r > 0)) {
-          if (ret == SSIZE_MAX) {
-            ret = r;
-          } else {
-            ret += r;
-          }
-        } else if (r < ret) {
-          ret = r;
-        }
-      }
-      free(buf);
-    }
-    list_remove(node);
-    free(names);
-  }
-  return (ret == SSIZE_MAX) ? -ENOENT : ret;
-}
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
new file mode 100644
index 0000000..ba27fd7
--- /dev/null
+++ b/liblog/pmsg_reader.cpp
@@ -0,0 +1,589 @@
+/*
+ * Copyright (C) 2007-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 <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "config_read.h"
+#include "logger.h"
+
+static int pmsgAvailable(log_id_t logId);
+static int pmsgVersion(struct android_log_logger* logger,
+                       struct android_log_transport_context* transp);
+static int pmsgRead(struct android_log_logger_list* logger_list,
+                    struct android_log_transport_context* transp, struct log_msg* log_msg);
+static void pmsgClose(struct android_log_logger_list* logger_list,
+                      struct android_log_transport_context* transp);
+static int pmsgClear(struct android_log_logger* logger,
+                     struct android_log_transport_context* transp);
+
+struct android_log_transport_read pmsgLoggerRead = {
+    .node = {&pmsgLoggerRead.node, &pmsgLoggerRead.node},
+    .name = "pmsg",
+    .available = pmsgAvailable,
+    .version = pmsgVersion,
+    .read = pmsgRead,
+    .poll = NULL,
+    .close = pmsgClose,
+    .clear = pmsgClear,
+    .setSize = NULL,
+    .getSize = NULL,
+    .getReadableSize = NULL,
+    .getPrune = NULL,
+    .setPrune = NULL,
+    .getStats = NULL,
+};
+
+static int pmsgAvailable(log_id_t logId) {
+  if (logId > LOG_ID_SECURITY) {
+    return -EINVAL;
+  }
+  if (access("/dev/pmsg0", W_OK) == 0) {
+    return 0;
+  }
+  return -EBADF;
+}
+
+/* Determine the credentials of the caller */
+static bool uid_has_log_permission(uid_t uid) {
+  return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT) || (uid == AID_LOGD);
+}
+
+static uid_t get_best_effective_uid() {
+  uid_t euid;
+  uid_t uid;
+  gid_t gid;
+  ssize_t i;
+  static uid_t last_uid = (uid_t)-1;
+
+  if (last_uid != (uid_t)-1) {
+    return last_uid;
+  }
+  uid = __android_log_uid();
+  if (uid_has_log_permission(uid)) {
+    return last_uid = uid;
+  }
+  euid = geteuid();
+  if (uid_has_log_permission(euid)) {
+    return last_uid = euid;
+  }
+  gid = getgid();
+  if (uid_has_log_permission(gid)) {
+    return last_uid = gid;
+  }
+  gid = getegid();
+  if (uid_has_log_permission(gid)) {
+    return last_uid = gid;
+  }
+  i = getgroups((size_t)0, NULL);
+  if (i > 0) {
+    gid_t list[i];
+
+    getgroups(i, list);
+    while (--i >= 0) {
+      if (uid_has_log_permission(list[i])) {
+        return last_uid = list[i];
+      }
+    }
+  }
+  return last_uid = uid;
+}
+
+static int pmsgClear(struct android_log_logger* logger __unused,
+                     struct android_log_transport_context* transp __unused) {
+  if (uid_has_log_permission(get_best_effective_uid())) {
+    return unlink("/sys/fs/pstore/pmsg-ramoops-0");
+  }
+  errno = EPERM;
+  return -1;
+}
+
+/*
+ * returns the logger version
+ */
+static int pmsgVersion(struct android_log_logger* logger __unused,
+                       struct android_log_transport_context* transp __unused) {
+  return 4;
+}
+
+static int pmsgRead(struct android_log_logger_list* logger_list,
+                    struct android_log_transport_context* transp, struct log_msg* log_msg) {
+  ssize_t ret;
+  off_t current, next;
+  uid_t uid;
+  struct android_log_logger* logger;
+  struct __attribute__((__packed__)) {
+    android_pmsg_log_header_t p;
+    android_log_header_t l;
+    uint8_t prio;
+  } buf;
+  static uint8_t preread_count;
+  bool is_system;
+
+  memset(log_msg, 0, sizeof(*log_msg));
+
+  if (atomic_load(&transp->context.fd) <= 0) {
+    int i, fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY | O_CLOEXEC);
+
+    if (fd < 0) {
+      return -errno;
+    }
+    if (fd == 0) { /* Argggg */
+      fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY | O_CLOEXEC);
+      close(0);
+      if (fd < 0) {
+        return -errno;
+      }
+    }
+    i = atomic_exchange(&transp->context.fd, fd);
+    if ((i > 0) && (i != fd)) {
+      close(i);
+    }
+    preread_count = 0;
+  }
+
+  while (1) {
+    int fd;
+
+    if (preread_count < sizeof(buf)) {
+      fd = atomic_load(&transp->context.fd);
+      if (fd <= 0) {
+        return -EBADF;
+      }
+      ret = TEMP_FAILURE_RETRY(read(fd, &buf.p.magic + preread_count, sizeof(buf) - preread_count));
+      if (ret < 0) {
+        return -errno;
+      }
+      preread_count += ret;
+    }
+    if (preread_count != sizeof(buf)) {
+      return preread_count ? -EIO : -EAGAIN;
+    }
+    if ((buf.p.magic != LOGGER_MAGIC) || (buf.p.len <= sizeof(buf)) ||
+        (buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD)) || (buf.l.id >= LOG_ID_MAX) ||
+        (buf.l.realtime.tv_nsec >= NS_PER_SEC) ||
+        ((buf.l.id != LOG_ID_EVENTS) && (buf.l.id != LOG_ID_SECURITY) &&
+         ((buf.prio == ANDROID_LOG_UNKNOWN) || (buf.prio == ANDROID_LOG_DEFAULT) ||
+          (buf.prio >= ANDROID_LOG_SILENT)))) {
+      do {
+        memmove(&buf.p.magic, &buf.p.magic + 1, --preread_count);
+      } while (preread_count && (buf.p.magic != LOGGER_MAGIC));
+      continue;
+    }
+    preread_count = 0;
+
+    if ((transp->logMask & (1 << buf.l.id)) &&
+        ((!logger_list->start.tv_sec && !logger_list->start.tv_nsec) ||
+         ((logger_list->start.tv_sec <= buf.l.realtime.tv_sec) &&
+          ((logger_list->start.tv_sec != buf.l.realtime.tv_sec) ||
+           (logger_list->start.tv_nsec <= buf.l.realtime.tv_nsec)))) &&
+        (!logger_list->pid || (logger_list->pid == buf.p.pid))) {
+      uid = get_best_effective_uid();
+      is_system = uid_has_log_permission(uid);
+      if (is_system || (uid == buf.p.uid)) {
+        char* msg = is_system ? log_msg->entry_v4.msg : log_msg->entry_v3.msg;
+        *msg = buf.prio;
+        fd = atomic_load(&transp->context.fd);
+        if (fd <= 0) {
+          return -EBADF;
+        }
+        ret = TEMP_FAILURE_RETRY(read(fd, msg + sizeof(buf.prio), buf.p.len - sizeof(buf)));
+        if (ret < 0) {
+          return -errno;
+        }
+        if (ret != (ssize_t)(buf.p.len - sizeof(buf))) {
+          return -EIO;
+        }
+
+        log_msg->entry_v4.len = buf.p.len - sizeof(buf) + sizeof(buf.prio);
+        log_msg->entry_v4.hdr_size =
+            is_system ? sizeof(log_msg->entry_v4) : sizeof(log_msg->entry_v3);
+        log_msg->entry_v4.pid = buf.p.pid;
+        log_msg->entry_v4.tid = buf.l.tid;
+        log_msg->entry_v4.sec = buf.l.realtime.tv_sec;
+        log_msg->entry_v4.nsec = buf.l.realtime.tv_nsec;
+        log_msg->entry_v4.lid = buf.l.id;
+        if (is_system) {
+          log_msg->entry_v4.uid = buf.p.uid;
+        }
+
+        return ret + sizeof(buf.prio) + log_msg->entry_v4.hdr_size;
+      }
+    }
+
+    fd = atomic_load(&transp->context.fd);
+    if (fd <= 0) {
+      return -EBADF;
+    }
+    current = TEMP_FAILURE_RETRY(lseek(fd, (off_t)0, SEEK_CUR));
+    if (current < 0) {
+      return -errno;
+    }
+    fd = atomic_load(&transp->context.fd);
+    if (fd <= 0) {
+      return -EBADF;
+    }
+    next = TEMP_FAILURE_RETRY(lseek(fd, (off_t)(buf.p.len - sizeof(buf)), SEEK_CUR));
+    if (next < 0) {
+      return -errno;
+    }
+    if ((next - current) != (ssize_t)(buf.p.len - sizeof(buf))) {
+      return -EIO;
+    }
+  }
+}
+
+static void pmsgClose(struct android_log_logger_list* logger_list __unused,
+                      struct android_log_transport_context* transp) {
+  int fd = atomic_exchange(&transp->context.fd, 0);
+  if (fd > 0) {
+    close(fd);
+  }
+}
+
+static void* realloc_or_free(void* ptr, size_t new_size) {
+  void* result = realloc(ptr, new_size);
+  if (!result) {
+    free(ptr);
+  }
+  return result;
+}
+
+ssize_t __android_log_pmsg_file_read(log_id_t logId, char prio, const char* prefix,
+                                     __android_log_pmsg_file_read_fn fn, void* arg) {
+  ssize_t ret;
+  struct android_log_logger_list logger_list;
+  struct android_log_transport_context transp;
+  struct content {
+    struct listnode node;
+    union {
+      struct logger_entry_v4 entry;
+      struct logger_entry_v4 entry_v4;
+      struct logger_entry_v3 entry_v3;
+      struct logger_entry_v2 entry_v2;
+      struct logger_entry entry_v1;
+    };
+  } * content;
+  struct names {
+    struct listnode node;
+    struct listnode content;
+    log_id_t id;
+    char prio;
+    char name[];
+  } * names;
+  struct listnode name_list;
+  struct listnode *node, *n;
+  size_t len, prefix_len;
+
+  if (!fn) {
+    return -EINVAL;
+  }
+
+  /* Add just enough clues in logger_list and transp to make API function */
+  memset(&logger_list, 0, sizeof(logger_list));
+  memset(&transp, 0, sizeof(transp));
+
+  logger_list.mode = ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK | ANDROID_LOG_RDONLY;
+  transp.logMask = (unsigned)-1;
+  if (logId != LOG_ID_ANY) {
+    transp.logMask = (1 << logId);
+  }
+  transp.logMask &= ~((1 << LOG_ID_KERNEL) | (1 << LOG_ID_EVENTS) | (1 << LOG_ID_SECURITY));
+  if (!transp.logMask) {
+    return -EINVAL;
+  }
+
+  /* Initialize name list */
+  list_init(&name_list);
+
+  ret = SSIZE_MAX;
+
+  /* Validate incoming prefix, shift until it contains only 0 or 1 : or / */
+  prefix_len = 0;
+  if (prefix) {
+    const char *prev = NULL, *last = NULL, *cp = prefix;
+    while ((cp = strpbrk(cp, "/:"))) {
+      prev = last;
+      last = cp;
+      cp = cp + 1;
+    }
+    if (prev) {
+      prefix = prev + 1;
+    }
+    prefix_len = strlen(prefix);
+  }
+
+  /* Read the file content */
+  while (pmsgRead(&logger_list, &transp, &transp.logMsg) > 0) {
+    const char* cp;
+    size_t hdr_size = transp.logMsg.entry.hdr_size ? transp.logMsg.entry.hdr_size
+                                                   : sizeof(transp.logMsg.entry_v1);
+    char* msg = (char*)&transp.logMsg + hdr_size;
+    const char* split = NULL;
+
+    if ((hdr_size < sizeof(transp.logMsg.entry_v1)) || (hdr_size > sizeof(transp.logMsg.entry))) {
+      continue;
+    }
+    /* Check for invalid sequence number */
+    if ((transp.logMsg.entry.nsec % ANDROID_LOG_PMSG_FILE_SEQUENCE) ||
+        ((transp.logMsg.entry.nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >=
+         ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE)) {
+      continue;
+    }
+
+    /* Determine if it has <dirbase>:<filebase> format for tag */
+    len = transp.logMsg.entry.len - sizeof(prio);
+    for (cp = msg + sizeof(prio); *cp && isprint(*cp) && !isspace(*cp) && --len; ++cp) {
+      if (*cp == ':') {
+        if (split) {
+          break;
+        }
+        split = cp;
+      }
+    }
+    if (*cp || !split) {
+      continue;
+    }
+
+    /* Filters */
+    if (prefix_len && strncmp(msg + sizeof(prio), prefix, prefix_len)) {
+      size_t offset;
+      /*
+       *   Allow : to be a synonym for /
+       * Things we do dealing with const char * and do not alloc
+       */
+      split = strchr(prefix, ':');
+      if (split) {
+        continue;
+      }
+      split = strchr(prefix, '/');
+      if (!split) {
+        continue;
+      }
+      offset = split - prefix;
+      if ((msg[offset + sizeof(prio)] != ':') || strncmp(msg + sizeof(prio), prefix, offset)) {
+        continue;
+      }
+      ++offset;
+      if ((prefix_len > offset) &&
+          strncmp(&msg[offset + sizeof(prio)], split + 1, prefix_len - offset)) {
+        continue;
+      }
+    }
+
+    if ((prio != ANDROID_LOG_ANY) && (*msg < prio)) {
+      continue;
+    }
+
+    /* check if there is an existing entry */
+    list_for_each(node, &name_list) {
+      names = node_to_item(node, struct names, node);
+      if (!strcmp(names->name, msg + sizeof(prio)) && (names->id == transp.logMsg.entry.lid) &&
+          (names->prio == *msg)) {
+        break;
+      }
+    }
+
+    /* We do not have an existing entry, create and add one */
+    if (node == &name_list) {
+      static const char numbers[] = "0123456789";
+      unsigned long long nl;
+
+      len = strlen(msg + sizeof(prio)) + 1;
+      names = static_cast<struct names*>(calloc(1, sizeof(*names) + len));
+      if (!names) {
+        ret = -ENOMEM;
+        break;
+      }
+      strcpy(names->name, msg + sizeof(prio));
+      names->id = static_cast<log_id_t>(transp.logMsg.entry.lid);
+      names->prio = *msg;
+      list_init(&names->content);
+      /*
+       * Insert in reverse numeric _then_ alpha sorted order as
+       * representative of log rotation:
+       *
+       *   log.10
+       *   klog.10
+       *   . . .
+       *   log.2
+       *   klog.2
+       *   log.1
+       *   klog.1
+       *   log
+       *   klog
+       *
+       * thus when we present the content, we are provided the oldest
+       * first, which when 'refreshed' could spill off the end of the
+       * pmsg FIFO but retaining the newest data for last with best
+       * chances to survive.
+       */
+      nl = 0;
+      cp = strpbrk(names->name, numbers);
+      if (cp) {
+        nl = strtoull(cp, NULL, 10);
+      }
+      list_for_each_reverse(node, &name_list) {
+        struct names* a_name = node_to_item(node, struct names, node);
+        const char* r = a_name->name;
+        int compare = 0;
+
+        unsigned long long nr = 0;
+        cp = strpbrk(r, numbers);
+        if (cp) {
+          nr = strtoull(cp, NULL, 10);
+        }
+        if (nr != nl) {
+          compare = (nl > nr) ? 1 : -1;
+        }
+        if (compare == 0) {
+          compare = strcmp(names->name, r);
+        }
+        if (compare <= 0) {
+          break;
+        }
+      }
+      list_add_head(node, &names->node);
+    }
+
+    /* Remove any file fragments that match our sequence number */
+    list_for_each_safe(node, n, &names->content) {
+      content = node_to_item(node, struct content, node);
+      if (transp.logMsg.entry.nsec == content->entry.nsec) {
+        list_remove(&content->node);
+        free(content);
+      }
+    }
+
+    /* Add content */
+    content = static_cast<struct content*>(
+        calloc(1, sizeof(content->node) + hdr_size + transp.logMsg.entry.len));
+    if (!content) {
+      ret = -ENOMEM;
+      break;
+    }
+    memcpy(&content->entry, &transp.logMsg.entry, hdr_size + transp.logMsg.entry.len);
+
+    /* Insert in sequence number sorted order, to ease reconstruction */
+    list_for_each_reverse(node, &names->content) {
+      if ((node_to_item(node, struct content, node))->entry.nsec < transp.logMsg.entry.nsec) {
+        break;
+      }
+    }
+    list_add_head(node, &content->node);
+  }
+  pmsgClose(&logger_list, &transp);
+
+  /* Progress through all the collected files */
+  list_for_each_safe(node, n, &name_list) {
+    struct listnode *content_node, *m;
+    char* buf;
+    size_t sequence, tag_len;
+
+    names = node_to_item(node, struct names, node);
+
+    /* Construct content into a linear buffer */
+    buf = NULL;
+    len = 0;
+    sequence = 0;
+    tag_len = strlen(names->name) + sizeof(char); /* tag + nul */
+    list_for_each_safe(content_node, m, &names->content) {
+      ssize_t add_len;
+
+      content = node_to_item(content_node, struct content, node);
+      add_len = content->entry.len - tag_len - sizeof(prio);
+      if (add_len <= 0) {
+        list_remove(content_node);
+        free(content);
+        continue;
+      }
+
+      if (!buf) {
+        buf = static_cast<char*>(malloc(sizeof(char)));
+        if (!buf) {
+          ret = -ENOMEM;
+          list_remove(content_node);
+          free(content);
+          continue;
+        }
+        *buf = '\0';
+      }
+
+      /* Missing sequence numbers */
+      while (sequence < content->entry.nsec) {
+        /* plus space for enforced nul */
+        buf = static_cast<char*>(realloc_or_free(buf, len + sizeof(char) + sizeof(char)));
+        if (!buf) {
+          break;
+        }
+        buf[len] = '\f'; /* Mark missing content with a form feed */
+        buf[++len] = '\0';
+        sequence += ANDROID_LOG_PMSG_FILE_SEQUENCE;
+      }
+      if (!buf) {
+        ret = -ENOMEM;
+        list_remove(content_node);
+        free(content);
+        continue;
+      }
+      /* plus space for enforced nul */
+      buf = static_cast<char*>(realloc_or_free(buf, len + add_len + sizeof(char)));
+      if (!buf) {
+        ret = -ENOMEM;
+        list_remove(content_node);
+        free(content);
+        continue;
+      }
+      memcpy(buf + len, (char*)&content->entry + content->entry.hdr_size + tag_len + sizeof(prio),
+             add_len);
+      len += add_len;
+      buf[len] = '\0'; /* enforce trailing hidden nul */
+      sequence = content->entry.nsec + ANDROID_LOG_PMSG_FILE_SEQUENCE;
+
+      list_remove(content_node);
+      free(content);
+    }
+    if (buf) {
+      if (len) {
+        /* Buffer contains enforced trailing nul just beyond length */
+        ssize_t r;
+        *strchr(names->name, ':') = '/'; /* Convert back to filename */
+        r = (*fn)(names->id, names->prio, names->name, buf, len, arg);
+        if ((ret >= 0) && (r > 0)) {
+          if (ret == SSIZE_MAX) {
+            ret = r;
+          } else {
+            ret += r;
+          }
+        } else if (r < ret) {
+          ret = r;
+        }
+      }
+      free(buf);
+    }
+    list_remove(node);
+    free(names);
+  }
+  return (ret == SSIZE_MAX) ? -ENOENT : ret;
+}
diff --git a/liblog/pmsg_writer.c b/liblog/pmsg_writer.c
deleted file mode 100644
index dc42856..0000000
--- a/liblog/pmsg_writer.c
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * Copyright (C) 2007-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.
- */
-
-/*
- * pmsg write handler
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <time.h>
-
-#include <log/log_properties.h>
-#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
-
-#include "config_write.h"
-#include "log_portability.h"
-#include "logger.h"
-
-static int pmsgOpen();
-static void pmsgClose();
-static int pmsgAvailable(log_id_t logId);
-static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec,
-                     size_t nr);
-
-LIBLOG_HIDDEN struct android_log_transport_write pmsgLoggerWrite = {
-  .node = { &pmsgLoggerWrite.node, &pmsgLoggerWrite.node },
-  .context.fd = -1,
-  .name = "pmsg",
-  .available = pmsgAvailable,
-  .open = pmsgOpen,
-  .close = pmsgClose,
-  .write = pmsgWrite,
-};
-
-static int pmsgOpen() {
-  int fd = atomic_load(&pmsgLoggerWrite.context.fd);
-  if (fd < 0) {
-    int i;
-
-    fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
-    i = atomic_exchange(&pmsgLoggerWrite.context.fd, fd);
-    if ((i >= 0) && (i != fd)) {
-      close(i);
-    }
-  }
-
-  return fd;
-}
-
-static void pmsgClose() {
-  int fd = atomic_exchange(&pmsgLoggerWrite.context.fd, -1);
-  if (fd >= 0) {
-    close(fd);
-  }
-}
-
-static int pmsgAvailable(log_id_t logId) {
-  if (logId > LOG_ID_SECURITY) {
-    return -EINVAL;
-  }
-  if ((logId != LOG_ID_SECURITY) && (logId != LOG_ID_EVENTS) &&
-      !__android_log_is_debuggable()) {
-    return -EINVAL;
-  }
-  if (atomic_load(&pmsgLoggerWrite.context.fd) < 0) {
-    if (access("/dev/pmsg0", W_OK) == 0) {
-      return 0;
-    }
-    return -EBADF;
-  }
-  return 1;
-}
-
-/*
- * Extract a 4-byte value from a byte stream.
- */
-static inline uint32_t get4LE(const uint8_t* src) {
-  return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
-}
-
-static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec,
-                     size_t nr) {
-  static const unsigned headerLength = 2;
-  struct iovec newVec[nr + headerLength];
-  android_log_header_t header;
-  android_pmsg_log_header_t pmsgHeader;
-  size_t i, payloadSize;
-  ssize_t ret;
-
-  if ((logId == LOG_ID_EVENTS) && !__android_log_is_debuggable()) {
-    if (vec[0].iov_len < 4) {
-      return -EINVAL;
-    }
-
-    if (SNET_EVENT_LOG_TAG != get4LE(vec[0].iov_base)) {
-      return -EPERM;
-    }
-  }
-
-  if (atomic_load(&pmsgLoggerWrite.context.fd) < 0) {
-    return -EBADF;
-  }
-
-  /*
-   *  struct {
-   *      // what we provide to pstore
-   *      android_pmsg_log_header_t pmsgHeader;
-   *      // what we provide to file
-   *      android_log_header_t header;
-   *      // caller provides
-   *      union {
-   *          struct {
-   *              char     prio;
-   *              char     payload[];
-   *          } string;
-   *          struct {
-   *              uint32_t tag
-   *              char     payload[];
-   *          } binary;
-   *      };
-   *  };
-   */
-
-  pmsgHeader.magic = LOGGER_MAGIC;
-  pmsgHeader.len = sizeof(pmsgHeader) + sizeof(header);
-  pmsgHeader.uid = __android_log_uid();
-  pmsgHeader.pid = getpid();
-
-  header.id = logId;
-  header.tid = gettid();
-  header.realtime.tv_sec = ts->tv_sec;
-  header.realtime.tv_nsec = ts->tv_nsec;
-
-  newVec[0].iov_base = (unsigned char*)&pmsgHeader;
-  newVec[0].iov_len = sizeof(pmsgHeader);
-  newVec[1].iov_base = (unsigned char*)&header;
-  newVec[1].iov_len = sizeof(header);
-
-  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;
-      }
-      payloadSize = LOGGER_ENTRY_MAX_PAYLOAD;
-      break;
-    }
-  }
-  pmsgHeader.len += payloadSize;
-
-  ret = TEMP_FAILURE_RETRY(
-      writev(atomic_load(&pmsgLoggerWrite.context.fd), newVec, i));
-  if (ret < 0) {
-    ret = errno ? -errno : -ENOTCONN;
-  }
-
-  if (ret > (ssize_t)(sizeof(header) + sizeof(pmsgHeader))) {
-    ret -= sizeof(header) - sizeof(pmsgHeader);
-  }
-
-  return ret;
-}
-
-/*
- * Virtual pmsg filesystem
- *
- * Payload will comprise the string "<basedir>:<basefile>\0<content>" to a
- * maximum of LOGGER_ENTRY_MAX_PAYLOAD, but scaled to the last newline in the
- * file.
- *
- * Will hijack the header.realtime.tv_nsec field for a sequence number in usec.
- */
-
-static inline const char* strnrchr(const char* buf, size_t len, char c) {
-  const char* cp = buf + len;
-  while ((--cp > buf) && (*cp != c))
-    ;
-  if (cp <= buf) {
-    return buf + len;
-  }
-  return cp;
-}
-
-/* Write a buffer as filename references (tag = <basedir>:<basename>) */
-LIBLOG_ABI_PRIVATE ssize_t __android_log_pmsg_file_write(log_id_t logId,
-                                                         char prio,
-                                                         const char* filename,
-                                                         const char* buf,
-                                                         size_t len) {
-  bool weOpened;
-  size_t length, packet_len;
-  const char* tag;
-  char *cp, *slash;
-  struct timespec ts;
-  struct iovec vec[3];
-
-  /* Make sure the logId value is not a bad idea */
-  if ((logId == LOG_ID_KERNEL) ||   /* Verbotten */
-      (logId == LOG_ID_EVENTS) ||   /* Do not support binary content */
-      (logId == LOG_ID_SECURITY) || /* Bad idea to allow */
-      ((unsigned)logId >= 32)) {    /* fit within logMask on arch32 */
-    return -EINVAL;
-  }
-
-  clock_gettime(android_log_clockid(), &ts);
-
-  cp = strdup(filename);
-  if (!cp) {
-    return -ENOMEM;
-  }
-
-  tag = cp;
-  slash = strrchr(cp, '/');
-  if (slash) {
-    *slash = ':';
-    slash = strrchr(cp, '/');
-    if (slash) {
-      tag = slash + 1;
-    }
-  }
-
-  length = strlen(tag) + 1;
-  packet_len = LOGGER_ENTRY_MAX_PAYLOAD - sizeof(char) - length;
-
-  vec[0].iov_base = &prio;
-  vec[0].iov_len = sizeof(char);
-  vec[1].iov_base = (unsigned char*)tag;
-  vec[1].iov_len = length;
-
-  weOpened = false;
-  for (ts.tv_nsec = 0, length = len; length;
-       ts.tv_nsec += ANDROID_LOG_PMSG_FILE_SEQUENCE) {
-    ssize_t ret;
-    size_t transfer;
-
-    if ((ts.tv_nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >=
-        ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE) {
-      len -= length;
-      break;
-    }
-
-    transfer = length;
-    if (transfer > packet_len) {
-      transfer = strnrchr(buf, packet_len - 1, '\n') - buf;
-      if ((transfer < length) && (buf[transfer] == '\n')) {
-        ++transfer;
-      }
-    }
-
-    vec[2].iov_base = (unsigned char*)buf;
-    vec[2].iov_len = transfer;
-
-    if (atomic_load(&pmsgLoggerWrite.context.fd) < 0) {
-      if (!weOpened) { /* Impossible for weOpened = true here */
-        __android_log_lock();
-      }
-      weOpened = atomic_load(&pmsgLoggerWrite.context.fd) < 0;
-      if (!weOpened) {
-        __android_log_unlock();
-      } else if (pmsgOpen() < 0) {
-        __android_log_unlock();
-        free(cp);
-        return -EBADF;
-      }
-    }
-
-    ret = pmsgWrite(logId, &ts, vec, sizeof(vec) / sizeof(vec[0]));
-
-    if (ret <= 0) {
-      if (weOpened) {
-        pmsgClose();
-        __android_log_unlock();
-      }
-      free(cp);
-      return ret ? ret : (len - length);
-    }
-    length -= transfer;
-    buf += transfer;
-  }
-  if (weOpened) {
-    pmsgClose();
-    __android_log_unlock();
-  }
-  free(cp);
-  return len;
-}
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
new file mode 100644
index 0000000..e851100
--- /dev/null
+++ b/liblog/pmsg_writer.cpp
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2007-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.
+ */
+
+/*
+ * pmsg write handler
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <log/log_properties.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "config_write.h"
+#include "log_portability.h"
+#include "logger.h"
+#include "uio.h"
+
+static int pmsgOpen();
+static void pmsgClose();
+static int pmsgAvailable(log_id_t logId);
+static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
+
+struct android_log_transport_write pmsgLoggerWrite = {
+    .node = {&pmsgLoggerWrite.node, &pmsgLoggerWrite.node},
+    .context.fd = -1,
+    .name = "pmsg",
+    .available = pmsgAvailable,
+    .open = pmsgOpen,
+    .close = pmsgClose,
+    .write = pmsgWrite,
+};
+
+static int pmsgOpen() {
+  int fd = atomic_load(&pmsgLoggerWrite.context.fd);
+  if (fd < 0) {
+    int i;
+
+    fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+    i = atomic_exchange(&pmsgLoggerWrite.context.fd, fd);
+    if ((i >= 0) && (i != fd)) {
+      close(i);
+    }
+  }
+
+  return fd;
+}
+
+static void pmsgClose() {
+  int fd = atomic_exchange(&pmsgLoggerWrite.context.fd, -1);
+  if (fd >= 0) {
+    close(fd);
+  }
+}
+
+static int pmsgAvailable(log_id_t logId) {
+  if (logId > LOG_ID_SECURITY) {
+    return -EINVAL;
+  }
+  if ((logId != LOG_ID_SECURITY) && (logId != LOG_ID_EVENTS) && !__android_log_is_debuggable()) {
+    return -EINVAL;
+  }
+  if (atomic_load(&pmsgLoggerWrite.context.fd) < 0) {
+    if (access("/dev/pmsg0", W_OK) == 0) {
+      return 0;
+    }
+    return -EBADF;
+  }
+  return 1;
+}
+
+/*
+ * Extract a 4-byte value from a byte stream.
+ */
+static inline uint32_t get4LE(const uint8_t* src) {
+  return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
+  static const unsigned headerLength = 2;
+  struct iovec newVec[nr + headerLength];
+  android_log_header_t header;
+  android_pmsg_log_header_t pmsgHeader;
+  size_t i, payloadSize;
+  ssize_t ret;
+
+  if ((logId == LOG_ID_EVENTS) && !__android_log_is_debuggable()) {
+    if (vec[0].iov_len < 4) {
+      return -EINVAL;
+    }
+
+    if (SNET_EVENT_LOG_TAG != get4LE(static_cast<uint8_t*>(vec[0].iov_base))) {
+      return -EPERM;
+    }
+  }
+
+  if (atomic_load(&pmsgLoggerWrite.context.fd) < 0) {
+    return -EBADF;
+  }
+
+  /*
+   *  struct {
+   *      // what we provide to pstore
+   *      android_pmsg_log_header_t pmsgHeader;
+   *      // what we provide to file
+   *      android_log_header_t header;
+   *      // caller provides
+   *      union {
+   *          struct {
+   *              char     prio;
+   *              char     payload[];
+   *          } string;
+   *          struct {
+   *              uint32_t tag
+   *              char     payload[];
+   *          } binary;
+   *      };
+   *  };
+   */
+
+  pmsgHeader.magic = LOGGER_MAGIC;
+  pmsgHeader.len = sizeof(pmsgHeader) + sizeof(header);
+  pmsgHeader.uid = __android_log_uid();
+  pmsgHeader.pid = getpid();
+
+  header.id = logId;
+  header.tid = gettid();
+  header.realtime.tv_sec = ts->tv_sec;
+  header.realtime.tv_nsec = ts->tv_nsec;
+
+  newVec[0].iov_base = (unsigned char*)&pmsgHeader;
+  newVec[0].iov_len = sizeof(pmsgHeader);
+  newVec[1].iov_base = (unsigned char*)&header;
+  newVec[1].iov_len = sizeof(header);
+
+  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;
+      }
+      payloadSize = LOGGER_ENTRY_MAX_PAYLOAD;
+      break;
+    }
+  }
+  pmsgHeader.len += payloadSize;
+
+  ret = TEMP_FAILURE_RETRY(writev(atomic_load(&pmsgLoggerWrite.context.fd), newVec, i));
+  if (ret < 0) {
+    ret = errno ? -errno : -ENOTCONN;
+  }
+
+  if (ret > (ssize_t)(sizeof(header) + sizeof(pmsgHeader))) {
+    ret -= sizeof(header) - sizeof(pmsgHeader);
+  }
+
+  return ret;
+}
+
+/*
+ * Virtual pmsg filesystem
+ *
+ * Payload will comprise the string "<basedir>:<basefile>\0<content>" to a
+ * maximum of LOGGER_ENTRY_MAX_PAYLOAD, but scaled to the last newline in the
+ * file.
+ *
+ * Will hijack the header.realtime.tv_nsec field for a sequence number in usec.
+ */
+
+static inline const char* strnrchr(const char* buf, size_t len, char c) {
+  const char* cp = buf + len;
+  while ((--cp > buf) && (*cp != c))
+    ;
+  if (cp <= buf) {
+    return buf + len;
+  }
+  return cp;
+}
+
+/* Write a buffer as filename references (tag = <basedir>:<basename>) */
+ssize_t __android_log_pmsg_file_write(log_id_t logId, char prio, const char* filename,
+                                      const char* buf, size_t len) {
+  bool weOpened;
+  size_t length, packet_len;
+  const char* tag;
+  char *cp, *slash;
+  struct timespec ts;
+  struct iovec vec[3];
+
+  /* Make sure the logId value is not a bad idea */
+  if ((logId == LOG_ID_KERNEL) ||   /* Verbotten */
+      (logId == LOG_ID_EVENTS) ||   /* Do not support binary content */
+      (logId == LOG_ID_SECURITY) || /* Bad idea to allow */
+      ((unsigned)logId >= 32)) {    /* fit within logMask on arch32 */
+    return -EINVAL;
+  }
+
+  clock_gettime(android_log_clockid(), &ts);
+
+  cp = strdup(filename);
+  if (!cp) {
+    return -ENOMEM;
+  }
+
+  tag = cp;
+  slash = strrchr(cp, '/');
+  if (slash) {
+    *slash = ':';
+    slash = strrchr(cp, '/');
+    if (slash) {
+      tag = slash + 1;
+    }
+  }
+
+  length = strlen(tag) + 1;
+  packet_len = LOGGER_ENTRY_MAX_PAYLOAD - sizeof(char) - length;
+
+  vec[0].iov_base = &prio;
+  vec[0].iov_len = sizeof(char);
+  vec[1].iov_base = (unsigned char*)tag;
+  vec[1].iov_len = length;
+
+  weOpened = false;
+  for (ts.tv_nsec = 0, length = len; length; ts.tv_nsec += ANDROID_LOG_PMSG_FILE_SEQUENCE) {
+    ssize_t ret;
+    size_t transfer;
+
+    if ((ts.tv_nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >= ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE) {
+      len -= length;
+      break;
+    }
+
+    transfer = length;
+    if (transfer > packet_len) {
+      transfer = strnrchr(buf, packet_len - 1, '\n') - buf;
+      if ((transfer < length) && (buf[transfer] == '\n')) {
+        ++transfer;
+      }
+    }
+
+    vec[2].iov_base = (unsigned char*)buf;
+    vec[2].iov_len = transfer;
+
+    if (atomic_load(&pmsgLoggerWrite.context.fd) < 0) {
+      if (!weOpened) { /* Impossible for weOpened = true here */
+        __android_log_lock();
+      }
+      weOpened = atomic_load(&pmsgLoggerWrite.context.fd) < 0;
+      if (!weOpened) {
+        __android_log_unlock();
+      } else if (pmsgOpen() < 0) {
+        __android_log_unlock();
+        free(cp);
+        return -EBADF;
+      }
+    }
+
+    ret = pmsgWrite(logId, &ts, vec, sizeof(vec) / sizeof(vec[0]));
+
+    if (ret <= 0) {
+      if (weOpened) {
+        pmsgClose();
+        __android_log_unlock();
+      }
+      free(cp);
+      return ret ? ret : (len - length);
+    }
+    length -= transfer;
+    buf += transfer;
+  }
+  if (weOpened) {
+    pmsgClose();
+    __android_log_unlock();
+  }
+  free(cp);
+  return len;
+}
diff --git a/liblog/properties.c b/liblog/properties.c
deleted file mode 100644
index 11be827..0000000
--- a/liblog/properties.c
+++ /dev/null
@@ -1,646 +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.
-*/
-
-#include <ctype.h>
-#include <pthread.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
-#include <unistd.h>
-
-#include <private/android_logger.h>
-
-#include "log_portability.h"
-
-static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER;
-
-static int 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.
-   */
-  /*
-   *  Any contention, and we can turn around and use the non-cached method
-   * in less time than the system call associated with a mutex to deal with
-   * the contention.
-   */
-  return pthread_mutex_trylock(&lock_loggable);
-}
-
-static void unlock() {
-  pthread_mutex_unlock(&lock_loggable);
-}
-
-struct cache {
-  const prop_info* pinfo;
-  uint32_t serial;
-};
-
-struct cache_char {
-  struct cache cache;
-  unsigned char c;
-};
-
-static int check_cache(struct cache* cache) {
-  return cache->pinfo && __system_property_serial(cache->pinfo) != cache->serial;
-}
-
-#define BOOLEAN_TRUE 0xFF
-#define BOOLEAN_FALSE 0xFE
-
-static void refresh_cache(struct cache_char* cache, const char* key) {
-  char buf[PROP_VALUE_MAX];
-
-  if (!cache->cache.pinfo) {
-    cache->cache.pinfo = __system_property_find(key);
-    if (!cache->cache.pinfo) {
-      return;
-    }
-  }
-  cache->cache.serial = __system_property_serial(cache->cache.pinfo);
-  __system_property_read(cache->cache.pinfo, 0, buf);
-  switch (buf[0]) {
-    case 't':
-    case 'T':
-      cache->c = strcasecmp(buf + 1, "rue") ? buf[0] : BOOLEAN_TRUE;
-      break;
-    case 'f':
-    case 'F':
-      cache->c = strcasecmp(buf + 1, "alse") ? buf[0] : BOOLEAN_FALSE;
-      break;
-    default:
-      cache->c = buf[0];
-  }
-}
-
-static int __android_log_level(const char* tag, size_t len, int default_prio) {
-  /* sizeof() is used on this array below */
-  static const char log_namespace[] = "persist.log.tag.";
-  static const size_t base_offset = 8; /* skip "persist." */
-  /* calculate the size of our key temporary buffer */
-  const size_t taglen = tag ? len : 0;
-  /* sizeof(log_namespace) = strlen(log_namespace) + 1 */
-  char key[sizeof(log_namespace) + taglen];
-  char* kp;
-  size_t i;
-  char c = 0;
-  /*
-   * Single layer cache of four properties. Priorities are:
-   *    log.tag.<tag>
-   *    persist.log.tag.<tag>
-   *    log.tag
-   *    persist.log.tag
-   * Where the missing tag matches all tags and becomes the
-   * system global default. We do not support ro.log.tag* .
-   */
-  static char* last_tag;
-  static size_t last_tag_len;
-  static uint32_t global_serial;
-  /* some compilers erroneously see uninitialized use. !not_locked */
-  uint32_t current_global_serial = 0;
-  static struct cache_char tag_cache[2];
-  static struct cache_char global_cache[2];
-  int change_detected;
-  int global_change_detected;
-  int not_locked;
-
-  strcpy(key, log_namespace);
-
-  global_change_detected = change_detected = not_locked = lock();
-
-  if (!not_locked) {
-    /*
-     *  check all known serial numbers to changes.
-     */
-    for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
-      if (check_cache(&tag_cache[i].cache)) {
-        change_detected = 1;
-      }
-    }
-    for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
-      if (check_cache(&global_cache[i].cache)) {
-        global_change_detected = 1;
-      }
-    }
-
-    current_global_serial = __system_property_area_serial();
-    if (current_global_serial != global_serial) {
-      change_detected = 1;
-      global_change_detected = 1;
-    }
-  }
-
-  if (taglen) {
-    int local_change_detected = change_detected;
-    if (!not_locked) {
-      if (!last_tag || !last_tag[0] || (last_tag[0] != tag[0]) ||
-          strncmp(last_tag + 1, tag + 1, last_tag_len - 1)) {
-        /* invalidate log.tag.<tag> cache */
-        for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
-          tag_cache[i].cache.pinfo = NULL;
-          tag_cache[i].c = '\0';
-        }
-        if (last_tag) last_tag[0] = '\0';
-        local_change_detected = 1;
-      }
-      if (!last_tag || !last_tag[0]) {
-        if (!last_tag) {
-          last_tag = calloc(1, len + 1);
-          last_tag_len = 0;
-          if (last_tag) last_tag_len = len + 1;
-        } else if (len >= last_tag_len) {
-          last_tag = realloc(last_tag, len + 1);
-          last_tag_len = 0;
-          if (last_tag) last_tag_len = len + 1;
-        }
-        if (last_tag) {
-          strncpy(last_tag, tag, len);
-          last_tag[len] = '\0';
-        }
-      }
-    }
-    strncpy(key + sizeof(log_namespace) - 1, tag, len);
-    key[sizeof(log_namespace) - 1 + len] = '\0';
-
-    kp = key;
-    for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
-      struct cache_char* cache = &tag_cache[i];
-      struct cache_char temp_cache;
-
-      if (not_locked) {
-        temp_cache.cache.pinfo = NULL;
-        temp_cache.c = '\0';
-        cache = &temp_cache;
-      }
-      if (local_change_detected) {
-        refresh_cache(cache, kp);
-      }
-
-      if (cache->c) {
-        c = cache->c;
-        break;
-      }
-
-      kp = key + base_offset;
-    }
-  }
-
-  switch (toupper(c)) { /* if invalid, resort to global */
-    case 'V':
-    case 'D':
-    case 'I':
-    case 'W':
-    case 'E':
-    case 'F': /* Not officially supported */
-    case 'A':
-    case 'S':
-    case BOOLEAN_FALSE: /* Not officially supported */
-      break;
-    default:
-      /* clear '.' after log.tag */
-      key[sizeof(log_namespace) - 2] = '\0';
-
-      kp = key;
-      for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
-        struct cache_char* cache = &global_cache[i];
-        struct cache_char temp_cache;
-
-        if (not_locked) {
-          temp_cache = *cache;
-          if (temp_cache.cache.pinfo != cache->cache.pinfo) { /* check atomic */
-            temp_cache.cache.pinfo = NULL;
-            temp_cache.c = '\0';
-          }
-          cache = &temp_cache;
-        }
-        if (global_change_detected) {
-          refresh_cache(cache, kp);
-        }
-
-        if (cache->c) {
-          c = cache->c;
-          break;
-        }
-
-        kp = key + base_offset;
-      }
-      break;
-  }
-
-  if (!not_locked) {
-    global_serial = current_global_serial;
-    unlock();
-  }
-
-  switch (toupper(c)) {
-    /* clang-format off */
-    case 'V': return ANDROID_LOG_VERBOSE;
-    case 'D': return ANDROID_LOG_DEBUG;
-    case 'I': return ANDROID_LOG_INFO;
-    case 'W': return ANDROID_LOG_WARN;
-    case 'E': return ANDROID_LOG_ERROR;
-    case 'F': /* FALLTHRU */ /* Not officially supported */
-    case 'A': return ANDROID_LOG_FATAL;
-    case BOOLEAN_FALSE: /* FALLTHRU */ /* Not Officially supported */
-    case 'S': return -1; /* ANDROID_LOG_SUPPRESS */
-    /* clang-format on */
-  }
-  return default_prio;
-}
-
-LIBLOG_ABI_PUBLIC int __android_log_is_loggable_len(int prio, const char* tag,
-                                                    size_t len,
-                                                    int default_prio) {
-  int logLevel = __android_log_level(tag, len, default_prio);
-  return logLevel >= 0 && prio >= logLevel;
-}
-
-LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio, const char* tag,
-                                                int default_prio) {
-  int logLevel =
-      __android_log_level(tag, (tag && *tag) ? strlen(tag) : 0, default_prio);
-  return logLevel >= 0 && prio >= logLevel;
-}
-
-LIBLOG_ABI_PUBLIC int __android_log_is_debuggable() {
-  static uint32_t serial;
-  static struct cache_char tag_cache;
-  static const char key[] = "ro.debuggable";
-  int ret;
-
-  if (tag_cache.c) { /* ro property does not change after set */
-    ret = tag_cache.c == '1';
-  } else if (lock()) {
-    struct cache_char temp_cache = { { NULL, -1 }, '\0' };
-    refresh_cache(&temp_cache, key);
-    ret = temp_cache.c == '1';
-  } else {
-    int change_detected = check_cache(&tag_cache.cache);
-    uint32_t current_serial = __system_property_area_serial();
-    if (current_serial != serial) {
-      change_detected = 1;
-    }
-    if (change_detected) {
-      refresh_cache(&tag_cache, key);
-      serial = current_serial;
-    }
-    ret = tag_cache.c == '1';
-
-    unlock();
-  }
-
-  return ret;
-}
-
-/*
- * For properties that are read often, but generally remain constant.
- * Since a change is rare, we will accept a trylock failure gracefully.
- * Use a separate lock from is_loggable to keep contention down b/25563384.
- */
-struct cache2_char {
-  pthread_mutex_t lock;
-  uint32_t serial;
-  const char* key_persist;
-  struct cache_char cache_persist;
-  const char* key_ro;
-  struct cache_char cache_ro;
-  unsigned char (*const evaluate)(const struct cache2_char* self);
-};
-
-static inline unsigned char do_cache2_char(struct cache2_char* self) {
-  uint32_t current_serial;
-  int change_detected;
-  unsigned char c;
-
-  if (pthread_mutex_trylock(&self->lock)) {
-    /* We are willing to accept some race in this context */
-    return self->evaluate(self);
-  }
-
-  change_detected = check_cache(&self->cache_persist.cache) ||
-                    check_cache(&self->cache_ro.cache);
-  current_serial = __system_property_area_serial();
-  if (current_serial != self->serial) {
-    change_detected = 1;
-  }
-  if (change_detected) {
-    refresh_cache(&self->cache_persist, self->key_persist);
-    refresh_cache(&self->cache_ro, self->key_ro);
-    self->serial = current_serial;
-  }
-  c = self->evaluate(self);
-
-  pthread_mutex_unlock(&self->lock);
-
-  return c;
-}
-
-static unsigned char evaluate_persist_ro(const struct cache2_char* self) {
-  unsigned char c = self->cache_persist.c;
-
-  if (c) {
-    return c;
-  }
-
-  return self->cache_ro.c;
-}
-
-/*
- * Timestamp state generally remains constant, but can change at any time
- * to handle developer requirements.
- */
-LIBLOG_ABI_PUBLIC clockid_t android_log_clockid() {
-  static struct cache2_char clockid = {
-    PTHREAD_MUTEX_INITIALIZER, 0,
-    "persist.logd.timestamp",  { { NULL, -1 }, '\0' },
-    "ro.logd.timestamp",       { { NULL, -1 }, '\0' },
-    evaluate_persist_ro
-  };
-
-  return (tolower(do_cache2_char(&clockid)) == 'm') ? CLOCK_MONOTONIC
-                                                    : CLOCK_REALTIME;
-}
-
-/*
- * Security state generally remains constant, but the DO must be able
- * to turn off logging should it become spammy after an attack is detected.
- */
-static unsigned char evaluate_security(const struct cache2_char* self) {
-  unsigned char c = self->cache_ro.c;
-
-  return (c != BOOLEAN_FALSE) && c && (self->cache_persist.c == BOOLEAN_TRUE);
-}
-
-LIBLOG_ABI_PUBLIC int __android_log_security() {
-  static struct cache2_char security = {
-    PTHREAD_MUTEX_INITIALIZER, 0,
-    "persist.logd.security",   { { NULL, -1 }, BOOLEAN_FALSE },
-    "ro.device_owner",         { { NULL, -1 }, BOOLEAN_FALSE },
-    evaluate_security
-  };
-
-  return do_cache2_char(&security);
-}
-
-/*
- * Interface that represents the logd buffer size determination so that others
- * need not guess our intentions.
- */
-
-/* Property helper */
-static bool check_flag(const char* prop, const char* flag) {
-  const char* cp = strcasestr(prop, flag);
-  if (!cp) {
-    return false;
-  }
-  /* We only will document comma (,) */
-  static const char sep[] = ",:;|+ \t\f";
-  if ((cp != prop) && !strchr(sep, cp[-1])) {
-    return false;
-  }
-  cp += strlen(flag);
-  return !*cp || !!strchr(sep, *cp);
-}
-
-/* cache structure */
-struct cache_property {
-  struct cache cache;
-  char property[PROP_VALUE_MAX];
-};
-
-static void refresh_cache_property(struct cache_property* cache,
-                                   const char* key) {
-  if (!cache->cache.pinfo) {
-    cache->cache.pinfo = __system_property_find(key);
-    if (!cache->cache.pinfo) {
-      return;
-    }
-  }
-  cache->cache.serial = __system_property_serial(cache->cache.pinfo);
-  __system_property_read(cache->cache.pinfo, 0, cache->property);
-}
-
-/* get boolean with the logger twist that supports eng adjustments */
-LIBLOG_ABI_PRIVATE bool __android_logger_property_get_bool(const char* key,
-                                                           int flag) {
-  struct cache_property property = { { NULL, -1 }, { 0 } };
-  if (flag & BOOL_DEFAULT_FLAG_PERSIST) {
-    char newkey[strlen("persist.") + strlen(key) + 1];
-    snprintf(newkey, sizeof(newkey), "ro.%s", key);
-    refresh_cache_property(&property, newkey);
-    property.cache.pinfo = NULL;
-    property.cache.serial = -1;
-    snprintf(newkey, sizeof(newkey), "persist.%s", key);
-    refresh_cache_property(&property, newkey);
-    property.cache.pinfo = NULL;
-    property.cache.serial = -1;
-  }
-
-  refresh_cache_property(&property, key);
-
-  if (check_flag(property.property, "true")) {
-    return true;
-  }
-  if (check_flag(property.property, "false")) {
-    return false;
-  }
-  if (property.property[0]) {
-    flag &= ~(BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
-  }
-  if (check_flag(property.property, "eng")) {
-    flag |= BOOL_DEFAULT_FLAG_ENG;
-  }
-  /* this is really a "not" flag */
-  if (check_flag(property.property, "svelte")) {
-    flag |= BOOL_DEFAULT_FLAG_SVELTE;
-  }
-
-  /* Sanity Check */
-  if (flag & (BOOL_DEFAULT_FLAG_SVELTE | BOOL_DEFAULT_FLAG_ENG)) {
-    flag &= ~BOOL_DEFAULT_FLAG_TRUE_FALSE;
-    flag |= BOOL_DEFAULT_TRUE;
-  }
-
-  if ((flag & BOOL_DEFAULT_FLAG_SVELTE) &&
-      __android_logger_property_get_bool("ro.config.low_ram",
-                                         BOOL_DEFAULT_FALSE)) {
-    return false;
-  }
-  if ((flag & BOOL_DEFAULT_FLAG_ENG) && !__android_log_is_debuggable()) {
-    return false;
-  }
-
-  return (flag & BOOL_DEFAULT_FLAG_TRUE_FALSE) != BOOL_DEFAULT_FALSE;
-}
-
-LIBLOG_ABI_PRIVATE bool __android_logger_valid_buffer_size(unsigned long value) {
-  static long pages, pagesize;
-  unsigned long maximum;
-
-  if ((value < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < value)) {
-    return false;
-  }
-
-  if (!pages) {
-    pages = sysconf(_SC_PHYS_PAGES);
-  }
-  if (pages < 1) {
-    return true;
-  }
-
-  if (!pagesize) {
-    pagesize = sysconf(_SC_PAGESIZE);
-    if (pagesize <= 1) {
-      pagesize = PAGE_SIZE;
-    }
-  }
-
-  /* maximum memory impact a somewhat arbitrary ~3% */
-  pages = (pages + 31) / 32;
-  maximum = pages * pagesize;
-
-  if ((maximum < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < maximum)) {
-    return true;
-  }
-
-  return value <= maximum;
-}
-
-struct cache2_property_size {
-  pthread_mutex_t lock;
-  uint32_t serial;
-  const char* key_persist;
-  struct cache_property cache_persist;
-  const char* key_ro;
-  struct cache_property cache_ro;
-  unsigned long (*const evaluate)(const struct cache2_property_size* self);
-};
-
-static inline unsigned long do_cache2_property_size(
-    struct cache2_property_size* self) {
-  uint32_t current_serial;
-  int change_detected;
-  unsigned long v;
-
-  if (pthread_mutex_trylock(&self->lock)) {
-    /* We are willing to accept some race in this context */
-    return self->evaluate(self);
-  }
-
-  change_detected = check_cache(&self->cache_persist.cache) ||
-                    check_cache(&self->cache_ro.cache);
-  current_serial = __system_property_area_serial();
-  if (current_serial != self->serial) {
-    change_detected = 1;
-  }
-  if (change_detected) {
-    refresh_cache_property(&self->cache_persist, self->key_persist);
-    refresh_cache_property(&self->cache_ro, self->key_ro);
-    self->serial = current_serial;
-  }
-  v = self->evaluate(self);
-
-  pthread_mutex_unlock(&self->lock);
-
-  return v;
-}
-
-static unsigned long property_get_size_from_cache(
-    const struct cache_property* cache) {
-  char* cp;
-  unsigned long value = strtoul(cache->property, &cp, 10);
-
-  switch (*cp) {
-    case 'm':
-    case 'M':
-      value *= 1024;
-    /* FALLTHRU */
-    case 'k':
-    case 'K':
-      value *= 1024;
-    /* FALLTHRU */
-    case '\0':
-      break;
-
-    default:
-      value = 0;
-  }
-
-  if (!__android_logger_valid_buffer_size(value)) {
-    value = 0;
-  }
-
-  return value;
-}
-
-static unsigned long evaluate_property_get_size(
-    const struct cache2_property_size* self) {
-  unsigned long size = property_get_size_from_cache(&self->cache_persist);
-  if (size) {
-    return size;
-  }
-  return property_get_size_from_cache(&self->cache_ro);
-}
-
-LIBLOG_ABI_PRIVATE unsigned long __android_logger_get_buffer_size(log_id_t logId) {
-  static const char global_tunable[] = "persist.logd.size"; /* Settings App */
-  static const char global_default[] = "ro.logd.size";      /* BoardConfig.mk */
-  static struct cache2_property_size global = {
-    /* clang-format off */
-    PTHREAD_MUTEX_INITIALIZER, 0,
-    global_tunable, { { NULL, -1 }, {} },
-    global_default, { { NULL, -1 }, {} },
-    evaluate_property_get_size
-    /* clang-format on */
-  };
-  char key_persist[strlen(global_tunable) + strlen(".security") + 1];
-  char key_ro[strlen(global_default) + strlen(".security") + 1];
-  struct cache2_property_size local = {
-    /* clang-format off */
-    PTHREAD_MUTEX_INITIALIZER, 0,
-    key_persist, { { NULL, -1 }, {} },
-    key_ro,      { { NULL, -1 }, {} },
-    evaluate_property_get_size
-    /* clang-format on */
-  };
-  unsigned long property_size, default_size;
-
-  default_size = do_cache2_property_size(&global);
-  if (!default_size) {
-    default_size = __android_logger_property_get_bool("ro.config.low_ram",
-                                                      BOOL_DEFAULT_FALSE)
-                       ? LOG_BUFFER_MIN_SIZE /* 64K  */
-                       : LOG_BUFFER_SIZE;    /* 256K */
-  }
-
-  snprintf(key_persist, sizeof(key_persist), "%s.%s", global_tunable,
-           android_log_id_to_name(logId));
-  snprintf(key_ro, sizeof(key_ro), "%s.%s", global_default,
-           android_log_id_to_name(logId));
-  property_size = do_cache2_property_size(&local);
-
-  if (!property_size) {
-    property_size = default_size;
-  }
-
-  if (!property_size) {
-    property_size = LOG_BUFFER_SIZE;
-  }
-
-  return property_size;
-}
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
new file mode 100644
index 0000000..2e0a8c9
--- /dev/null
+++ b/liblog/properties.cpp
@@ -0,0 +1,629 @@
+/*
+** 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.
+*/
+
+#include <log/log_properties.h>
+
+#include <ctype.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+#include <unistd.h>
+
+#include <private/android_logger.h>
+
+#include "log_portability.h"
+
+static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER;
+
+static int 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.
+   */
+  /*
+   *  Any contention, and we can turn around and use the non-cached method
+   * in less time than the system call associated with a mutex to deal with
+   * the contention.
+   */
+  return pthread_mutex_trylock(&lock_loggable);
+}
+
+static void unlock() {
+  pthread_mutex_unlock(&lock_loggable);
+}
+
+struct cache {
+  const prop_info* pinfo;
+  uint32_t serial;
+};
+
+struct cache_char {
+  struct cache cache;
+  unsigned char c;
+};
+
+static int check_cache(struct cache* cache) {
+  return cache->pinfo && __system_property_serial(cache->pinfo) != cache->serial;
+}
+
+#define BOOLEAN_TRUE 0xFF
+#define BOOLEAN_FALSE 0xFE
+
+static void refresh_cache(struct cache_char* cache, const char* key) {
+  char buf[PROP_VALUE_MAX];
+
+  if (!cache->cache.pinfo) {
+    cache->cache.pinfo = __system_property_find(key);
+    if (!cache->cache.pinfo) {
+      return;
+    }
+  }
+  cache->cache.serial = __system_property_serial(cache->cache.pinfo);
+  __system_property_read(cache->cache.pinfo, 0, buf);
+  switch (buf[0]) {
+    case 't':
+    case 'T':
+      cache->c = strcasecmp(buf + 1, "rue") ? buf[0] : BOOLEAN_TRUE;
+      break;
+    case 'f':
+    case 'F':
+      cache->c = strcasecmp(buf + 1, "alse") ? buf[0] : BOOLEAN_FALSE;
+      break;
+    default:
+      cache->c = buf[0];
+  }
+}
+
+static int __android_log_level(const char* tag, size_t len, int default_prio) {
+  /* sizeof() is used on this array below */
+  static const char log_namespace[] = "persist.log.tag.";
+  static const size_t base_offset = 8; /* skip "persist." */
+  /* calculate the size of our key temporary buffer */
+  const size_t taglen = tag ? len : 0;
+  /* sizeof(log_namespace) = strlen(log_namespace) + 1 */
+  char key[sizeof(log_namespace) + taglen];
+  char* kp;
+  size_t i;
+  char c = 0;
+  /*
+   * Single layer cache of four properties. Priorities are:
+   *    log.tag.<tag>
+   *    persist.log.tag.<tag>
+   *    log.tag
+   *    persist.log.tag
+   * Where the missing tag matches all tags and becomes the
+   * system global default. We do not support ro.log.tag* .
+   */
+  static char* last_tag;
+  static size_t last_tag_len;
+  static uint32_t global_serial;
+  /* some compilers erroneously see uninitialized use. !not_locked */
+  uint32_t current_global_serial = 0;
+  static struct cache_char tag_cache[2];
+  static struct cache_char global_cache[2];
+  int change_detected;
+  int global_change_detected;
+  int not_locked;
+
+  strcpy(key, log_namespace);
+
+  global_change_detected = change_detected = not_locked = lock();
+
+  if (!not_locked) {
+    /*
+     *  check all known serial numbers to changes.
+     */
+    for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+      if (check_cache(&tag_cache[i].cache)) {
+        change_detected = 1;
+      }
+    }
+    for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
+      if (check_cache(&global_cache[i].cache)) {
+        global_change_detected = 1;
+      }
+    }
+
+    current_global_serial = __system_property_area_serial();
+    if (current_global_serial != global_serial) {
+      change_detected = 1;
+      global_change_detected = 1;
+    }
+  }
+
+  if (taglen) {
+    int local_change_detected = change_detected;
+    if (!not_locked) {
+      if (!last_tag || !last_tag[0] || (last_tag[0] != tag[0]) ||
+          strncmp(last_tag + 1, tag + 1, last_tag_len - 1)) {
+        /* invalidate log.tag.<tag> cache */
+        for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+          tag_cache[i].cache.pinfo = NULL;
+          tag_cache[i].c = '\0';
+        }
+        if (last_tag) last_tag[0] = '\0';
+        local_change_detected = 1;
+      }
+      if (!last_tag || !last_tag[0]) {
+        if (!last_tag) {
+          last_tag = static_cast<char*>(calloc(1, len + 1));
+          last_tag_len = 0;
+          if (last_tag) last_tag_len = len + 1;
+        } else if (len >= last_tag_len) {
+          last_tag = static_cast<char*>(realloc(last_tag, len + 1));
+          last_tag_len = 0;
+          if (last_tag) last_tag_len = len + 1;
+        }
+        if (last_tag) {
+          strncpy(last_tag, tag, len);
+          last_tag[len] = '\0';
+        }
+      }
+    }
+    strncpy(key + sizeof(log_namespace) - 1, tag, len);
+    key[sizeof(log_namespace) - 1 + len] = '\0';
+
+    kp = key;
+    for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+      struct cache_char* cache = &tag_cache[i];
+      struct cache_char temp_cache;
+
+      if (not_locked) {
+        temp_cache.cache.pinfo = NULL;
+        temp_cache.c = '\0';
+        cache = &temp_cache;
+      }
+      if (local_change_detected) {
+        refresh_cache(cache, kp);
+      }
+
+      if (cache->c) {
+        c = cache->c;
+        break;
+      }
+
+      kp = key + base_offset;
+    }
+  }
+
+  switch (toupper(c)) { /* if invalid, resort to global */
+    case 'V':
+    case 'D':
+    case 'I':
+    case 'W':
+    case 'E':
+    case 'F': /* Not officially supported */
+    case 'A':
+    case 'S':
+    case BOOLEAN_FALSE: /* Not officially supported */
+      break;
+    default:
+      /* clear '.' after log.tag */
+      key[sizeof(log_namespace) - 2] = '\0';
+
+      kp = key;
+      for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
+        struct cache_char* cache = &global_cache[i];
+        struct cache_char temp_cache;
+
+        if (not_locked) {
+          temp_cache = *cache;
+          if (temp_cache.cache.pinfo != cache->cache.pinfo) { /* check atomic */
+            temp_cache.cache.pinfo = NULL;
+            temp_cache.c = '\0';
+          }
+          cache = &temp_cache;
+        }
+        if (global_change_detected) {
+          refresh_cache(cache, kp);
+        }
+
+        if (cache->c) {
+          c = cache->c;
+          break;
+        }
+
+        kp = key + base_offset;
+      }
+      break;
+  }
+
+  if (!not_locked) {
+    global_serial = current_global_serial;
+    unlock();
+  }
+
+  switch (toupper(c)) {
+    /* clang-format off */
+    case 'V': return ANDROID_LOG_VERBOSE;
+    case 'D': return ANDROID_LOG_DEBUG;
+    case 'I': return ANDROID_LOG_INFO;
+    case 'W': return ANDROID_LOG_WARN;
+    case 'E': return ANDROID_LOG_ERROR;
+    case 'F': /* FALLTHRU */ /* Not officially supported */
+    case 'A': return ANDROID_LOG_FATAL;
+    case BOOLEAN_FALSE: /* FALLTHRU */ /* Not Officially supported */
+    case 'S': return -1; /* ANDROID_LOG_SUPPRESS */
+      /* clang-format on */
+  }
+  return default_prio;
+}
+
+int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio) {
+  int logLevel = __android_log_level(tag, len, default_prio);
+  return logLevel >= 0 && prio >= logLevel;
+}
+
+int __android_log_is_loggable(int prio, const char* tag, int default_prio) {
+  int logLevel = __android_log_level(tag, (tag && *tag) ? strlen(tag) : 0, default_prio);
+  return logLevel >= 0 && prio >= logLevel;
+}
+
+int __android_log_is_debuggable() {
+  static uint32_t serial;
+  static struct cache_char tag_cache;
+  static const char key[] = "ro.debuggable";
+  int ret;
+
+  if (tag_cache.c) { /* ro property does not change after set */
+    ret = tag_cache.c == '1';
+  } else if (lock()) {
+    struct cache_char temp_cache = {{NULL, 0xFFFFFFFF}, '\0'};
+    refresh_cache(&temp_cache, key);
+    ret = temp_cache.c == '1';
+  } else {
+    int change_detected = check_cache(&tag_cache.cache);
+    uint32_t current_serial = __system_property_area_serial();
+    if (current_serial != serial) {
+      change_detected = 1;
+    }
+    if (change_detected) {
+      refresh_cache(&tag_cache, key);
+      serial = current_serial;
+    }
+    ret = tag_cache.c == '1';
+
+    unlock();
+  }
+
+  return ret;
+}
+
+/*
+ * For properties that are read often, but generally remain constant.
+ * Since a change is rare, we will accept a trylock failure gracefully.
+ * Use a separate lock from is_loggable to keep contention down b/25563384.
+ */
+struct cache2_char {
+  pthread_mutex_t lock;
+  uint32_t serial;
+  const char* key_persist;
+  struct cache_char cache_persist;
+  const char* key_ro;
+  struct cache_char cache_ro;
+  unsigned char (*const evaluate)(const struct cache2_char* self);
+};
+
+static inline unsigned char do_cache2_char(struct cache2_char* self) {
+  uint32_t current_serial;
+  int change_detected;
+  unsigned char c;
+
+  if (pthread_mutex_trylock(&self->lock)) {
+    /* We are willing to accept some race in this context */
+    return self->evaluate(self);
+  }
+
+  change_detected = check_cache(&self->cache_persist.cache) || check_cache(&self->cache_ro.cache);
+  current_serial = __system_property_area_serial();
+  if (current_serial != self->serial) {
+    change_detected = 1;
+  }
+  if (change_detected) {
+    refresh_cache(&self->cache_persist, self->key_persist);
+    refresh_cache(&self->cache_ro, self->key_ro);
+    self->serial = current_serial;
+  }
+  c = self->evaluate(self);
+
+  pthread_mutex_unlock(&self->lock);
+
+  return c;
+}
+
+static unsigned char evaluate_persist_ro(const struct cache2_char* self) {
+  unsigned char c = self->cache_persist.c;
+
+  if (c) {
+    return c;
+  }
+
+  return self->cache_ro.c;
+}
+
+/*
+ * Timestamp state generally remains constant, but can change at any time
+ * to handle developer requirements.
+ */
+clockid_t android_log_clockid() {
+  static struct cache2_char clockid = {PTHREAD_MUTEX_INITIALIZER, 0,
+                                       "persist.logd.timestamp",  {{NULL, 0xFFFFFFFF}, '\0'},
+                                       "ro.logd.timestamp",       {{NULL, 0xFFFFFFFF}, '\0'},
+                                       evaluate_persist_ro};
+
+  return (tolower(do_cache2_char(&clockid)) == 'm') ? CLOCK_MONOTONIC : CLOCK_REALTIME;
+}
+
+/*
+ * Security state generally remains constant, but the DO must be able
+ * to turn off logging should it become spammy after an attack is detected.
+ */
+static unsigned char evaluate_security(const struct cache2_char* self) {
+  unsigned char c = self->cache_ro.c;
+
+  return (c != BOOLEAN_FALSE) && c && (self->cache_persist.c == BOOLEAN_TRUE);
+}
+
+int __android_log_security() {
+  static struct cache2_char security = {
+      PTHREAD_MUTEX_INITIALIZER, 0,
+      "persist.logd.security",   {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
+      "ro.device_owner",         {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
+      evaluate_security};
+
+  return do_cache2_char(&security);
+}
+
+/*
+ * Interface that represents the logd buffer size determination so that others
+ * need not guess our intentions.
+ */
+
+/* Property helper */
+static bool check_flag(const char* prop, const char* flag) {
+  const char* cp = strcasestr(prop, flag);
+  if (!cp) {
+    return false;
+  }
+  /* We only will document comma (,) */
+  static const char sep[] = ",:;|+ \t\f";
+  if ((cp != prop) && !strchr(sep, cp[-1])) {
+    return false;
+  }
+  cp += strlen(flag);
+  return !*cp || !!strchr(sep, *cp);
+}
+
+/* cache structure */
+struct cache_property {
+  struct cache cache;
+  char property[PROP_VALUE_MAX];
+};
+
+static void refresh_cache_property(struct cache_property* cache, const char* key) {
+  if (!cache->cache.pinfo) {
+    cache->cache.pinfo = __system_property_find(key);
+    if (!cache->cache.pinfo) {
+      return;
+    }
+  }
+  cache->cache.serial = __system_property_serial(cache->cache.pinfo);
+  __system_property_read(cache->cache.pinfo, 0, cache->property);
+}
+
+/* get boolean with the logger twist that supports eng adjustments */
+bool __android_logger_property_get_bool(const char* key, int flag) {
+  struct cache_property property = {{NULL, 0xFFFFFFFF}, {0}};
+  if (flag & BOOL_DEFAULT_FLAG_PERSIST) {
+    char newkey[strlen("persist.") + strlen(key) + 1];
+    snprintf(newkey, sizeof(newkey), "ro.%s", key);
+    refresh_cache_property(&property, newkey);
+    property.cache.pinfo = NULL;
+    property.cache.serial = 0xFFFFFFFF;
+    snprintf(newkey, sizeof(newkey), "persist.%s", key);
+    refresh_cache_property(&property, newkey);
+    property.cache.pinfo = NULL;
+    property.cache.serial = 0xFFFFFFFF;
+  }
+
+  refresh_cache_property(&property, key);
+
+  if (check_flag(property.property, "true")) {
+    return true;
+  }
+  if (check_flag(property.property, "false")) {
+    return false;
+  }
+  if (property.property[0]) {
+    flag &= ~(BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
+  }
+  if (check_flag(property.property, "eng")) {
+    flag |= BOOL_DEFAULT_FLAG_ENG;
+  }
+  /* this is really a "not" flag */
+  if (check_flag(property.property, "svelte")) {
+    flag |= BOOL_DEFAULT_FLAG_SVELTE;
+  }
+
+  /* Sanity Check */
+  if (flag & (BOOL_DEFAULT_FLAG_SVELTE | BOOL_DEFAULT_FLAG_ENG)) {
+    flag &= ~BOOL_DEFAULT_FLAG_TRUE_FALSE;
+    flag |= BOOL_DEFAULT_TRUE;
+  }
+
+  if ((flag & BOOL_DEFAULT_FLAG_SVELTE) &&
+      __android_logger_property_get_bool("ro.config.low_ram", BOOL_DEFAULT_FALSE)) {
+    return false;
+  }
+  if ((flag & BOOL_DEFAULT_FLAG_ENG) && !__android_log_is_debuggable()) {
+    return false;
+  }
+
+  return (flag & BOOL_DEFAULT_FLAG_TRUE_FALSE) != BOOL_DEFAULT_FALSE;
+}
+
+bool __android_logger_valid_buffer_size(unsigned long value) {
+  static long pages, pagesize;
+  unsigned long maximum;
+
+  if ((value < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < value)) {
+    return false;
+  }
+
+  if (!pages) {
+    pages = sysconf(_SC_PHYS_PAGES);
+  }
+  if (pages < 1) {
+    return true;
+  }
+
+  if (!pagesize) {
+    pagesize = sysconf(_SC_PAGESIZE);
+    if (pagesize <= 1) {
+      pagesize = PAGE_SIZE;
+    }
+  }
+
+  /* maximum memory impact a somewhat arbitrary ~3% */
+  pages = (pages + 31) / 32;
+  maximum = pages * pagesize;
+
+  if ((maximum < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < maximum)) {
+    return true;
+  }
+
+  return value <= maximum;
+}
+
+struct cache2_property_size {
+  pthread_mutex_t lock;
+  uint32_t serial;
+  const char* key_persist;
+  struct cache_property cache_persist;
+  const char* key_ro;
+  struct cache_property cache_ro;
+  unsigned long (*const evaluate)(const struct cache2_property_size* self);
+};
+
+static inline unsigned long do_cache2_property_size(struct cache2_property_size* self) {
+  uint32_t current_serial;
+  int change_detected;
+  unsigned long v;
+
+  if (pthread_mutex_trylock(&self->lock)) {
+    /* We are willing to accept some race in this context */
+    return self->evaluate(self);
+  }
+
+  change_detected = check_cache(&self->cache_persist.cache) || check_cache(&self->cache_ro.cache);
+  current_serial = __system_property_area_serial();
+  if (current_serial != self->serial) {
+    change_detected = 1;
+  }
+  if (change_detected) {
+    refresh_cache_property(&self->cache_persist, self->key_persist);
+    refresh_cache_property(&self->cache_ro, self->key_ro);
+    self->serial = current_serial;
+  }
+  v = self->evaluate(self);
+
+  pthread_mutex_unlock(&self->lock);
+
+  return v;
+}
+
+static unsigned long property_get_size_from_cache(const struct cache_property* cache) {
+  char* cp;
+  unsigned long value = strtoul(cache->property, &cp, 10);
+
+  switch (*cp) {
+    case 'm':
+    case 'M':
+      value *= 1024;
+      [[fallthrough]];
+    case 'k':
+    case 'K':
+      value *= 1024;
+      [[fallthrough]];
+    case '\0':
+      break;
+
+    default:
+      value = 0;
+  }
+
+  if (!__android_logger_valid_buffer_size(value)) {
+    value = 0;
+  }
+
+  return value;
+}
+
+static unsigned long evaluate_property_get_size(const struct cache2_property_size* self) {
+  unsigned long size = property_get_size_from_cache(&self->cache_persist);
+  if (size) {
+    return size;
+  }
+  return property_get_size_from_cache(&self->cache_ro);
+}
+
+unsigned long __android_logger_get_buffer_size(log_id_t logId) {
+  static const char global_tunable[] = "persist.logd.size"; /* Settings App */
+  static const char global_default[] = "ro.logd.size";      /* BoardConfig.mk */
+  static struct cache2_property_size global = {
+      /* clang-format off */
+    PTHREAD_MUTEX_INITIALIZER, 0,
+    global_tunable, { { NULL, 0xFFFFFFFF }, {} },
+    global_default, { { NULL, 0xFFFFFFFF }, {} },
+    evaluate_property_get_size
+      /* clang-format on */
+  };
+  char key_persist[strlen(global_tunable) + strlen(".security") + 1];
+  char key_ro[strlen(global_default) + strlen(".security") + 1];
+  struct cache2_property_size local = {
+      /* clang-format off */
+    PTHREAD_MUTEX_INITIALIZER, 0,
+    key_persist, { { NULL, 0xFFFFFFFF }, {} },
+    key_ro,      { { NULL, 0xFFFFFFFF }, {} },
+    evaluate_property_get_size
+      /* clang-format on */
+  };
+  unsigned long property_size, default_size;
+
+  default_size = do_cache2_property_size(&global);
+  if (!default_size) {
+    default_size = __android_logger_property_get_bool("ro.config.low_ram", BOOL_DEFAULT_FALSE)
+                       ? LOG_BUFFER_MIN_SIZE /* 64K  */
+                       : LOG_BUFFER_SIZE;    /* 256K */
+  }
+
+  snprintf(key_persist, sizeof(key_persist), "%s.%s", global_tunable,
+           android_log_id_to_name(logId));
+  snprintf(key_ro, sizeof(key_ro), "%s.%s", global_default, android_log_id_to_name(logId));
+  property_size = do_cache2_property_size(&local);
+
+  if (!property_size) {
+    property_size = default_size;
+  }
+
+  if (!property_size) {
+    property_size = LOG_BUFFER_SIZE;
+  }
+
+  return property_size;
+}
diff --git a/liblog/stderr_write.c b/liblog/stderr_write.c
deleted file mode 100644
index dbe5309..0000000
--- a/liblog/stderr_write.c
+++ /dev/null
@@ -1,212 +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.
- */
-
-/*
- * stderr write handler.  Output is logcat-like, and responds to
- * logcat's environment variables ANDROID_PRINTF_LOG and
- * ANDROID_LOG_TAGS to filter output.
- *
- * This transport only provides a writer, that means that it does not
- * provide an End-To-End capability as the logs are effectively _lost_
- * to the stderr file stream.  The purpose of this transport is to
- * supply a means for command line tools to report their logging
- * to the stderr stream, in line with all other activities.
- */
-
-#include <errno.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <log/event_tag_map.h>
-#include <log/log.h>
-#include <log/logprint.h>
-#include <log/uio.h>
-
-#include "log_portability.h"
-#include "logger.h"
-
-static int stderrOpen();
-static void stderrClose();
-static int stderrAvailable(log_id_t logId);
-static int stderrWrite(log_id_t logId, struct timespec* ts, struct iovec* vec,
-                       size_t nr);
-
-struct stderrContext {
-  AndroidLogFormat* logformat;
-#if defined(__ANDROID__)
-  EventTagMap* eventTagMap;
-#endif
-};
-
-LIBLOG_HIDDEN struct android_log_transport_write stderrLoggerWrite = {
-  .node = { &stderrLoggerWrite.node, &stderrLoggerWrite.node },
-  .context.priv = NULL,
-  .name = "stderr",
-  .available = stderrAvailable,
-  .open = stderrOpen,
-  .close = stderrClose,
-  .write = stderrWrite,
-};
-
-static int stderrOpen() {
-  struct stderrContext* ctx;
-  const char* envStr;
-  bool setFormat;
-
-  if (!stderr || (fileno(stderr) < 0)) {
-    return -EBADF;
-  }
-
-  if (stderrLoggerWrite.context.priv) {
-    return fileno(stderr);
-  }
-
-  ctx = calloc(1, sizeof(struct stderrContext));
-  if (!ctx) {
-    return -ENOMEM;
-  }
-
-  ctx->logformat = android_log_format_new();
-  if (!ctx->logformat) {
-    free(ctx);
-    return -ENOMEM;
-  }
-
-  envStr = getenv("ANDROID_PRINTF_LOG");
-  setFormat = false;
-
-  if (envStr) {
-    char* formats = strdup(envStr);
-    char* sv = NULL;
-    char* arg = formats;
-    while (!!(arg = strtok_r(arg, ",:; \t\n\r\f", &sv))) {
-      AndroidLogPrintFormat format = android_log_formatFromString(arg);
-      arg = NULL;
-      if (format == FORMAT_OFF) {
-        continue;
-      }
-      if (android_log_setPrintFormat(ctx->logformat, format) <= 0) {
-        continue;
-      }
-      setFormat = true;
-    }
-    free(formats);
-  }
-  if (!setFormat) {
-    AndroidLogPrintFormat format = android_log_formatFromString("threadtime");
-    android_log_setPrintFormat(ctx->logformat, format);
-  }
-  envStr = getenv("ANDROID_LOG_TAGS");
-  if (envStr) {
-    android_log_addFilterString(ctx->logformat, envStr);
-  }
-  stderrLoggerWrite.context.priv = ctx;
-
-  return fileno(stderr);
-}
-
-static void stderrClose() {
-  struct stderrContext* ctx = stderrLoggerWrite.context.priv;
-
-  if (ctx) {
-    stderrLoggerWrite.context.priv = NULL;
-    if (ctx->logformat) {
-      android_log_format_free(ctx->logformat);
-      ctx->logformat = NULL;
-    }
-#if defined(__ANDROID__)
-    if (ctx->eventTagMap) {
-      android_closeEventTagMap(ctx->eventTagMap);
-      ctx->eventTagMap = NULL;
-    }
-#endif
-  }
-}
-
-static int stderrAvailable(log_id_t logId) {
-  if ((logId >= LOG_ID_MAX) || (logId == LOG_ID_KERNEL)) {
-    return -EINVAL;
-  }
-  return 1;
-}
-
-static int stderrWrite(log_id_t logId, struct timespec* ts, struct iovec* vec,
-                       size_t nr) {
-  struct log_msg log_msg;
-  AndroidLogEntry entry;
-  char binaryMsgBuf[1024];
-  int err;
-  size_t i;
-  struct stderrContext* ctx = stderrLoggerWrite.context.priv;
-
-  if (!ctx) return -EBADF;
-  if (!vec || !nr) return -EINVAL;
-
-  log_msg.entry.len = 0;
-  log_msg.entry.hdr_size = sizeof(log_msg.entry);
-  log_msg.entry.pid = getpid();
-#ifdef __BIONIC__
-  log_msg.entry.tid = gettid();
-#else
-  log_msg.entry.tid = getpid();
-#endif
-  log_msg.entry.sec = ts->tv_sec;
-  log_msg.entry.nsec = ts->tv_nsec;
-  log_msg.entry.lid = logId;
-  log_msg.entry.uid = __android_log_uid();
-
-  for (i = 0; i < nr; ++i) {
-    size_t len = vec[i].iov_len;
-    if ((log_msg.entry.len + len) > LOGGER_ENTRY_MAX_PAYLOAD) {
-      len = LOGGER_ENTRY_MAX_PAYLOAD - log_msg.entry.len;
-    }
-    if (!len) continue;
-    memcpy(log_msg.entry.msg + log_msg.entry.len, vec[i].iov_base, len);
-    log_msg.entry.len += len;
-  }
-
-  if ((logId == LOG_ID_EVENTS) || (logId == LOG_ID_SECURITY)) {
-#if defined(__ANDROID__)
-    if (!ctx->eventTagMap) {
-      ctx->eventTagMap = android_openEventTagMap(NULL);
-    }
-#endif
-    err = android_log_processBinaryLogBuffer(&log_msg.entry_v1, &entry,
-#if defined(__ANDROID__)
-                                             ctx->eventTagMap,
-#else
-                                             NULL,
-#endif
-                                             binaryMsgBuf, sizeof(binaryMsgBuf));
-  } else {
-    err = android_log_processLogBuffer(&log_msg.entry_v1, &entry);
-  }
-
-  /* print known truncated data, in essence logcat --debug */
-  if ((err < 0) && !entry.message) return -EINVAL;
-
-  if (!android_log_shouldPrintLine(ctx->logformat, entry.tag, entry.priority)) {
-    return log_msg.entry.len;
-  }
-
-  err = android_log_printLogLine(ctx->logformat, fileno(stderr), &entry);
-  if (err < 0) return errno ? -errno : -EINVAL;
-  return log_msg.entry.len;
-}
diff --git a/liblog/stderr_write.cpp b/liblog/stderr_write.cpp
new file mode 100644
index 0000000..e324a7c
--- /dev/null
+++ b/liblog/stderr_write.cpp
@@ -0,0 +1,210 @@
+/*
+ * 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.
+ */
+
+/*
+ * stderr write handler.  Output is logcat-like, and responds to
+ * logcat's environment variables ANDROID_PRINTF_LOG and
+ * ANDROID_LOG_TAGS to filter output.
+ *
+ * This transport only provides a writer, that means that it does not
+ * provide an End-To-End capability as the logs are effectively _lost_
+ * to the stderr file stream.  The purpose of this transport is to
+ * supply a means for command line tools to report their logging
+ * to the stderr stream, in line with all other activities.
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <log/event_tag_map.h>
+#include <log/log.h>
+#include <log/logprint.h>
+
+#include "log_portability.h"
+#include "logger.h"
+#include "uio.h"
+
+static int stderrOpen();
+static void stderrClose();
+static int stderrAvailable(log_id_t logId);
+static int stderrWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
+
+struct stderrContext {
+  AndroidLogFormat* logformat;
+#if defined(__ANDROID__)
+  EventTagMap* eventTagMap;
+#endif
+};
+
+struct android_log_transport_write stderrLoggerWrite = {
+    .node = {&stderrLoggerWrite.node, &stderrLoggerWrite.node},
+    .context.priv = NULL,
+    .name = "stderr",
+    .available = stderrAvailable,
+    .open = stderrOpen,
+    .close = stderrClose,
+    .write = stderrWrite,
+};
+
+static int stderrOpen() {
+  struct stderrContext* ctx;
+  const char* envStr;
+  bool setFormat;
+
+  if (!stderr || (fileno(stderr) < 0)) {
+    return -EBADF;
+  }
+
+  if (stderrLoggerWrite.context.priv) {
+    return fileno(stderr);
+  }
+
+  ctx = static_cast<stderrContext*>(calloc(1, sizeof(stderrContext)));
+  if (!ctx) {
+    return -ENOMEM;
+  }
+
+  ctx->logformat = android_log_format_new();
+  if (!ctx->logformat) {
+    free(ctx);
+    return -ENOMEM;
+  }
+
+  envStr = getenv("ANDROID_PRINTF_LOG");
+  setFormat = false;
+
+  if (envStr) {
+    char* formats = strdup(envStr);
+    char* sv = NULL;
+    char* arg = formats;
+    while (!!(arg = strtok_r(arg, ",:; \t\n\r\f", &sv))) {
+      AndroidLogPrintFormat format = android_log_formatFromString(arg);
+      arg = NULL;
+      if (format == FORMAT_OFF) {
+        continue;
+      }
+      if (android_log_setPrintFormat(ctx->logformat, format) <= 0) {
+        continue;
+      }
+      setFormat = true;
+    }
+    free(formats);
+  }
+  if (!setFormat) {
+    AndroidLogPrintFormat format = android_log_formatFromString("threadtime");
+    android_log_setPrintFormat(ctx->logformat, format);
+  }
+  envStr = getenv("ANDROID_LOG_TAGS");
+  if (envStr) {
+    android_log_addFilterString(ctx->logformat, envStr);
+  }
+  stderrLoggerWrite.context.priv = ctx;
+
+  return fileno(stderr);
+}
+
+static void stderrClose() {
+  stderrContext* ctx = static_cast<stderrContext*>(stderrLoggerWrite.context.priv);
+
+  if (ctx) {
+    stderrLoggerWrite.context.priv = NULL;
+    if (ctx->logformat) {
+      android_log_format_free(ctx->logformat);
+      ctx->logformat = NULL;
+    }
+#if defined(__ANDROID__)
+    if (ctx->eventTagMap) {
+      android_closeEventTagMap(ctx->eventTagMap);
+      ctx->eventTagMap = NULL;
+    }
+#endif
+  }
+}
+
+static int stderrAvailable(log_id_t logId) {
+  if ((logId >= LOG_ID_MAX) || (logId == LOG_ID_KERNEL)) {
+    return -EINVAL;
+  }
+  return 1;
+}
+
+static int stderrWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
+  struct log_msg log_msg;
+  AndroidLogEntry entry;
+  char binaryMsgBuf[1024];
+  int err;
+  size_t i;
+  stderrContext* ctx = static_cast<stderrContext*>(stderrLoggerWrite.context.priv);
+
+  if (!ctx) return -EBADF;
+  if (!vec || !nr) return -EINVAL;
+
+  log_msg.entry.len = 0;
+  log_msg.entry.hdr_size = sizeof(log_msg.entry);
+  log_msg.entry.pid = getpid();
+#ifdef __BIONIC__
+  log_msg.entry.tid = gettid();
+#else
+  log_msg.entry.tid = getpid();
+#endif
+  log_msg.entry.sec = ts->tv_sec;
+  log_msg.entry.nsec = ts->tv_nsec;
+  log_msg.entry.lid = logId;
+  log_msg.entry.uid = __android_log_uid();
+
+  for (i = 0; i < nr; ++i) {
+    size_t len = vec[i].iov_len;
+    if ((log_msg.entry.len + len) > LOGGER_ENTRY_MAX_PAYLOAD) {
+      len = LOGGER_ENTRY_MAX_PAYLOAD - log_msg.entry.len;
+    }
+    if (!len) continue;
+    memcpy(log_msg.entry.msg + log_msg.entry.len, vec[i].iov_base, len);
+    log_msg.entry.len += len;
+  }
+
+  if ((logId == LOG_ID_EVENTS) || (logId == LOG_ID_SECURITY)) {
+#if defined(__ANDROID__)
+    if (!ctx->eventTagMap) {
+      ctx->eventTagMap = android_openEventTagMap(NULL);
+    }
+#endif
+    err = android_log_processBinaryLogBuffer(&log_msg.entry_v1, &entry,
+#if defined(__ANDROID__)
+                                             ctx->eventTagMap,
+#else
+                                             NULL,
+#endif
+                                             binaryMsgBuf, sizeof(binaryMsgBuf));
+  } else {
+    err = android_log_processLogBuffer(&log_msg.entry_v1, &entry);
+  }
+
+  /* print known truncated data, in essence logcat --debug */
+  if ((err < 0) && !entry.message) return -EINVAL;
+
+  if (!android_log_shouldPrintLine(ctx->logformat, entry.tag, entry.priority)) {
+    return log_msg.entry.len;
+  }
+
+  err = android_log_printLogLine(ctx->logformat, fileno(stderr), &entry);
+  if (err < 0) return errno ? -errno : -EINVAL;
+  return log_msg.entry.len;
+}
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
new file mode 100644
index 0000000..d9d1a21
--- /dev/null
+++ b/liblog/tests/Android.bp
@@ -0,0 +1,98 @@
+//
+// Copyright (C) 2013-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.
+//
+
+// -----------------------------------------------------------------------------
+// Benchmarks.
+// -----------------------------------------------------------------------------
+
+// Build benchmarks for the device. Run with:
+//   adb shell liblog-benchmarks
+cc_benchmark {
+    name: "liblog-benchmarks",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-fno-builtin",
+    ],
+    shared_libs: [
+        "libm",
+        "libbase",
+        "libcutils",
+    ],
+    static_libs: ["liblog"],
+    srcs: ["liblog_benchmark.cpp"],
+}
+
+// -----------------------------------------------------------------------------
+// Unit tests.
+// -----------------------------------------------------------------------------
+
+cc_defaults {
+    name: "liblog-tests-defaults",
+
+    cflags: [
+        "-fstack-protector-all",
+        "-g",
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-fno-builtin",
+    ],
+    srcs: [
+        "libc_test.cpp",
+        "liblog_test_default.cpp",
+        "liblog_test_stderr.cpp",
+        "log_id_test.cpp",
+        "log_radio_test.cpp",
+        "log_read_test.cpp",
+        "log_system_test.cpp",
+        "log_time_test.cpp",
+        "log_wrap_test.cpp",
+        "logprint_test.cpp",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libbase",
+    ],
+    static_libs: ["liblog"],
+}
+
+// Build tests for the device (with .so). Run with:
+//   adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests
+cc_test {
+    name: "liblog-unit-tests",
+    defaults: ["liblog-tests-defaults"],
+}
+
+cc_test {
+    name: "CtsLiblogTestCases",
+    defaults: ["liblog-tests-defaults"],
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+
+    cflags: ["-DNO_PSTORE"],
+    test_suites: [
+        "cts",
+        "vts",
+    ],
+}
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
deleted file mode 100644
index cfa849b..0000000
--- a/liblog/tests/Android.mk
+++ /dev/null
@@ -1,116 +0,0 @@
-#
-# Copyright (C) 2013-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)
-
-# -----------------------------------------------------------------------------
-# Benchmarks.
-# -----------------------------------------------------------------------------
-
-test_module_prefix := liblog-
-test_tags := tests
-
-benchmark_c_flags := \
-    -Wall \
-    -Wextra \
-    -Werror \
-    -fno-builtin \
-
-benchmark_src_files := \
-    liblog_benchmark.cpp
-
-# Build benchmarks for the device. Run with:
-#   adb shell liblog-benchmarks
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)benchmarks
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(benchmark_c_flags)
-LOCAL_SHARED_LIBRARIES += liblog libm libbase
-LOCAL_SRC_FILES := $(benchmark_src_files)
-include $(BUILD_NATIVE_BENCHMARK)
-
-# -----------------------------------------------------------------------------
-# Unit tests.
-# -----------------------------------------------------------------------------
-
-test_c_flags := \
-    -fstack-protector-all \
-    -g \
-    -Wall -Wextra \
-    -Werror \
-    -fno-builtin \
-
-cts_src_files := \
-    libc_test.cpp \
-    liblog_test_default.cpp \
-    liblog_test_local.cpp \
-    liblog_test_stderr.cpp \
-    liblog_test_stderr_local.cpp \
-    log_id_test.cpp \
-    log_radio_test.cpp \
-    log_read_test.cpp \
-    log_system_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
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)unit-tests
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog libcutils libbase
-LOCAL_SRC_FILES := $(test_src_files)
-include $(BUILD_NATIVE_TEST)
-
-cts_executable := CtsLiblogTestCases
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(cts_executable)
-LOCAL_MODULE_TAGS := tests
-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 vts
-LOCAL_CTS_TEST_PACKAGE := android.core.liblog
-include $(BUILD_CTS_EXECUTABLE)
-
-ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(cts_executable)_list
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := $(test_c_flags) -DHOST
-LOCAL_C_INCLUDES := external/gtest/include
-LOCAL_SRC_FILES := $(test_src_files)
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_CXX_STL := libc++
-LOCAL_SHARED_LIBRARIES := liblog libcutils libbase
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_LDLIBS_linux := -lrt
-include $(BUILD_HOST_NATIVE_TEST)
-
-endif  # ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
diff --git a/liblog/tests/AndroidTest.xml b/liblog/tests/AndroidTest.xml
index 7b64433..c167478 100644
--- a/liblog/tests/AndroidTest.xml
+++ b/liblog/tests/AndroidTest.xml
@@ -16,6 +16,8 @@
 <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" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <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/libc_test.cpp b/liblog/tests/libc_test.cpp
index 329ba85..3534eb8 100644
--- a/liblog/tests/libc_test.cpp
+++ b/liblog/tests/libc_test.cpp
@@ -21,8 +21,9 @@
 
 TEST(libc, __pstore_append) {
 #ifdef __ANDROID__
+#ifndef NO_PSTORE
   FILE* fp;
-  ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "a")));
+  ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "ae")));
   static const char message[] = "libc.__pstore_append\n";
   ASSERT_EQ((size_t)1, fwrite(message, sizeof(message), 1, fp));
   int fflushReturn = fflush(fp);
@@ -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 c2f3f83..21d12a1 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -21,6 +21,7 @@
 #include <sys/socket.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
+#include <sys/uio.h>
 #include <unistd.h>
 
 #include <unordered_set>
@@ -55,8 +56,8 @@
  */
 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()));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_INFO, "BM_log_maximum_retry", "%" PRIu64,
+                                          state.iterations()));
   }
 }
 BENCHMARK(BM_log_maximum_retry);
@@ -68,8 +69,7 @@
  */
 static void BM_log_maximum(benchmark::State& state) {
   while (state.KeepRunning()) {
-    __android_log_print(ANDROID_LOG_INFO, "BM_log_maximum", "%zu",
-                        state.iterations());
+    __android_log_print(ANDROID_LOG_INFO, "BM_log_maximum", "%" PRIu64, state.iterations());
   }
 }
 BENCHMARK(BM_log_maximum);
@@ -171,7 +171,7 @@
  * Measure the time it takes to submit the android logging data to pstore
  */
 static void BM_pmsg_short(benchmark::State& state) {
-  int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+  int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
   if (pstore_fd < 0) {
     state.SkipWithError("/dev/pmsg0");
     return;
@@ -247,7 +247,7 @@
  * best case aligned single block.
  */
 static void BM_pmsg_short_aligned(benchmark::State& state) {
-  int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+  int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
   if (pstore_fd < 0) {
     state.SkipWithError("/dev/pmsg0");
     return;
@@ -285,7 +285,7 @@
   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 iterations=%zu\n", &buffer->pmsg_header,
+    fprintf(stderr, "&buffer=0x%p iterations=%" PRIu64 "\n", &buffer->pmsg_header,
             state.iterations());
   }
 
@@ -322,7 +322,7 @@
  * best case aligned single block.
  */
 static void BM_pmsg_short_unaligned1(benchmark::State& state) {
-  int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+  int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
   if (pstore_fd < 0) {
     state.SkipWithError("/dev/pmsg0");
     return;
@@ -360,7 +360,7 @@
   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 iterations=%zu\n", &buffer->pmsg_header,
+    fprintf(stderr, "&buffer=0x%p iterations=%" PRIu64 "\n", &buffer->pmsg_header,
             state.iterations());
   }
 
@@ -397,7 +397,7 @@
  * best case aligned single block.
  */
 static void BM_pmsg_long_aligned(benchmark::State& state) {
-  int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+  int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
   if (pstore_fd < 0) {
     state.SkipWithError("/dev/pmsg0");
     return;
@@ -435,7 +435,7 @@
   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 iterations=%zu\n", &buffer->pmsg_header,
+    fprintf(stderr, "&buffer=0x%p iterations=%" PRIu64 "\n", &buffer->pmsg_header,
             state.iterations());
   }
 
@@ -470,7 +470,7 @@
  * best case aligned single block.
  */
 static void BM_pmsg_long_unaligned1(benchmark::State& state) {
-  int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+  int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
   if (pstore_fd < 0) {
     state.SkipWithError("/dev/pmsg0");
     return;
@@ -508,7 +508,7 @@
   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 iterations=%zu\n", &buffer->pmsg_header,
+    fprintf(stderr, "&buffer=0x%p iterations=%" PRIu64 "\n", &buffer->pmsg_header,
             state.iterations());
   }
 
@@ -559,7 +559,7 @@
 /* performance test */
 static void BM_sprintf_overhead(benchmark::State& state) {
   while (state.KeepRunning()) {
-    test_print("BM_sprintf_overhead:%zu", state.iterations());
+    test_print("BM_sprintf_overhead:%" PRIu64, state.iterations());
     state.PauseTiming();
     logd_yield();
     state.ResumeTiming();
@@ -574,8 +574,7 @@
  */
 static void BM_log_print_overhead(benchmark::State& state) {
   while (state.KeepRunning()) {
-    __android_log_print(ANDROID_LOG_INFO, "BM_log_overhead", "%zu",
-                        state.iterations());
+    __android_log_print(ANDROID_LOG_INFO, "BM_log_overhead", "%" PRIu64, state.iterations());
     state.PauseTiming();
     logd_yield();
     state.ResumeTiming();
@@ -948,8 +947,8 @@
 
 // Must be functionally identical to liblog internal __send_log_msg.
 static void send_to_control(char* buf, size_t len) {
-  int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
-                                 SOCK_STREAM);
+  int sock =
+      socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM | SOCK_CLOEXEC);
   if (sock < 0) return;
   size_t writeLen = strlen(buf) + 1;
 
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 597a6bb..1f87b3e 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -30,6 +30,7 @@
 #include <string>
 
 #include <android-base/file.h>
+#include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #ifdef __ANDROID__  // includes sys/properties.h which does not exist outside
 #include <cutils/properties.h>
@@ -51,22 +52,6 @@
 #endif
 #endif
 
-#if (!defined(USING_LOGGER_DEFAULT) || !defined(USING_LOGGER_LOCAL) || \
-     !defined(USING_LOGGER_STDERR))
-#ifdef liblog  // a binary clue that we are overriding the test names
-// Does not support log reading blocking feature yet
-// Does not support LOG_ID_SECURITY (unless we set LOGGER_LOCAL | LOGGER_LOGD)
-// Assume some common aspects are tested by USING_LOGGER_DEFAULT:
-// Does not need to _retest_ pmsg functionality
-// Does not need to _retest_ property handling as it is a higher function
-// Does not need to _retest_ event mapping functionality
-// Does not need to _retest_ ratelimit
-// Does not need to _retest_ logprint
-#define USING_LOGGER_LOCAL
-#else
-#define USING_LOGGER_DEFAULT
-#endif
-#endif
 #ifdef USING_LOGGER_STDERR
 #define SUPPORTS_END_TO_END 0
 #else
@@ -108,7 +93,7 @@
 static std::string popenToString(const std::string& command) {
   std::string ret;
 
-  FILE* fp = popen(command.c_str(), "r");
+  FILE* fp = popen(command.c_str(), "re");
   if (fp) {
     if (!android::base::ReadFdToString(fileno(fp), &ret)) ret = "";
     pclose(fp);
@@ -130,7 +115,7 @@
 
 static bool isLogdwActive() {
   std::string logdwSignature =
-      popenToString("grep /dev/socket/logdw /proc/net/unix");
+      popenToString("grep -a /dev/socket/logdw /proc/net/unix");
   size_t beginning = logdwSignature.find(' ');
   if (beginning == std::string::npos) return true;
   beginning = logdwSignature.find(' ', beginning + 1);
@@ -144,7 +129,7 @@
   end = logdwSignature.find(' ', end + 1);
   if (end == std::string::npos) return true;
   std::string allLogdwEndpoints = popenToString(
-      "grep ' 00000002" + logdwSignature.substr(beginning, end - beginning) +
+      "grep -a ' 00000002" + logdwSignature.substr(beginning, end - beginning) +
       " ' /proc/net/unix | " +
       "sed -n 's/.* \\([0-9][0-9]*\\)$/ -> socket:[\\1]/p'");
   if (allLogdwEndpoints.length() == 0) return true;
@@ -174,7 +159,7 @@
 #endif
 
 TEST(liblog, __android_log_btwrite__android_logger_list_read) {
-#if (defined(__ANDROID__) || defined(USING_LOGGER_LOCAL))
+#ifdef __ANDROID__
 #ifdef TEST_PREFIX
   TEST_PREFIX
 #endif
@@ -268,7 +253,7 @@
 #endif
 }
 
-#if (defined(__ANDROID__) || defined(USING_LOGGER_LOCAL))
+#ifdef __ANDROID__
 static void print_transport(const char* prefix, int logger) {
   static const char orstr[] = " | ";
 
@@ -296,16 +281,11 @@
     fprintf(stderr, "%sLOGGER_NULL", prefix);
     prefix = orstr;
   }
-  if (logger & LOGGER_LOCAL) {
-    fprintf(stderr, "%sLOGGER_LOCAL", prefix);
-    prefix = orstr;
-  }
   if (logger & LOGGER_STDERR) {
     fprintf(stderr, "%sLOGGER_STDERR", prefix);
     prefix = orstr;
   }
-  logger &= ~(LOGGER_LOGD | LOGGER_KERNEL | LOGGER_NULL | LOGGER_LOCAL |
-              LOGGER_STDERR);
+  logger &= ~(LOGGER_LOGD | LOGGER_KERNEL | LOGGER_NULL | LOGGER_STDERR);
   if (logger) {
     fprintf(stderr, "%s0x%x", prefix, logger);
     prefix = orstr;
@@ -320,7 +300,7 @@
 // and behind us, to make us whole.  We could incorporate a prefix and
 // suffix test to make this standalone, but opted to not complicate this.
 TEST(liblog, android_set_log_transport) {
-#if (defined(__ANDROID__) || defined(USING_LOGGER_LOCAL))
+#ifdef __ANDROID__
 #ifdef TEST_PREFIX
   TEST_PREFIX
 #endif
@@ -631,7 +611,7 @@
   buf_write_test("\n Hello World \n");
 }
 
-#ifndef USING_LOGGER_LOCAL  // requires blocking reader functionality
+#ifdef USING_LOGGER_DEFAULT  // requires blocking reader functionality
 #ifdef TEST_PREFIX
 static unsigned signaled;
 static log_time signal_time;
@@ -665,7 +645,7 @@
   char buffer[512];
   snprintf(buffer, sizeof(buffer), "/proc/%u/stat", pid);
 
-  FILE* fp = fopen(buffer, "r");
+  FILE* fp = fopen(buffer, "re");
   if (!fp) {
     return;
   }
@@ -943,7 +923,7 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
-#endif  // !USING_LOGGER_LOCAL
+#endif  // USING_LOGGER_DEFAULT
 
 #ifdef TEST_PREFIX
 static const char max_payload_tag[] = "TEST_max_payload_and_longish_tag_XXXX";
@@ -2416,7 +2396,7 @@
 }
 
 // Do not retest logger list handling
-#if (defined(TEST_PREFIX) || !defined(USING_LOGGER_LOCAL))
+#ifdef TEST_PREFIX
 static int is_real_element(int type) {
   return ((type == EVENT_TYPE_INT) || (type == EVENT_TYPE_LONG) ||
           (type == EVENT_TYPE_STRING) || (type == EVENT_TYPE_FLOAT));
@@ -2516,7 +2496,7 @@
 #endif
         elem.data.string = const_cast<char*>("<unknown>");
         elem.len = strlen(elem.data.string);
-      /* FALLTHRU */
+        FALLTHROUGH_INTENDED;
       case EVENT_TYPE_STRING:
         if (elem.len <= strOutLen) {
           memcpy(strOut, elem.data.string, elem.len);
@@ -2571,7 +2551,7 @@
 
   return 0;
 }
-#endif  // TEST_PREFIX || !USING_LOGGER_LOCAL
+#endif  // TEST_PREFIX
 
 #ifdef TEST_PREFIX
 static const char* event_test_int32(uint32_t tag, size_t& expected_len) {
@@ -3181,148 +3161,6 @@
 #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
-  GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-#endif  // USING_LOGGER_DEFAULT
-
-#ifdef USING_LOGGER_DEFAULT  // Do not retest ratelimit
-TEST(liblog, __android_log_ratelimit) {
-  time_t state = 0;
-
-  errno = 42;
-  // Prime
-  __android_log_ratelimit(3, &state);
-  EXPECT_EQ(errno, 42);
-  // Check
-  EXPECT_FALSE(__android_log_ratelimit(3, &state));
-  sleep(1);
-  EXPECT_FALSE(__android_log_ratelimit(3, &state));
-  sleep(4);
-  EXPECT_TRUE(__android_log_ratelimit(3, &state));
-  sleep(5);
-  EXPECT_TRUE(__android_log_ratelimit(3, &state));
-
-  // API checks
-  IF_ALOG_RATELIMIT_LOCAL(3, &state) {
-    EXPECT_FALSE(0 != "IF_ALOG_RATELIMIT_LOCAL(3, &state)");
-  }
-
-  IF_ALOG_RATELIMIT() {
-    ;
-  }
-  else {
-    EXPECT_TRUE(0 == "IF_ALOG_RATELIMIT()");
-  }
-  IF_ALOG_RATELIMIT() {
-    EXPECT_FALSE(0 != "IF_ALOG_RATELIMIT()");
-  }
-  // Do not test default seconds, to allow liblog to tune freely
-}
-#endif  // USING_LOGGER_DEFAULT
-
-#ifdef USING_LOGGER_DEFAULT  // Do not retest event mapping functionality
 TEST(liblog, android_lookupEventTagNum) {
 #ifdef __ANDROID__
   EventTagMap* map = android_openEventTagMap(NULL);
diff --git a/liblog/tests/liblog_test_local.cpp b/liblog/tests/liblog_test_local.cpp
deleted file mode 100644
index 451beca..0000000
--- a/liblog/tests/liblog_test_local.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-#include <log/log_transport.h>
-#define liblog liblog_local
-#define TEST_LOGGER LOGGER_LOCAL
-#include "liblog_test.cpp"
diff --git a/liblog/tests/liblog_test_stderr_local.cpp b/liblog/tests/liblog_test_stderr_local.cpp
deleted file mode 100644
index bb5c7ae..0000000
--- a/liblog/tests/liblog_test_stderr_local.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-#include <log/log_transport.h>
-#define liblog liblog_stderr_local
-#define TEST_LOGGER (LOGGER_LOCAL | LOGGER_STDERR)
-#include "liblog_test.cpp"
diff --git a/liblog/tests/log_radio_test.cpp b/liblog/tests/log_radio_test.cpp
index f202c67..fa1255e 100644
--- a/liblog/tests/log_radio_test.cpp
+++ b/liblog/tests/log_radio_test.cpp
@@ -91,7 +91,7 @@
       "logcat -b radio --pid=%u -d -s"
       " TEST__RLOGV TEST__RLOGD TEST__RLOGI TEST__RLOGW TEST__RLOGE",
       (unsigned)getpid());
-  FILE* fp = popen(buf.c_str(), "r");
+  FILE* fp = popen(buf.c_str(), "re");
   int count = 0;
   int count_false = 0;
   if (fp) {
diff --git a/liblog/tests/log_system_test.cpp b/liblog/tests/log_system_test.cpp
index 0656c0b..13f026d 100644
--- a/liblog/tests/log_system_test.cpp
+++ b/liblog/tests/log_system_test.cpp
@@ -91,7 +91,7 @@
       "logcat -b system --pid=%u -d -s"
       " TEST__SLOGV TEST__SLOGD TEST__SLOGI TEST__SLOGW TEST__SLOGE",
       (unsigned)getpid());
-  FILE* fp = popen(buf.c_str(), "r");
+  FILE* fp = popen(buf.c_str(), "re");
   int count = 0;
   int count_false = 0;
   if (fp) {
diff --git a/liblog/tests/log_time_test.cpp b/liblog/tests/log_time_test.cpp
index 0ae1d18..47fe594 100644
--- a/liblog/tests/log_time_test.cpp
+++ b/liblog/tests/log_time_test.cpp
@@ -21,12 +21,6 @@
 #include <log/log_time.h>
 
 TEST(liblog, log_time) {
-#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
-  log_time(CLOCK_MONOTONIC);
-
-  EXPECT_EQ(log_time, log_time::EPOCH);
-#endif
-
   struct timespec ts;
   clock_gettime(CLOCK_MONOTONIC, &ts);
   log_time tl(ts);
diff --git a/liblog/tests/logprint_test.cpp b/liblog/tests/logprint_test.cpp
new file mode 100644
index 0000000..7ca02ac
--- /dev/null
+++ b/liblog/tests/logprint_test.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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>
+
+size_t convertPrintable(char* p, const char* message, size_t messageLen);
+
+TEST(liblog, convertPrintable_ascii) {
+  auto input = "easy string, output same";
+  auto output_size = convertPrintable(nullptr, input, strlen(input));
+  EXPECT_EQ(output_size, strlen(input));
+
+  char output[output_size];
+
+  output_size = convertPrintable(output, input, strlen(input));
+  EXPECT_EQ(output_size, strlen(input));
+  EXPECT_STREQ(input, output);
+}
+
+TEST(liblog, convertPrintable_escapes) {
+  // Note that \t is not escaped.
+  auto input = "escape\a\b\t\v\f\r\\";
+  auto expected_output = "escape\\a\\b\t\\v\\f\\r\\\\";
+  auto output_size = convertPrintable(nullptr, input, strlen(input));
+  EXPECT_EQ(output_size, strlen(expected_output));
+
+  char output[output_size];
+
+  output_size = convertPrintable(output, input, strlen(input));
+  EXPECT_EQ(output_size, strlen(expected_output));
+  EXPECT_STREQ(expected_output, output);
+}
+
+TEST(liblog, convertPrintable_validutf8) {
+  auto input = u8"¢ह€𐍈";
+  auto output_size = convertPrintable(nullptr, input, strlen(input));
+  EXPECT_EQ(output_size, strlen(input));
+
+  char output[output_size];
+
+  output_size = convertPrintable(output, input, strlen(input));
+  EXPECT_EQ(output_size, strlen(input));
+  EXPECT_STREQ(input, output);
+}
+
+TEST(liblog, convertPrintable_invalidutf8) {
+  auto input = "\x80\xC2\x01\xE0\xA4\x06\xE0\x06\xF0\x90\x8D\x06\xF0\x90\x06\xF0\x0E";
+  auto expected_output =
+      "\\x80\\xC2\\x01\\xE0\\xA4\\x06\\xE0\\x06\\xF0\\x90\\x8D\\x06\\xF0\\x90\\x06\\xF0\\x0E";
+  auto output_size = convertPrintable(nullptr, input, strlen(input));
+  EXPECT_EQ(output_size, strlen(expected_output));
+
+  char output[output_size];
+
+  output_size = convertPrintable(output, input, strlen(input));
+  EXPECT_EQ(output_size, strlen(expected_output));
+  EXPECT_STREQ(expected_output, output);
+}
+
+TEST(liblog, convertPrintable_mixed) {
+  auto input =
+      u8"\x80\xC2¢ह€𐍈\x01\xE0\xA4\x06¢ह€𐍈\xE0\x06\a\b\xF0\x90¢ह€𐍈\x8D\x06\xF0\t\t\x90\x06\xF0\x0E";
+  auto expected_output =
+      u8"\\x80\\xC2¢ह€𐍈\\x01\\xE0\\xA4\\x06¢ह€𐍈\\xE0\\x06\\a\\b\\xF0\\x90¢ह€𐍈\\x8D\\x06\\xF0\t\t"
+      u8"\\x90\\x06\\xF0\\x0E";
+  auto output_size = convertPrintable(nullptr, input, strlen(input));
+  EXPECT_EQ(output_size, strlen(expected_output));
+
+  char output[output_size];
+
+  output_size = convertPrintable(output, input, strlen(input));
+  EXPECT_EQ(output_size, strlen(expected_output));
+  EXPECT_STREQ(expected_output, output);
+}
diff --git a/liblog/uio.c b/liblog/uio.c
deleted file mode 100644
index e127202..0000000
--- a/liblog/uio.c
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2007-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.
- */
-
-#if defined(_WIN32)
-
-#include <unistd.h>
-
-#include <log/uio.h>
-
-#include "log_portability.h"
-
-LIBLOG_ABI_PUBLIC int readv(int fd, struct iovec* vecs, int count) {
-  int total = 0;
-
-  for (; count > 0; count--, vecs++) {
-    char* buf = vecs->iov_base;
-    int len = vecs->iov_len;
-
-    while (len > 0) {
-      int ret = read(fd, buf, len);
-      if (ret < 0) {
-        if (total == 0) total = -1;
-        goto Exit;
-      }
-      if (ret == 0) goto Exit;
-
-      total += ret;
-      buf += ret;
-      len -= ret;
-    }
-  }
-Exit:
-  return total;
-}
-
-LIBLOG_ABI_PUBLIC int writev(int fd, const struct iovec* vecs, int count) {
-  int total = 0;
-
-  for (; count > 0; count--, vecs++) {
-    const char* buf = vecs->iov_base;
-    int len = vecs->iov_len;
-
-    while (len > 0) {
-      int ret = write(fd, buf, len);
-      if (ret < 0) {
-        if (total == 0) total = -1;
-        goto Exit;
-      }
-      if (ret == 0) goto Exit;
-
-      total += ret;
-      buf += ret;
-      len -= ret;
-    }
-  }
-Exit:
-  return total;
-}
-
-#endif
diff --git a/liblog/uio.h b/liblog/uio.h
new file mode 100644
index 0000000..c85893c
--- /dev/null
+++ b/liblog/uio.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
+
+#if defined(_WIN32)
+#include <stddef.h>
+struct iovec {
+  void* iov_base;
+  size_t iov_len;
+};
+#else
+#include <sys/uio.h>
+#endif
diff --git a/libmeminfo/.clang-format b/libmeminfo/.clang-format
new file mode 120000
index 0000000..1af4f51
--- /dev/null
+++ b/libmeminfo/.clang-format
@@ -0,0 +1 @@
+../.clang-format-4
\ No newline at end of file
diff --git a/libmeminfo/Android.bp b/libmeminfo/Android.bp
new file mode 100644
index 0000000..fc022bd
--- /dev/null
+++ b/libmeminfo/Android.bp
@@ -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.
+//
+
+cc_defaults {
+    name: "libmeminfo_defaults",
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libprocinfo",
+    ],
+}
+
+cc_library {
+    name: "libmeminfo",
+    defaults: ["libmeminfo_defaults"],
+    export_include_dirs: ["include"],
+    export_shared_lib_headers: ["libbase"],
+    srcs: [
+        "pageacct.cpp",
+        "procmeminfo.cpp",
+        "sysmeminfo.cpp",
+    ],
+}
+
+cc_test {
+    name: "libmeminfo_test",
+    defaults: ["libmeminfo_defaults"],
+
+    static_libs: [
+        "libmeminfo",
+        "libbase",
+        "liblog",
+    ],
+
+    srcs: [
+        "libmeminfo_test.cpp"
+    ],
+
+    data: [
+        "testdata1/*",
+        "testdata2/*"
+    ],
+}
+
+cc_benchmark {
+    name: "libmeminfo_benchmark",
+    srcs: [
+        "libmeminfo_benchmark.cpp",
+    ],
+    static_libs : [
+        "libbase",
+        "liblog",
+        "libmeminfo",
+        "libprocinfo",
+    ],
+
+    data: [
+        "testdata1/*",
+    ],
+}
diff --git a/libmeminfo/OWNERS b/libmeminfo/OWNERS
new file mode 100644
index 0000000..26e71fe
--- /dev/null
+++ b/libmeminfo/OWNERS
@@ -0,0 +1 @@
+sspatil@google.com
diff --git a/libmeminfo/include/meminfo/meminfo.h b/libmeminfo/include/meminfo/meminfo.h
new file mode 100644
index 0000000..2fc7867
--- /dev/null
+++ b/libmeminfo/include/meminfo/meminfo.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace meminfo {
+
+struct MemUsage {
+    uint64_t vss;
+    uint64_t rss;
+    uint64_t pss;
+    uint64_t uss;
+
+    uint64_t swap;
+    uint64_t swap_pss;
+
+    uint64_t private_clean;
+    uint64_t private_dirty;
+    uint64_t shared_clean;
+    uint64_t shared_dirty;
+
+    MemUsage()
+        : vss(0),
+          rss(0),
+          pss(0),
+          uss(0),
+          swap(0),
+          swap_pss(0),
+          private_clean(0),
+          private_dirty(0),
+          shared_clean(0),
+          shared_dirty(0) {}
+
+    ~MemUsage() = default;
+
+    void clear() {
+        vss = rss = pss = uss = swap = swap_pss = 0;
+        private_clean = private_dirty = shared_clean = shared_dirty = 0;
+    }
+};
+
+struct Vma {
+    uint64_t start;
+    uint64_t end;
+    uint64_t offset;
+    uint16_t flags;
+    std::string name;
+
+    Vma() : start(0), end(0), offset(0), flags(0), name("") {}
+    Vma(uint64_t s, uint64_t e, uint64_t off, uint16_t f, const char* n)
+        : start(s), end(e), offset(off), flags(f), name(n) {}
+    ~Vma() = default;
+
+    void clear() { memset(&usage, 0, sizeof(usage)); }
+
+    // Memory usage of this mapping.
+    MemUsage usage;
+};
+
+}  // namespace meminfo
+}  // namespace android
diff --git a/libmeminfo/include/meminfo/pageacct.h b/libmeminfo/include/meminfo/pageacct.h
new file mode 100644
index 0000000..8483d84
--- /dev/null
+++ b/libmeminfo/include/meminfo/pageacct.h
@@ -0,0 +1,81 @@
+/*
+ * 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>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace meminfo {
+
+class PageAcct final {
+    // Class for per-page accounting by using kernel provided interfaces like
+    // kpagecount, kpageflags etc.
+  public:
+    static bool KernelHasPageIdle() {
+        return (access("/sys/kernel/mm/page_idle/bitmap", R_OK | W_OK) == 0);
+    }
+
+    bool InitPageAcct(bool pageidle_enable = false);
+    bool PageFlags(uint64_t pfn, uint64_t* flags);
+    bool PageMapCount(uint64_t pfn, uint64_t* mapcount);
+
+    int IsPageIdle(uint64_t pfn);
+
+    // The only way to create PageAcct object
+    static PageAcct& Instance() {
+        static PageAcct instance;
+        return instance;
+    }
+
+    ~PageAcct() = default;
+
+  private:
+    PageAcct() : kpagecount_fd_(-1), kpageflags_fd_(-1), pageidle_fd_(-1) {}
+    int MarkPageIdle(uint64_t pfn) const;
+    int GetPageIdle(uint64_t pfn) const;
+
+    // Non-copyable & Non-movable
+    PageAcct(const PageAcct&) = delete;
+    PageAcct& operator=(const PageAcct&) = delete;
+    PageAcct& operator=(PageAcct&&) = delete;
+    PageAcct(PageAcct&&) = delete;
+
+    ::android::base::unique_fd kpagecount_fd_;
+    ::android::base::unique_fd kpageflags_fd_;
+    ::android::base::unique_fd pageidle_fd_;
+};
+
+// Returns if the page present bit is set in the value
+// passed in.
+bool page_present(uint64_t pagemap_val);
+
+// Returns if the page swapped bit is set in the value
+// passed in.
+bool page_swapped(uint64_t pagemap_val);
+
+// Returns the page frame number (physical page) from
+// pagemap value
+uint64_t page_pfn(uint64_t pagemap_val);
+
+}  // namespace meminfo
+}  // namespace android
diff --git a/libmeminfo/include/meminfo/procmeminfo.h b/libmeminfo/include/meminfo/procmeminfo.h
new file mode 100644
index 0000000..1fb4151
--- /dev/null
+++ b/libmeminfo/include/meminfo/procmeminfo.h
@@ -0,0 +1,126 @@
+/*
+ * 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>
+
+#include <string>
+#include <vector>
+
+#include "meminfo.h"
+
+namespace android {
+namespace meminfo {
+
+using VmaCallback = std::function<void(const Vma&)>;
+
+class ProcMemInfo final {
+    // Per-process memory accounting
+  public:
+    // Reset the working set accounting of the process via /proc/<pid>/clear_refs
+    static bool ResetWorkingSet(pid_t pid);
+
+    ProcMemInfo(pid_t pid, bool get_wss = false, uint64_t pgflags = 0, uint64_t pgflags_mask = 0);
+
+    const std::vector<Vma>& Maps();
+    const MemUsage& Usage();
+    const MemUsage& Wss();
+
+    // Same as Maps() except, only valid for reading working set using CONFIG_IDLE_PAGE_TRACKING
+    // support in kernel. If the kernel support doesn't exist, the function will return an empty
+    // vector.
+    const std::vector<Vma>& MapsWithPageIdle();
+
+    // Collect all 'vma' or 'maps' from /proc/<pid>/smaps and store them in 'maps_'. Returns a
+    // constant reference to the vma vector after the collection is done.
+    //
+    // Each 'struct Vma' is *fully* populated by this method (unlike SmapsOrRollup).
+    const std::vector<Vma>& Smaps(const std::string& path = "");
+
+    // This method reads /proc/<pid>/smaps and calls the callback() for each
+    // vma or map that it finds. The map is converted to 'struct Vma' object which is then
+    // passed to the callback.
+    // Returns 'false' if the file is malformed.
+    bool ForEachVma(const VmaCallback& callback);
+
+    // Used to parse either of /proc/<pid>/{smaps, smaps_rollup} and record the process's
+    // Pss and Private memory usage in 'stats'.  In particular, the method only populates the fields
+    // of the MemUsage structure that are intended to be used by Android's periodic Pss collection.
+    //
+    // The method populates the following statistics in order to be fast an efficient.
+    //   Pss
+    //   Rss
+    //   Uss
+    //   private_clean
+    //   private_dirty
+    //   SwapPss
+    // All other fields of MemUsage are zeroed.
+    bool SmapsOrRollup(MemUsage* stats) const;
+
+    // Used to parse either of /proc/<pid>/{smaps, smaps_rollup} and record the process's
+    // Pss.
+    // Returns 'true' on success and the value of Pss in the out parameter.
+    bool SmapsOrRollupPss(uint64_t* pss) const;
+
+    const std::vector<uint16_t>& SwapOffsets();
+
+    // Reads /proc/<pid>/pagemap for this process for each page within
+    // the 'vma' and stores that in 'pagemap'. It is assumed that the 'vma'
+    // is obtained by calling Maps() or 'ForEachVma' for the same object. No special checks
+    // are made to see if 'vma' is *valid*.
+    // Returns false if anything goes wrong, 'true' otherwise.
+    bool PageMap(const Vma& vma, std::vector<uint64_t>* pagemap);
+
+    ~ProcMemInfo() = default;
+
+  private:
+    bool ReadMaps(bool get_wss, bool use_pageidle = false);
+    bool ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss, bool use_pageidle);
+
+    pid_t pid_;
+    bool get_wss_;
+    uint64_t pgflags_;
+    uint64_t pgflags_mask_;
+
+    std::vector<Vma> maps_;
+
+    MemUsage usage_;
+    std::vector<uint16_t> swap_offsets_;
+};
+
+// Makes callback for each 'vma' or 'map' found in file provided. The file is expected to be in the
+// same format as /proc/<pid>/smaps. Returns 'false' if the file is malformed.
+bool ForEachVmaFromFile(const std::string& path, const VmaCallback& callback);
+
+// Returns if the kernel supports /proc/<pid>/smaps_rollup. Assumes that the
+// calling process has access to the /proc/<pid>/smaps_rollup.
+// Returns 'false' if the calling process has no permission to read the file if it exists
+// of if the file doesn't exist.
+bool IsSmapsRollupSupported(pid_t pid);
+
+// Same as ProcMemInfo::SmapsOrRollup but reads the statistics directly
+// from a file. The file MUST be in the same format as /proc/<pid>/smaps
+// or /proc/<pid>/smaps_rollup
+bool SmapsOrRollupFromFile(const std::string& path, MemUsage* stats);
+
+// Same as ProcMemInfo::SmapsOrRollupPss but reads the statistics directly
+// from a file and returns total Pss in kB. The file MUST be in the same format
+// as /proc/<pid>/smaps or /proc/<pid>/smaps_rollup
+bool SmapsOrRollupPssFromFile(const std::string& path, uint64_t* pss);
+
+}  // namespace meminfo
+}  // namespace android
diff --git a/libmeminfo/include/meminfo/sysmeminfo.h b/libmeminfo/include/meminfo/sysmeminfo.h
new file mode 100644
index 0000000..8388920
--- /dev/null
+++ b/libmeminfo/include/meminfo/sysmeminfo.h
@@ -0,0 +1,93 @@
+/*
+ * 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>
+
+#include <functional>
+#include <map>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace meminfo {
+
+class SysMemInfo final {
+    // System or Global memory accounting
+  public:
+    static constexpr const char* kMemTotal = "MemTotal:";
+    static constexpr const char* kMemFree = "MemFree:";
+    static constexpr const char* kMemBuffers = "Buffers:";
+    static constexpr const char* kMemCached = "Cached:";
+    static constexpr const char* kMemShmem = "Shmem:";
+    static constexpr const char* kMemSlab = "Slab:";
+    static constexpr const char* kMemSReclaim = "SReclaimable:";
+    static constexpr const char* kMemSUnreclaim = "SUnreclaim:";
+    static constexpr const char* kMemSwapTotal = "SwapTotal:";
+    static constexpr const char* kMemSwapFree = "SwapFree:";
+    static constexpr const char* kMemMapped = "Mapped:";
+    static constexpr const char* kMemVmallocUsed = "VmallocUsed:";
+    static constexpr const char* kMemPageTables = "PageTables:";
+    static constexpr const char* kMemKernelStack = "KernelStack:";
+
+    static const std::vector<std::string> kDefaultSysMemInfoTags;
+
+    SysMemInfo() = default;
+
+    // Parse /proc/meminfo and read values that are needed
+    bool ReadMemInfo(const std::string& path = "/proc/meminfo");
+    bool ReadMemInfo(const std::vector<std::string>& tags, std::vector<uint64_t>* out,
+                     const std::string& path = "/proc/meminfo");
+    bool ReadMemInfo(std::vector<uint64_t>* out, const std::string& path = "/proc/meminfo");
+
+    // Parse /proc/vmallocinfo and return total physical memory mapped
+    // in vmalloc area by the kernel.
+    // Note that this deliberately ignores binder buffers. They are _always_
+    // mapped in a process and are counted for in each process.
+    uint64_t ReadVmallocInfo();
+
+    // getters
+    uint64_t mem_total_kb() { return mem_in_kb_[kMemTotal]; }
+    uint64_t mem_free_kb() { return mem_in_kb_[kMemFree]; }
+    uint64_t mem_buffers_kb() { return mem_in_kb_[kMemBuffers]; }
+    uint64_t mem_cached_kb() { return mem_in_kb_[kMemCached]; }
+    uint64_t mem_shmem_kb() { return mem_in_kb_[kMemShmem]; }
+    uint64_t mem_slab_kb() { return mem_in_kb_[kMemSlab]; }
+    uint64_t mem_slab_reclaimable_kb() { return mem_in_kb_[kMemSReclaim]; }
+    uint64_t mem_slab_unreclaimable_kb() { return mem_in_kb_[kMemSUnreclaim]; }
+    uint64_t mem_swap_kb() { return mem_in_kb_[kMemSwapTotal]; }
+    uint64_t mem_swap_free_kb() { return mem_in_kb_[kMemSwapFree]; }
+    uint64_t mem_mapped_kb() { return mem_in_kb_[kMemMapped]; }
+    uint64_t mem_vmalloc_used_kb() { return mem_in_kb_[kMemVmallocUsed]; }
+    uint64_t mem_page_tables_kb() { return mem_in_kb_[kMemPageTables]; }
+    uint64_t mem_kernel_stack_kb() { return mem_in_kb_[kMemKernelStack]; }
+    uint64_t mem_zram_kb(const std::string& zram_dev = "");
+
+  private:
+    std::map<std::string, uint64_t> mem_in_kb_;
+    bool MemZramDevice(const std::string& zram_dev, uint64_t* mem_zram_dev);
+    bool ReadMemInfo(const std::vector<std::string>& tags, const std::string& path,
+                     std::function<void(const std::string&, uint64_t)> store_val);
+};
+
+// Parse /proc/vmallocinfo and return total physical memory mapped
+// in vmalloc area by the kernel. Note that this deliberately ignores binder buffers. They are
+// _always_ mapped in a process and are counted for in each process.
+uint64_t ReadVmallocInfo(const std::string& path = "/proc/vmallocinfo");
+
+}  // namespace meminfo
+}  // namespace android
diff --git a/libmeminfo/libdmabufinfo/Android.bp b/libmeminfo/libdmabufinfo/Android.bp
new file mode 100644
index 0000000..4aed45c
--- /dev/null
+++ b/libmeminfo/libdmabufinfo/Android.bp
@@ -0,0 +1,56 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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: "dmabufinfo_defaults",
+    static_libs: [
+        "libbase",
+        "libprocinfo",
+    ],
+    shared_libs: [
+        "liblog",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+}
+
+cc_library_static {
+    name: "libdmabufinfo",
+    vendor_available: true,
+    defaults: ["dmabufinfo_defaults"],
+    export_include_dirs: ["include"],
+    srcs: [
+         "dmabufinfo.cpp",
+    ],
+}
+
+cc_test {
+    name: "dmabufinfo_test",
+    defaults: ["dmabufinfo_defaults"],
+    srcs: [
+         "dmabufinfo_test.cpp"
+    ],
+
+    static_libs: [
+        "libc++fs",
+        "libdmabufinfo",
+        "libion",
+        "libmeminfo",
+    ],
+}
diff --git a/libmeminfo/libdmabufinfo/dmabufinfo.cpp b/libmeminfo/libdmabufinfo/dmabufinfo.cpp
new file mode 100644
index 0000000..9fb22a1
--- /dev/null
+++ b/libmeminfo/libdmabufinfo/dmabufinfo.cpp
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <filesystem>
+#include <memory>
+#include <string>
+#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>
+#include <procinfo/process_map.h>
+
+#include <dmabufinfo/dmabufinfo.h>
+
+namespace android {
+namespace dmabufinfo {
+
+static bool FileIsDmaBuf(const std::string& path) {
+    return ::android::base::StartsWith(path, "/dmabuf");
+}
+
+static bool ReadDmaBufFdInfo(pid_t pid, int fd, std::string* name, std::string* exporter,
+                             uint64_t* count) {
+    std::string fdinfo = ::android::base::StringPrintf("/proc/%d/fdinfo/%d", pid, fd);
+    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fdinfo.c_str(), "re"), fclose};
+    if (fp == nullptr) {
+        LOG(ERROR) << "Failed to open dmabuf info from debugfs";
+        return false;
+    }
+
+    char* line = nullptr;
+    size_t len = 0;
+    while (getline(&line, &len, fp.get()) > 0) {
+        switch (line[0]) {
+            case 'c':
+                if (strncmp(line, "count:", 6) == 0) {
+                    char* c = line + 6;
+                    *count = strtoull(c, nullptr, 10);
+                }
+                break;
+            case 'e':
+                if (strncmp(line, "exp_name:", 9) == 0) {
+                    char* c = line + 9;
+                    *exporter = ::android::base::Trim(c);
+                }
+                break;
+            case 'n':
+                if (strncmp(line, "name:", 5) == 0) {
+                    char* c = line + 5;
+                    *name = ::android::base::Trim(std::string(c));
+                }
+                break;
+        }
+    }
+
+    free(line);
+    return true;
+}
+
+// TODO: std::filesystem::is_symlink fails to link on vendor code,
+// forcing this workaround.
+// Move back to libc++fs once it is vendor-available. See b/124012728
+static bool is_symlink(const char *filename)
+{
+    struct stat p_statbuf;
+    if (lstat(filename, &p_statbuf) < 0) {
+        return false;
+    }
+    if (S_ISLNK(p_statbuf.st_mode) == 1) {
+        return true;
+    }
+    return false;
+}
+
+static bool ReadDmaBufFdRefs(pid_t pid, std::vector<DmaBuffer>* dmabufs) {
+    std::string fdpath = ::android::base::StringPrintf("/proc/%d/fd", pid);
+
+    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(fdpath.c_str()), closedir);
+    if (!dir) {
+        LOG(ERROR) << "Failed to open " << fdpath << " directory" << std::endl;
+        return false;
+    }
+    struct dirent* dent;
+    while ((dent = readdir(dir.get()))) {
+        std::string path =
+            ::android::base::StringPrintf("%s/%s", fdpath.c_str(), dent->d_name);
+
+        if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..") ||
+            !is_symlink(path.c_str())) {
+            continue;
+        }
+
+        std::string target;
+        if (!::android::base::Readlink(path, &target)) {
+            LOG(ERROR) << "Failed to find target for symlink: " << path;
+            return false;
+        }
+
+        if (!FileIsDmaBuf(target)) {
+            continue;
+        }
+
+        int fd;
+        if (!::android::base::ParseInt(dent->d_name, &fd)) {
+            LOG(ERROR) << "Dmabuf fd: " << path << " is invalid";
+            return false;
+        }
+
+        // Set defaults in case the kernel doesn't give us the information
+        // we need in fdinfo
+        std::string name = "<unknown>";
+        std::string exporter = "<unknown>";
+        uint64_t count = 0;
+        if (!ReadDmaBufFdInfo(pid, fd, &name, &exporter, &count)) {
+            LOG(ERROR) << "Failed to read fdinfo for: " << path;
+            return false;
+        }
+
+        struct stat sb;
+        if (stat(path.c_str(), &sb) < 0) {
+            PLOG(ERROR) << "Failed to stat: " << path;
+            return false;
+        }
+
+        uint64_t inode = sb.st_ino;
+        auto buf = std::find_if(dmabufs->begin(), dmabufs->end(),
+                                [&inode](const DmaBuffer& dbuf) { return dbuf.inode() == inode; });
+        if (buf != dmabufs->end()) {
+            if (buf->name() == "" || buf->name() == "<unknown>") buf->SetName(name);
+            if (buf->exporter() == "" || buf->exporter() == "<unknown>") buf->SetExporter(exporter);
+            if (buf->count() == 0) buf->SetCount(count);
+            buf->AddFdRef(pid);
+            continue;
+        }
+
+        DmaBuffer& db = dmabufs->emplace_back(sb.st_ino, sb.st_blocks * 512, count, exporter, name);
+        db.AddFdRef(pid);
+    }
+
+    return true;
+}
+
+static bool ReadDmaBufMapRefs(pid_t pid, std::vector<DmaBuffer>* dmabufs) {
+    std::string mapspath = ::android::base::StringPrintf("/proc/%d/maps", pid);
+    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(mapspath.c_str(), "re"), fclose};
+    if (fp == nullptr) {
+        LOG(ERROR) << "Failed to open maps for pid: " << pid;
+        return false;
+    }
+
+    char* line = nullptr;
+    size_t len = 0;
+
+    // Process the map if it is dmabuf. Add map reference to existing object in 'dmabufs'
+    // if it was already found. If it wasn't create a new one and append it to 'dmabufs'
+    auto account_dmabuf = [&](uint64_t start, uint64_t end, uint16_t /* flags */,
+                              uint64_t /* pgoff */, ino_t inode, const char* name) {
+        // no need to look into this mapping if it is not dmabuf
+        if (!FileIsDmaBuf(std::string(name))) {
+            return;
+        }
+
+        auto buf = std::find_if(dmabufs->begin(), dmabufs->end(),
+                                [&inode](const DmaBuffer& dbuf) { return dbuf.inode() == inode; });
+        if (buf != dmabufs->end()) {
+            buf->AddMapRef(pid);
+            return;
+        }
+
+        // We have a new buffer, but unknown count and name
+        DmaBuffer& dbuf = dmabufs->emplace_back(inode, end - start, 0, "<unknown>", "<unknown>");
+        dbuf.AddMapRef(pid);
+    };
+
+    while (getline(&line, &len, fp.get()) > 0) {
+        if (!::android::procinfo::ReadMapFileContent(line, account_dmabuf)) {
+            LOG(ERROR) << "Failed t parse maps for pid: " << pid;
+            return false;
+        }
+    }
+
+    free(line);
+    return true;
+}
+
+// Public methods
+bool ReadDmaBufInfo(std::vector<DmaBuffer>* dmabufs, const std::string& path) {
+    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+    if (fp == nullptr) {
+        LOG(ERROR) << "Failed to open dmabuf info from debugfs";
+        return false;
+    }
+
+    char* line = nullptr;
+    size_t len = 0;
+    dmabufs->clear();
+    while (getline(&line, &len, fp.get()) > 0) {
+        // The new dmabuf bufinfo format adds inode number and a name at the end
+        // We are looking for lines as follows:
+        // size     flags       mode        count  exp_name ino         name
+        // 01048576 00000002    00000007    00000001    ion 00018758    CAMERA
+        // 01048576 00000002    00000007    00000001    ion 00018758
+        uint64_t size, count;
+        char* exporter_name = nullptr;
+        ino_t inode;
+        char* name = nullptr;
+        int matched = sscanf(line, "%" SCNu64 "%*x %*x %" SCNu64 " %ms %lu %ms", &size, &count,
+                             &exporter_name, &inode, &name);
+        if (matched < 4) {
+            continue;
+        }
+        dmabufs->emplace_back(inode, size, count, exporter_name, matched > 4 ? name : "");
+        free(exporter_name);
+        free(name);
+    }
+
+    free(line);
+
+    return true;
+}
+
+bool ReadDmaBufInfo(pid_t pid, std::vector<DmaBuffer>* dmabufs) {
+    dmabufs->clear();
+    return AppendDmaBufInfo(pid, dmabufs);
+}
+
+bool AppendDmaBufInfo(pid_t pid, std::vector<DmaBuffer>* dmabufs) {
+    if (!ReadDmaBufFdRefs(pid, dmabufs)) {
+        LOG(ERROR) << "Failed to read dmabuf fd references";
+        return false;
+    }
+
+    if (!ReadDmaBufMapRefs(pid, dmabufs)) {
+        LOG(ERROR) << "Failed to read dmabuf map references";
+        return false;
+    }
+    return true;
+}
+
+}  // namespace dmabufinfo
+}  // namespace android
diff --git a/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp b/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp
new file mode 100644
index 0000000..eb53e57
--- /dev/null
+++ b/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp
@@ -0,0 +1,484 @@
+/* Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <inttypes.h>
+#include <linux/dma-buf.h>
+#include <poll.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <ion/ion.h>
+
+#include <dmabufinfo/dmabufinfo.h>
+
+using namespace ::android::dmabufinfo;
+using namespace ::android::base;
+
+#define MAX_HEAP_NAME 32
+#define ION_HEAP_ANY_MASK (0x7fffffff)
+
+struct ion_heap_data {
+    char name[MAX_HEAP_NAME];
+    __u32 type;
+    __u32 heap_id;
+    __u32 reserved0;
+    __u32 reserved1;
+    __u32 reserved2;
+};
+
+#ifndef DMA_BUF_SET_NAME
+#define DMA_BUF_SET_NAME _IOW(DMA_BUF_BASE, 5, const char*)
+#endif
+
+class fd_sharer {
+  public:
+    fd_sharer();
+    ~fd_sharer() { kill(); }
+
+    bool ok() const { return child_pid > 0; }
+    bool sendfd(int fd);
+    bool kill();
+    pid_t pid() const { return child_pid; }
+
+  private:
+    unique_fd parent_fd, child_fd;
+    pid_t child_pid;
+
+    void run();
+};
+
+fd_sharer::fd_sharer() : parent_fd{}, child_fd{}, child_pid{-1} {
+    bool sp_ok = android::base::Socketpair(SOCK_STREAM, &parent_fd, &child_fd);
+    if (!sp_ok) return;
+
+    child_pid = fork();
+    if (child_pid < 0) return;
+
+    if (child_pid == 0) run();
+}
+
+bool fd_sharer::kill() {
+    int err = ::kill(child_pid, SIGKILL);
+    if (err < 0) return false;
+
+    return ::waitpid(child_pid, nullptr, 0) == child_pid;
+}
+
+void fd_sharer::run() {
+    while (true) {
+        int fd;
+        char unused = 0;
+
+        iovec iov{};
+        iov.iov_base = &unused;
+        iov.iov_len = sizeof(unused);
+
+        msghdr msg{};
+        msg.msg_iov = &iov;
+        msg.msg_iovlen = 1;
+
+        char cmsg_buf[CMSG_SPACE(sizeof(fd))];
+        msg.msg_control = cmsg_buf;
+        msg.msg_controllen = sizeof(cmsg_buf);
+
+        cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+        cmsg->cmsg_level = SOL_SOCKET;
+        cmsg->cmsg_type = SCM_RIGHTS;
+        cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+
+        ssize_t s = TEMP_FAILURE_RETRY(recvmsg(child_fd, &msg, 0));
+        if (s == -1) break;
+
+        s = TEMP_FAILURE_RETRY(write(child_fd, &unused, sizeof(unused)));
+        if (s == -1) break;
+    }
+}
+
+bool fd_sharer::sendfd(int fd) {
+    char unused = 0;
+
+    iovec iov{};
+    iov.iov_base = &unused;
+    iov.iov_len = sizeof(unused);
+
+    msghdr msg{};
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+
+    char cmsg_buf[CMSG_SPACE(sizeof(fd))];
+    msg.msg_control = cmsg_buf;
+    msg.msg_controllen = sizeof(cmsg_buf);
+
+    cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type = SCM_RIGHTS;
+    cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+
+    int* fd_buf = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+    *fd_buf = fd;
+
+    ssize_t s = TEMP_FAILURE_RETRY(sendmsg(parent_fd, &msg, 0));
+    if (s == -1) return false;
+
+    // The target process installs the fd into its fd table during recvmsg().
+    // So if we return now, there's a brief window between sendfd() finishing
+    // and libmemoryinfo actually seeing that the buffer has been shared.  This
+    // window is just large enough to break tests.
+    //
+    // To work around this, wait for the target process to respond with a dummy
+    // byte, with a timeout of 1 s.
+    pollfd p{};
+    p.fd = parent_fd;
+    p.events = POLL_IN;
+    int ready = poll(&p, 1, 1000);
+    if (ready != 1) return false;
+
+    s = TEMP_FAILURE_RETRY(read(parent_fd, &unused, sizeof(unused)));
+    if (s == -1) return false;
+
+    return true;
+}
+
+#define EXPECT_ONE_BUF_EQ(_bufptr, _name, _fdrefs, _maprefs, _expname, _count, _size) \
+    do {                                                                              \
+        EXPECT_EQ(_bufptr->name(), _name);                                            \
+        EXPECT_EQ(_bufptr->fdrefs().size(), _fdrefs);                                 \
+        EXPECT_EQ(_bufptr->maprefs().size(), _maprefs);                               \
+        EXPECT_EQ(_bufptr->exporter(), _expname);                                     \
+        EXPECT_EQ(_bufptr->count(), _count);                                          \
+        EXPECT_EQ(_bufptr->size(), _size);                                            \
+    } while (0)
+
+#define EXPECT_PID_IN_FDREFS(_bufptr, _pid, _expect)                         \
+    do {                                                                     \
+        const std::unordered_map<pid_t, int>& _fdrefs = _bufptr->fdrefs();   \
+        auto _ref = _fdrefs.find(_pid);                                      \
+        EXPECT_EQ((_ref != _fdrefs.end()), _expect);                         \
+    } while (0)
+
+#define EXPECT_PID_IN_MAPREFS(_bufptr, _pid, _expect)                        \
+    do {                                                                     \
+        const std::unordered_map<pid_t, int>& _maprefs = _bufptr->maprefs(); \
+        auto _ref = _maprefs.find(_pid);                                     \
+        EXPECT_EQ((_ref != _maprefs.end()), _expect);                        \
+    } while (0)
+
+TEST(DmaBufInfoParser, TestReadDmaBufInfo) {
+    std::string bufinfo = R"bufinfo(00045056    00000002    00000007    00000002    ion 00022069    
+	Attached Devices:
+Total 0 devices attached
+01048576    00000002    00000007    00000001    ion 00019834    CAMERA
+	Attached Devices:
+	soc:qcom,cam_smmu:msm_cam_smmu_icp
+Total 1 devices attached)bufinfo";
+
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(::android::base::WriteStringToFd(bufinfo, tf.fd));
+    std::string path = std::string(tf.path);
+
+    std::vector<DmaBuffer> dmabufs;
+    EXPECT_TRUE(ReadDmaBufInfo(&dmabufs, path));
+
+    EXPECT_EQ(dmabufs.size(), 2UL);
+
+    EXPECT_EQ(dmabufs[0].size(), 45056UL);
+    EXPECT_EQ(dmabufs[0].inode(), 22069UL);
+    EXPECT_EQ(dmabufs[0].count(), 2UL);
+    EXPECT_EQ(dmabufs[0].exporter(), "ion");
+    EXPECT_TRUE(dmabufs[0].name().empty());
+    EXPECT_EQ(dmabufs[0].total_refs(), 0ULL);
+    EXPECT_TRUE(dmabufs[0].fdrefs().empty());
+    EXPECT_TRUE(dmabufs[0].maprefs().empty());
+
+    EXPECT_EQ(dmabufs[1].size(), 1048576UL);
+    EXPECT_EQ(dmabufs[1].inode(), 19834UL);
+    EXPECT_EQ(dmabufs[1].count(), 1UL);
+    EXPECT_EQ(dmabufs[1].exporter(), "ion");
+    EXPECT_FALSE(dmabufs[1].name().empty());
+    EXPECT_EQ(dmabufs[1].name(), "CAMERA");
+    EXPECT_EQ(dmabufs[1].total_refs(), 0ULL);
+    EXPECT_TRUE(dmabufs[1].fdrefs().empty());
+    EXPECT_TRUE(dmabufs[1].maprefs().empty());
+}
+
+class DmaBufTester : public ::testing::Test {
+  public:
+    DmaBufTester() : ion_fd(ion_open()), ion_heap_mask(get_ion_heap_mask()) {}
+
+    ~DmaBufTester() {
+        if (is_valid()) {
+            ion_close(ion_fd);
+        }
+    }
+
+    bool is_valid() { return (ion_fd >= 0 && ion_heap_mask > 0); }
+
+    unique_fd allocate(uint64_t size, const std::string& name) {
+        int fd;
+        int err = ion_alloc_fd(ion_fd, size, 0, ion_heap_mask, 0, &fd);
+        if (err < 0) {
+            return unique_fd{err};
+        }
+
+        if (!name.empty()) {
+            err = ioctl(fd, DMA_BUF_SET_NAME, name.c_str());
+            if (err < 0) return unique_fd{-errno};
+        }
+
+        return unique_fd{fd};
+    }
+
+    void readAndCheckDmaBuffer(std::vector<DmaBuffer>* dmabufs, pid_t pid, const std::string name,
+                               size_t fdrefs_size, size_t maprefs_size, const std::string exporter,
+                               size_t refcount, uint64_t buf_size, bool expectFdrefs,
+                               bool expectMapRefs) {
+        EXPECT_TRUE(ReadDmaBufInfo(pid, dmabufs));
+        EXPECT_EQ(dmabufs->size(), 1UL);
+        EXPECT_ONE_BUF_EQ(dmabufs->begin(), name, fdrefs_size, maprefs_size, exporter, refcount,
+                          buf_size);
+        // Make sure the buffer has the right pid too.
+        EXPECT_PID_IN_FDREFS(dmabufs->begin(), pid, expectFdrefs);
+        EXPECT_PID_IN_MAPREFS(dmabufs->begin(), pid, expectMapRefs);
+    }
+
+    bool checkPidRef(DmaBuffer& dmabuf, pid_t pid, int expectFdrefs) {
+        int fdrefs = dmabuf.fdrefs().find(pid)->second;
+        return fdrefs == expectFdrefs;
+    }
+
+  private:
+    int get_ion_heap_mask() {
+        if (ion_fd < 0) {
+            return 0;
+        }
+
+        if (ion_is_legacy(ion_fd)) {
+            // Since ION is still in staging, we've seen that the heap mask ids are also
+            // changed across kernels for some reason. So, here we basically ask for a buffer
+            // from _any_ heap.
+            return ION_HEAP_ANY_MASK;
+        }
+
+        int cnt;
+        int err = ion_query_heap_cnt(ion_fd, &cnt);
+        if (err < 0) {
+            return err;
+        }
+
+        std::vector<ion_heap_data> heaps;
+        heaps.resize(cnt);
+        err = ion_query_get_heaps(ion_fd, cnt, &heaps[0]);
+        if (err < 0) {
+            return err;
+        }
+
+        unsigned int ret = 0;
+        for (auto& it : heaps) {
+            if (!strcmp(it.name, "ion_system_heap")) {
+                ret |= (1 << it.heap_id);
+            }
+        }
+
+        return ret;
+    }
+
+    unique_fd ion_fd;
+    const int ion_heap_mask;
+};
+
+TEST_F(DmaBufTester, TestFdRef) {
+    // Test if a dma buffer is found while the corresponding file descriptor
+    // is open
+    ASSERT_TRUE(is_valid());
+    pid_t pid = getpid();
+    std::vector<DmaBuffer> dmabufs;
+    {
+        // Allocate one buffer and make sure the library can see it
+        unique_fd buf = allocate(4096, "dmabuftester-4k");
+        ASSERT_GT(buf, 0) << "Allocated buffer is invalid";
+        ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs));
+
+        EXPECT_EQ(dmabufs.size(), 1UL);
+        EXPECT_ONE_BUF_EQ(dmabufs.begin(), "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL);
+
+        // Make sure the buffer has the right pid too.
+        EXPECT_PID_IN_FDREFS(dmabufs.begin(), pid, true);
+    }
+
+    // Now make sure the buffer has disappeared
+    ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs));
+    EXPECT_TRUE(dmabufs.empty());
+}
+
+TEST_F(DmaBufTester, TestMapRef) {
+    // Test to make sure we can find a buffer if the fd is closed but the buffer
+    // is mapped
+    ASSERT_TRUE(is_valid());
+    pid_t pid = getpid();
+    std::vector<DmaBuffer> dmabufs;
+    {
+        // Allocate one buffer and make sure the library can see it
+        unique_fd buf = allocate(4096, "dmabuftester-4k");
+        ASSERT_GT(buf, 0) << "Allocated buffer is invalid";
+        auto ptr = mmap(0, 4096, PROT_READ, MAP_SHARED, buf, 0);
+        ASSERT_NE(ptr, MAP_FAILED);
+        ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs));
+
+        EXPECT_EQ(dmabufs.size(), 1UL);
+        EXPECT_ONE_BUF_EQ(dmabufs.begin(), "dmabuftester-4k", 1UL, 1UL, "ion", 2UL, 4096ULL);
+
+        // Make sure the buffer has the right pid too.
+        EXPECT_PID_IN_FDREFS(dmabufs.begin(), pid, true);
+        EXPECT_PID_IN_MAPREFS(dmabufs.begin(), pid, true);
+
+        // close the file descriptor and re-read the stats
+        buf.reset(-1);
+        ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs));
+
+        EXPECT_EQ(dmabufs.size(), 1UL);
+        EXPECT_ONE_BUF_EQ(dmabufs.begin(), "<unknown>", 0UL, 1UL, "<unknown>", 0UL, 4096ULL);
+
+        EXPECT_PID_IN_FDREFS(dmabufs.begin(), pid, false);
+        EXPECT_PID_IN_MAPREFS(dmabufs.begin(), pid, true);
+
+        // unmap the bufer and lose all references
+        munmap(ptr, 4096);
+    }
+
+    // Now make sure the buffer has disappeared
+    ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs));
+    EXPECT_TRUE(dmabufs.empty());
+}
+
+TEST_F(DmaBufTester, TestSharedfd) {
+    // Each time a shared buffer is received over a socket, the remote process
+    // will take an extra reference on it.
+
+    ASSERT_TRUE(is_valid());
+
+    pid_t pid = getpid();
+    std::vector<DmaBuffer> dmabufs;
+    {
+        fd_sharer sharer{};
+        ASSERT_TRUE(sharer.ok());
+        // Allocate one buffer and make sure the library can see it
+        unique_fd buf = allocate(4096, "dmabuftester-4k");
+        ASSERT_GT(buf, 0) << "Allocated buffer is invalid";
+        readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL, true,
+                              false);
+
+        ASSERT_TRUE(sharer.sendfd(buf));
+        readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 2UL, 4096ULL, true,
+                              false);
+        EXPECT_TRUE(checkPidRef(dmabufs[0], pid, 1));
+        readAndCheckDmaBuffer(&dmabufs, sharer.pid(), "dmabuftester-4k", 1UL, 0UL, "ion", 2UL,
+                              4096ULL, true, false);
+        EXPECT_TRUE(checkPidRef(dmabufs[0], sharer.pid(), 1));
+
+        ASSERT_TRUE(sharer.sendfd(buf));
+        readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 3UL, 4096ULL, true,
+                              false);
+        EXPECT_TRUE(checkPidRef(dmabufs[0], pid, 1));
+        readAndCheckDmaBuffer(&dmabufs, sharer.pid(), "dmabuftester-4k", 1UL, 0UL, "ion", 3UL,
+                              4096ULL, true, false);
+        EXPECT_TRUE(checkPidRef(dmabufs[0], sharer.pid(), 2));
+
+        ASSERT_TRUE(sharer.kill());
+        readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL, true,
+                              false);
+    }
+
+    // Now make sure the buffer has disappeared
+    ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs));
+    EXPECT_TRUE(dmabufs.empty());
+}
+
+TEST_F(DmaBufTester, DupFdTest) {
+    // dup()ing an fd will make this process take an extra reference on the
+    // shared buffer.
+
+    ASSERT_TRUE(is_valid());
+
+    pid_t pid = getpid();
+    std::vector<DmaBuffer> dmabufs;
+    {
+        // Allocate one buffer and make sure the library can see it
+        unique_fd buf = allocate(4096, "dmabuftester-4k");
+        ASSERT_GT(buf, 0) << "Allocated buffer is invalid";
+        readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL, true,
+                              false);
+
+        unique_fd buf2{dup(buf)};
+        readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 2UL, 4096ULL, true,
+                              false);
+        EXPECT_TRUE(checkPidRef(dmabufs[0], pid, 2));
+
+        close(buf2.release());
+        readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL, true,
+                              false);
+        EXPECT_TRUE(checkPidRef(dmabufs[0], pid, 1));
+    }
+
+    // Now make sure the buffer has disappeared
+    ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs));
+    EXPECT_TRUE(dmabufs.empty());
+}
+
+TEST_F(DmaBufTester, ForkTest) {
+    // fork()ing a child will cause the child to automatically take a reference
+    // on any existing shared buffers.
+    ASSERT_TRUE(is_valid());
+
+    pid_t pid = getpid();
+    std::vector<DmaBuffer> dmabufs;
+    {
+        // Allocate one buffer and make sure the library can see it
+        unique_fd buf = allocate(4096, "dmabuftester-4k");
+        ASSERT_GT(buf, 0) << "Allocated buffer is invalid";
+        readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL, true,
+                              false);
+        fd_sharer sharer{};
+        ASSERT_TRUE(sharer.ok());
+        readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 2UL, 4096ULL, true,
+                              false);
+        readAndCheckDmaBuffer(&dmabufs, sharer.pid(), "dmabuftester-4k", 1UL, 0UL, "ion", 2UL,
+                              4096ULL, true, false);
+        ASSERT_TRUE(sharer.kill());
+        readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL, true,
+                              false);
+    }
+
+    // Now make sure the buffer has disappeared
+    ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs));
+    EXPECT_TRUE(dmabufs.empty());
+}
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ::android::base::InitLogging(argv, android::base::StderrLogger);
+    return RUN_ALL_TESTS();
+}
diff --git a/libmeminfo/libdmabufinfo/include/dmabufinfo/dmabufinfo.h b/libmeminfo/libdmabufinfo/include/dmabufinfo/dmabufinfo.h
new file mode 100644
index 0000000..a6e7f69
--- /dev/null
+++ b/libmeminfo/libdmabufinfo/include/dmabufinfo/dmabufinfo.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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>
+#include <unistd.h>
+
+#include <set>
+#include <string>
+#include <vector>
+#include <unordered_map>
+
+namespace android {
+namespace dmabufinfo {
+
+struct DmaBuffer {
+  public:
+    DmaBuffer(ino_t inode, uint64_t size, uint64_t count, const std::string& exporter,
+              const std::string& name)
+        : inode_(inode), size_(size), count_(count), exporter_(exporter), name_(name) {
+        total_refs_ = 0;
+    }
+    DmaBuffer() = default;
+    ~DmaBuffer() = default;
+
+    // Adds one file descriptor reference for the given pid
+    void AddFdRef(pid_t pid) {
+        AddRefToPidMap(pid, &fdrefs_);
+        total_refs_++;
+    }
+
+    // Adds one map reference for the given pid
+    void AddMapRef(pid_t pid) {
+        AddRefToPidMap(pid, &maprefs_);
+        total_refs_++;
+    }
+
+    // Getters for each property
+    uint64_t size() const { return size_; }
+    const std::unordered_map<pid_t, int>& fdrefs() const { return fdrefs_; }
+    const std::unordered_map<pid_t, int>& maprefs() const { return maprefs_; }
+    ino_t inode() const { return inode_; }
+    uint64_t total_refs() const { return total_refs_; }
+    uint64_t count() const { return count_; };
+    const std::set<pid_t>& pids() const { return pids_; }
+    const std::string& name() const { return name_; }
+    const std::string& exporter() const { return exporter_; }
+    void SetName(const std::string& name) { name_ = name; }
+    void SetExporter(const std::string& exporter) { exporter_ = exporter; }
+    void SetCount(uint64_t count) { count_ = count; }
+    uint64_t Pss() const { return size_ / pids_.size(); }
+
+    bool operator==(const DmaBuffer& rhs) {
+        return (inode_ == rhs.inode()) && (size_ == rhs.size()) && (name_ == rhs.name()) &&
+               (exporter_ == rhs.exporter());
+    }
+
+  private:
+    ino_t inode_;
+    uint64_t size_;
+    uint64_t count_;
+    uint64_t total_refs_;
+    std::set<pid_t> pids_;
+    std::string exporter_;
+    std::string name_;
+    std::unordered_map<pid_t, int> fdrefs_;
+    std::unordered_map<pid_t, int> maprefs_;
+    void AddRefToPidMap(pid_t pid, std::unordered_map<pid_t, int>* map) {
+        // The first time we find a ref, we set the ref count to 1
+        // otherwise, increment the existing ref count
+        auto [it, inserted] = map->insert(std::make_pair(pid, 1));
+        if (!inserted)
+            it->second++;
+        pids_.insert(pid);
+    }
+};
+
+// Read and return current dma buf objects from
+// DEBUGFS/dma_buf/bufinfo. The references to each dma buffer are not
+// populated here and will return an empty vector.
+// Returns false if something went wrong with the function, true otherwise.
+bool ReadDmaBufInfo(std::vector<DmaBuffer>* dmabufs,
+                    const std::string& path = "/sys/kernel/debug/dma_buf/bufinfo");
+
+
+// Read and return dmabuf objects for a given process without the help
+// of DEBUGFS
+// Returns false if something went wrong with the function, true otherwise.
+bool ReadDmaBufInfo(pid_t pid, std::vector<DmaBuffer>* dmabufs);
+
+// Append new dmabuf objects from a given process to an existing vector.
+// When the vector contains an existing element with a matching inode,
+// the reference counts will be updated.
+// Does not depend on DEBUGFS.
+// Returns false if something went wrong with the function, true otherwise.
+bool AppendDmaBufInfo(pid_t pid, std::vector<DmaBuffer>* dmabufs);
+
+}  // namespace dmabufinfo
+}  // namespace android
diff --git a/libmeminfo/libdmabufinfo/tools/Android.bp b/libmeminfo/libdmabufinfo/tools/Android.bp
new file mode 100644
index 0000000..224b68e
--- /dev/null
+++ b/libmeminfo/libdmabufinfo/tools/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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: "dmabuf_dump",
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    srcs: ["dmabuf_dump.cpp"],
+    shared_libs: [
+        "libbase",
+    ],
+    static_libs: [
+        "libdmabufinfo",
+    ],
+    product_specific: true,
+}
\ No newline at end of file
diff --git a/libmeminfo/libdmabufinfo/tools/dmabuf_dump.cpp b/libmeminfo/libdmabufinfo/tools/dmabuf_dump.cpp
new file mode 100644
index 0000000..48901b1
--- /dev/null
+++ b/libmeminfo/libdmabufinfo/tools/dmabuf_dump.cpp
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <set>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <dmabufinfo/dmabufinfo.h>
+
+using DmaBuffer = ::android::dmabufinfo::DmaBuffer;
+
+[[noreturn]] static void usage(int exit_status) {
+    fprintf(stderr,
+            "Usage: %s [-ah] [PID] \n"
+            "-a\t show all dma buffers (ion) in big table, [buffer x process] grid \n"
+            "-h\t show this help\n"
+            "  \t If PID is supplied, the dmabuf information for that process is shown.\n",
+            getprogname());
+
+    exit(exit_status);
+}
+
+static std::string GetProcessComm(const pid_t pid) {
+    std::string pid_path = android::base::StringPrintf("/proc/%d/comm", pid);
+    std::ifstream in{pid_path};
+    if (!in) return std::string("N/A");
+    std::string line;
+    std::getline(in, line);
+    if (!in) return std::string("N/A");
+    return line;
+}
+
+static void PrintDmaBufTable(const std::vector<DmaBuffer>& bufs) {
+    if (bufs.empty()) {
+        printf("dmabuf info not found ¯\\_(ツ)_/¯\n");
+        return;
+    }
+
+    // Find all unique pids in the input vector, create a set
+    std::set<pid_t> pid_set;
+    for (auto& buf : bufs) {
+        pid_set.insert(buf.pids().begin(), buf.pids().end());
+    }
+
+    // Format the header string spaced and separated with '|'
+    printf("    Dmabuf Inode |            Size |      Ref Counts |");
+    for (auto pid : pid_set) {
+        printf("%16s:%-5d |", GetProcessComm(pid).c_str(), pid);
+    }
+    printf("\n");
+
+    // holds per-process dmabuf size in kB
+    std::map<pid_t, uint64_t> per_pid_size = {};
+    uint64_t dmabuf_total_size = 0;
+
+    // Iterate through all dmabufs and collect per-process sizes, refs
+    for (auto& buf : bufs) {
+        printf("%16ju |%13" PRIu64 " kB |%16" PRIu64 " |", static_cast<uintmax_t>(buf.inode()),
+               buf.size() / 1024, buf.total_refs());
+        // Iterate through each process to find out per-process references for each buffer,
+        // gather total size used by each process etc.
+        for (pid_t pid : pid_set) {
+            int pid_refs = 0;
+            if (buf.fdrefs().count(pid) == 1) {
+                // Get the total number of ref counts the process is holding
+                // on this buffer. We don't differentiate between mmap or fd.
+                pid_refs += buf.fdrefs().at(pid);
+                if (buf.maprefs().count(pid) == 1) {
+                    pid_refs += buf.maprefs().at(pid);
+                }
+            }
+
+            if (pid_refs) {
+                // Add up the per-pid total size. Note that if a buffer is mapped
+                // in 2 different processes, the size will be shown as mapped or opened
+                // in both processes. This is intended for visibility.
+                //
+                // If one wants to get the total *unique* dma buffers, they can simply
+                // sum the size of all dma bufs shown by the tool
+                per_pid_size[pid] += buf.size() / 1024;
+                printf("%17d refs |", pid_refs);
+            } else {
+                printf("%22s |", "--");
+            }
+        }
+        dmabuf_total_size += buf.size() / 1024;
+        printf("\n");
+    }
+
+    printf("------------------------------------\n");
+    printf("%-16s  %13" PRIu64 " kB |%16s |", "TOTALS", dmabuf_total_size, "n/a");
+    for (auto pid : pid_set) {
+        printf("%19" PRIu64 " kB |", per_pid_size[pid]);
+    }
+    printf("\n");
+
+    return;
+}
+
+static void PrintDmaBufPerProcess(const std::vector<DmaBuffer>& bufs) {
+    if (bufs.empty()) {
+        printf("dmabuf info not found ¯\\_(ツ)_/¯\n");
+        return;
+    }
+
+    // Create a reverse map from pid to dmabufs
+    std::unordered_map<pid_t, std::set<ino_t>> pid_to_inodes = {};
+    uint64_t total_size = 0;  // Total size of dmabufs in the system
+    uint64_t kernel_rss = 0;  // Total size of dmabufs NOT mapped or opened by a process
+    for (auto& buf : bufs) {
+        for (auto pid : buf.pids()) {
+            pid_to_inodes[pid].insert(buf.inode());
+        }
+        total_size += buf.size();
+        if (buf.fdrefs().empty() && buf.maprefs().empty()) {
+            kernel_rss += buf.size();
+        }
+    }
+    // Create an inode to dmabuf map. We know inodes are unique..
+    std::unordered_map<ino_t, DmaBuffer> inode_to_dmabuf;
+    for (auto buf : bufs) {
+        inode_to_dmabuf[buf.inode()] = buf;
+    }
+
+    uint64_t total_rss = 0, total_pss = 0;
+    for (auto& [pid, inodes] : pid_to_inodes) {
+        uint64_t pss = 0;
+        uint64_t rss = 0;
+
+        printf("%16s:%-5d\n", GetProcessComm(pid).c_str(), pid);
+        printf("%22s %16s %16s %16s %16s\n", "Name", "Rss", "Pss", "nr_procs", "Inode");
+        for (auto& inode : inodes) {
+            DmaBuffer& buf = inode_to_dmabuf[inode];
+            printf("%22s %13" PRIu64 " kB %13" PRIu64 " kB %16zu %16" PRIuMAX "\n",
+                   buf.name().empty() ? "<unknown>" : buf.name().c_str(), buf.size() / 1024,
+                   buf.Pss() / 1024, buf.pids().size(), static_cast<uintmax_t>(buf.inode()));
+            rss += buf.size();
+            pss += buf.Pss();
+        }
+        printf("%22s %13" PRIu64 " kB %13" PRIu64 " kB %16s\n", "PROCESS TOTAL", rss / 1024,
+               pss / 1024, "");
+        printf("----------------------\n");
+        total_rss += rss;
+        total_pss += pss;
+    }
+    printf("dmabuf total: %" PRIu64 " kB kernel_rss: %" PRIu64 " kB userspace_rss: %" PRIu64
+           " kB userspace_pss: %" PRIu64 " kB\n ",
+           total_size / 1024, kernel_rss / 1024, total_rss / 1024, total_pss / 1024);
+}
+
+static bool ReadDmaBufs(std::vector<DmaBuffer>* bufs) {
+    bufs->clear();
+
+    if (!ReadDmaBufInfo(bufs)) {
+        fprintf(stderr, "debugfs entry for dmabuf not available, skipping\n");
+        return false;
+    }
+
+    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir("/proc"), closedir);
+    if (!dir) {
+        fprintf(stderr, "Failed to open /proc directory\n");
+        bufs->clear();
+        return false;
+    }
+
+    struct dirent* dent;
+    while ((dent = readdir(dir.get()))) {
+        if (dent->d_type != DT_DIR) continue;
+
+        int pid = atoi(dent->d_name);
+        if (pid == 0) {
+            continue;
+        }
+
+        if (!AppendDmaBufInfo(pid, bufs)) {
+            fprintf(stderr, "Unable to read dmabuf info for pid %d\n", pid);
+            bufs->clear();
+            return false;
+        }
+    }
+
+    return true;
+}
+
+int main(int argc, char* argv[]) {
+    struct option longopts[] = {{"all", no_argument, nullptr, 'a'},
+                                {"help", no_argument, nullptr, 'h'},
+                                {0, 0, nullptr, 0}};
+
+    int opt;
+    bool show_table = false;
+    while ((opt = getopt_long(argc, argv, "ah", longopts, nullptr)) != -1) {
+        switch (opt) {
+            case 'a':
+                show_table = true;
+                break;
+            case 'h':
+                usage(EXIT_SUCCESS);
+            default:
+                usage(EXIT_FAILURE);
+        }
+    }
+
+    pid_t pid = -1;
+    if (optind < argc) {
+        if (show_table) {
+            fprintf(stderr, "Invalid arguments: -a does not need arguments\n");
+            usage(EXIT_FAILURE);
+        }
+        if (optind != (argc - 1)) {
+            fprintf(stderr, "Invalid arguments - only one [PID] argument is allowed\n");
+            usage(EXIT_FAILURE);
+        }
+        pid = atoi(argv[optind]);
+        if (pid == 0) {
+            fprintf(stderr, "Invalid process id %s\n", argv[optind]);
+            usage(EXIT_FAILURE);
+        }
+    }
+
+    std::vector<DmaBuffer> bufs;
+    if (pid != -1) {
+        if (!ReadDmaBufInfo(pid, &bufs)) {
+            fprintf(stderr, "Unable to read dmabuf info for %d\n", pid);
+            exit(EXIT_FAILURE);
+        }
+    } else {
+        if (!ReadDmaBufs(&bufs)) exit(EXIT_FAILURE);
+    }
+
+    // Show the old dmabuf table, inode x process
+    if (show_table) {
+        PrintDmaBufTable(bufs);
+        return 0;
+    }
+
+    PrintDmaBufPerProcess(bufs);
+
+    return 0;
+}
diff --git a/libmeminfo/libmeminfo_benchmark.cpp b/libmeminfo/libmeminfo_benchmark.cpp
new file mode 100644
index 0000000..d88919b
--- /dev/null
+++ b/libmeminfo/libmeminfo_benchmark.cpp
@@ -0,0 +1,544 @@
+/*
+ * 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 <meminfo/procmeminfo.h>
+#include <meminfo/sysmeminfo.h>
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+
+#include <benchmark/benchmark.h>
+
+using ::android::meminfo::MemUsage;
+using ::android::meminfo::ProcMemInfo;
+using ::android::meminfo::SmapsOrRollupFromFile;
+using ::android::meminfo::SysMemInfo;
+
+enum {
+    MEMINFO_TOTAL,
+    MEMINFO_FREE,
+    MEMINFO_BUFFERS,
+    MEMINFO_CACHED,
+    MEMINFO_SHMEM,
+    MEMINFO_SLAB,
+    MEMINFO_SLAB_RECLAIMABLE,
+    MEMINFO_SLAB_UNRECLAIMABLE,
+    MEMINFO_SWAP_TOTAL,
+    MEMINFO_SWAP_FREE,
+    MEMINFO_ZRAM_TOTAL,
+    MEMINFO_MAPPED,
+    MEMINFO_VMALLOC_USED,
+    MEMINFO_PAGE_TABLES,
+    MEMINFO_KERNEL_STACK,
+    MEMINFO_COUNT
+};
+
+static void get_mem_info(uint64_t mem[], const char* file) {
+    char buffer[4096];
+    unsigned int numFound = 0;
+
+    int fd = open(file, O_RDONLY);
+
+    if (fd < 0) {
+        printf("Unable to open %s: %s\n", file, strerror(errno));
+        return;
+    }
+
+    const int len = read(fd, buffer, sizeof(buffer) - 1);
+    close(fd);
+
+    if (len < 0) {
+        printf("Empty %s\n", file);
+        return;
+    }
+    buffer[len] = 0;
+
+    static const char* const tags[] = {
+            "MemTotal:",     "MemFree:",    "Buffers:",     "Cached:",   "Shmem:", "Slab:",
+            "SReclaimable:", "SUnreclaim:", "SwapTotal:",   "SwapFree:", "ZRam:",  "Mapped:",
+            "VmallocUsed:",  "PageTables:", "KernelStack:", NULL};
+
+    static const int tagsLen[] = {9, 8, 8, 7, 6, 5, 13, 11, 10, 9, 5, 7, 12, 11, 12, 0};
+
+    memset(mem, 0, sizeof(uint64_t) * 15);
+    char* p = buffer;
+    while (*p && (numFound < (sizeof(tagsLen) / sizeof(tagsLen[0])))) {
+        int i = 0;
+        while (tags[i]) {
+            // std::cout << "tag =" << tags[i] << " p = " << std::string(p, tagsLen[i]) <<
+            // std::endl;
+            if (strncmp(p, tags[i], tagsLen[i]) == 0) {
+                p += tagsLen[i];
+                while (*p == ' ') p++;
+                char* num = p;
+                while (*p >= '0' && *p <= '9') p++;
+                if (*p != 0) {
+                    *p = 0;
+                    p++;
+                }
+                mem[i] = atoll(num);
+                numFound++;
+                break;
+            }
+            i++;
+        }
+        while (*p && *p != '\n') {
+            p++;
+        }
+        if (*p) p++;
+    }
+}
+
+static void BM_ReadMemInfo_old(benchmark::State& state) {
+    std::string meminfo = R"meminfo(MemTotal:        3019740 kB
+MemFree:         1809728 kB
+MemAvailable:    2546560 kB
+Buffers:           54736 kB
+Cached:           776052 kB
+SwapCached:            0 kB
+Active:           445856 kB
+Inactive:         459092 kB
+Active(anon):      78492 kB
+Inactive(anon):     2240 kB
+Active(file):     367364 kB
+Inactive(file):   456852 kB
+Unevictable:        3096 kB
+Mlocked:            3096 kB
+SwapTotal:             0 kB
+SwapFree:              0 kB
+Dirty:                32 kB
+Writeback:             0 kB
+AnonPages:         74988 kB
+Mapped:            62624 kB
+Shmem:              4020 kB
+Slab:              86464 kB
+SReclaimable:      44432 kB
+SUnreclaim:        42032 kB
+KernelStack:        4880 kB
+PageTables:         2900 kB
+NFS_Unstable:          0 kB
+Bounce:                0 kB
+WritebackTmp:          0 kB
+CommitLimit:     1509868 kB
+Committed_AS:      80296 kB
+VmallocTotal:   263061440 kB
+VmallocUsed:           0 kB
+VmallocChunk:          0 kB
+AnonHugePages:      6144 kB
+ShmemHugePages:        0 kB
+ShmemPmdMapped:        0 kB
+CmaTotal:         131072 kB
+CmaFree:          130380 kB
+HugePages_Total:       0
+HugePages_Free:        0
+HugePages_Rsvd:        0
+HugePages_Surp:        0
+Hugepagesize:       2048 kB)meminfo";
+
+    TemporaryFile tf;
+    ::android::base::WriteStringToFd(meminfo, tf.fd);
+
+    uint64_t mem[MEMINFO_COUNT];
+    for (auto _ : state) {
+        get_mem_info(mem, tf.path);
+    }
+}
+BENCHMARK(BM_ReadMemInfo_old);
+
+static void BM_ReadMemInfo_new(benchmark::State& state) {
+    std::string meminfo = R"meminfo(MemTotal:        3019740 kB
+MemFree:         1809728 kB
+MemAvailable:    2546560 kB
+Buffers:           54736 kB
+Cached:           776052 kB
+SwapCached:            0 kB
+Active:           445856 kB
+Inactive:         459092 kB
+Active(anon):      78492 kB
+Inactive(anon):     2240 kB
+Active(file):     367364 kB
+Inactive(file):   456852 kB
+Unevictable:        3096 kB
+Mlocked:            3096 kB
+SwapTotal:             0 kB
+SwapFree:              0 kB
+Dirty:                32 kB
+Writeback:             0 kB
+AnonPages:         74988 kB
+Mapped:            62624 kB
+Shmem:              4020 kB
+Slab:              86464 kB
+SReclaimable:      44432 kB
+SUnreclaim:        42032 kB
+KernelStack:        4880 kB
+PageTables:         2900 kB
+NFS_Unstable:          0 kB
+Bounce:                0 kB
+WritebackTmp:          0 kB
+CommitLimit:     1509868 kB
+Committed_AS:      80296 kB
+VmallocTotal:   263061440 kB
+VmallocUsed:           0 kB
+VmallocChunk:          0 kB
+AnonHugePages:      6144 kB
+ShmemHugePages:        0 kB
+ShmemPmdMapped:        0 kB
+CmaTotal:         131072 kB
+CmaFree:          130380 kB
+HugePages_Total:       0
+HugePages_Free:        0
+HugePages_Rsvd:        0
+HugePages_Surp:        0
+Hugepagesize:       2048 kB)meminfo";
+
+    TemporaryFile tf;
+    android::base::WriteStringToFd(meminfo, tf.fd);
+
+    std::string file = std::string(tf.path);
+    std::vector<uint64_t> mem(MEMINFO_COUNT);
+    const std::vector<std::string> tags = {
+            SysMemInfo::kMemTotal,      SysMemInfo::kMemFree,        SysMemInfo::kMemBuffers,
+            SysMemInfo::kMemCached,     SysMemInfo::kMemShmem,       SysMemInfo::kMemSlab,
+            SysMemInfo::kMemSReclaim,   SysMemInfo::kMemSUnreclaim,  SysMemInfo::kMemSwapTotal,
+            SysMemInfo::kMemSwapFree,   SysMemInfo::kMemMapped,      SysMemInfo::kMemVmallocUsed,
+            SysMemInfo::kMemPageTables, SysMemInfo::kMemKernelStack,
+    };
+
+    SysMemInfo smi;
+    for (auto _ : state) {
+        smi.ReadMemInfo(tags, &mem, file);
+    }
+}
+BENCHMARK(BM_ReadMemInfo_new);
+
+static uint64_t get_zram_mem_used(const std::string& zram_dir) {
+    FILE* f = fopen((zram_dir + "mm_stat").c_str(), "r");
+    if (f) {
+        uint64_t mem_used_total = 0;
+
+        int matched = fscanf(f, "%*d %*d %" SCNu64 " %*d %*d %*d %*d", &mem_used_total);
+        if (matched != 1)
+            fprintf(stderr, "warning: failed to parse %s\n", (zram_dir + "mm_stat").c_str());
+
+        fclose(f);
+        return mem_used_total;
+    }
+
+    f = fopen((zram_dir + "mem_used_total").c_str(), "r");
+    if (f) {
+        uint64_t mem_used_total = 0;
+
+        int matched = fscanf(f, "%" SCNu64, &mem_used_total);
+        if (matched != 1)
+            fprintf(stderr, "warning: failed to parse %s\n", (zram_dir + "mem_used_total").c_str());
+
+        fclose(f);
+        return mem_used_total;
+    }
+
+    return 0;
+}
+
+static void BM_ZramTotal_old(benchmark::State& state) {
+    std::string exec_dir = ::android::base::GetExecutableDirectory();
+    std::string zram_mmstat_dir = exec_dir + "/testdata1/";
+    for (auto _ : state) {
+        uint64_t zram_total __attribute__((unused)) = get_zram_mem_used(zram_mmstat_dir) / 1024;
+    }
+}
+BENCHMARK(BM_ZramTotal_old);
+
+static void BM_ZramTotal_new(benchmark::State& state) {
+    std::string exec_dir = ::android::base::GetExecutableDirectory();
+    std::string zram_mmstat_dir = exec_dir + "/testdata1/";
+    SysMemInfo smi;
+    for (auto _ : state) {
+        uint64_t zram_total __attribute__((unused)) = smi.mem_zram_kb(zram_mmstat_dir);
+    }
+}
+BENCHMARK(BM_ZramTotal_new);
+
+static void BM_MemInfoWithZram_old(benchmark::State& state) {
+    std::string meminfo = R"meminfo(MemTotal:        3019740 kB
+MemFree:         1809728 kB
+MemAvailable:    2546560 kB
+Buffers:           54736 kB
+Cached:           776052 kB
+SwapCached:            0 kB
+Active:           445856 kB
+Inactive:         459092 kB
+Active(anon):      78492 kB
+Inactive(anon):     2240 kB
+Active(file):     367364 kB
+Inactive(file):   456852 kB
+Unevictable:        3096 kB
+Mlocked:            3096 kB
+SwapTotal:             0 kB
+SwapFree:              0 kB
+Dirty:                32 kB
+Writeback:             0 kB
+AnonPages:         74988 kB
+Mapped:            62624 kB
+Shmem:              4020 kB
+Slab:              86464 kB
+SReclaimable:      44432 kB
+SUnreclaim:        42032 kB
+KernelStack:        4880 kB
+PageTables:         2900 kB
+NFS_Unstable:          0 kB
+Bounce:                0 kB
+WritebackTmp:          0 kB
+CommitLimit:     1509868 kB
+Committed_AS:      80296 kB
+VmallocTotal:   263061440 kB
+VmallocUsed:           0 kB
+VmallocChunk:          0 kB
+AnonHugePages:      6144 kB
+ShmemHugePages:        0 kB
+ShmemPmdMapped:        0 kB
+CmaTotal:         131072 kB
+CmaFree:          130380 kB
+HugePages_Total:       0
+HugePages_Free:        0
+HugePages_Rsvd:        0
+HugePages_Surp:        0
+Hugepagesize:       2048 kB)meminfo";
+
+    TemporaryFile tf;
+    ::android::base::WriteStringToFd(meminfo, tf.fd);
+    std::string exec_dir = ::android::base::GetExecutableDirectory();
+    std::string zram_mmstat_dir = exec_dir + "/testdata1/";
+    uint64_t mem[MEMINFO_COUNT];
+    for (auto _ : state) {
+        get_mem_info(mem, tf.path);
+        mem[MEMINFO_ZRAM_TOTAL] = get_zram_mem_used("/sys/block/zram0/") / 1024;
+        CHECK_EQ(mem[MEMINFO_KERNEL_STACK], 4880u);
+    }
+}
+BENCHMARK(BM_MemInfoWithZram_old);
+
+static void BM_MemInfoWithZram_new(benchmark::State& state) {
+    std::string meminfo = R"meminfo(MemTotal:        3019740 kB
+MemFree:         1809728 kB
+MemAvailable:    2546560 kB
+Buffers:           54736 kB
+Cached:           776052 kB
+SwapCached:            0 kB
+Active:           445856 kB
+Inactive:         459092 kB
+Active(anon):      78492 kB
+Inactive(anon):     2240 kB
+Active(file):     367364 kB
+Inactive(file):   456852 kB
+Unevictable:        3096 kB
+Mlocked:            3096 kB
+SwapTotal:             0 kB
+SwapFree:              0 kB
+Dirty:                32 kB
+Writeback:             0 kB
+AnonPages:         74988 kB
+Mapped:            62624 kB
+Shmem:              4020 kB
+Slab:              86464 kB
+SReclaimable:      44432 kB
+SUnreclaim:        42032 kB
+KernelStack:        4880 kB
+PageTables:         2900 kB
+NFS_Unstable:          0 kB
+Bounce:                0 kB
+WritebackTmp:          0 kB
+CommitLimit:     1509868 kB
+Committed_AS:      80296 kB
+VmallocTotal:   263061440 kB
+VmallocUsed:           0 kB
+VmallocChunk:          0 kB
+AnonHugePages:      6144 kB
+ShmemHugePages:        0 kB
+ShmemPmdMapped:        0 kB
+CmaTotal:         131072 kB
+CmaFree:          130380 kB
+HugePages_Total:       0
+HugePages_Free:        0
+HugePages_Rsvd:        0
+HugePages_Surp:        0
+Hugepagesize:       2048 kB)meminfo";
+
+    TemporaryFile tf;
+    android::base::WriteStringToFd(meminfo, tf.fd);
+
+    std::string file = std::string(tf.path);
+    std::vector<uint64_t> mem(MEMINFO_COUNT);
+    std::vector<std::string> tags(SysMemInfo::kDefaultSysMemInfoTags);
+    auto it = tags.begin();
+    tags.insert(it + MEMINFO_ZRAM_TOTAL, "Zram:");
+    SysMemInfo smi;
+
+    for (auto _ : state) {
+        smi.ReadMemInfo(tags, &mem, file);
+        CHECK_EQ(mem[MEMINFO_KERNEL_STACK], 4880u);
+    }
+}
+BENCHMARK(BM_MemInfoWithZram_new);
+
+// Current implementation is in frameworks/base/core/jni/android_os_Debug.cpp.
+// That implementation is still buggy and it skips over vmalloc allocated memory by kernel modules.
+// This is the *fixed* version of the same implementation intended for benchmarking against the new
+// one.
+static uint64_t get_allocated_vmalloc_memory(const std::string& vm_file) {
+    char line[1024];
+
+    uint64_t vmalloc_allocated_size = 0;
+    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(vm_file.c_str(), "re"), fclose};
+    if (fp == nullptr) {
+        return 0;
+    }
+
+    while (true) {
+        if (fgets(line, 1024, fp.get()) == NULL) {
+            break;
+        }
+
+        // check to see if there are pages mapped in vmalloc area
+        if (!strstr(line, "pages=")) {
+            continue;
+        }
+
+        long nr_pages;
+        if (sscanf(line, "%*x-%*x %*ld %*s pages=%ld", &nr_pages) == 1) {
+            vmalloc_allocated_size += (nr_pages * getpagesize());
+        } else if (sscanf(line, "%*x-%*x %*ld %*s %*s pages=%ld", &nr_pages) == 1) {
+            // The second case is for kernel modules. If allocation comes from the module,
+            // kernel puts an extra string containing the module name before "pages=" in
+            // the line.
+            //    See: https://elixir.bootlin.com/linux/latest/source/kernel/kallsyms.c#L373
+            vmalloc_allocated_size += (nr_pages * getpagesize());
+        }
+    }
+    return vmalloc_allocated_size;
+}
+
+static void BM_VmallocInfo_old_fixed(benchmark::State& state) {
+    std::string exec_dir = ::android::base::GetExecutableDirectory();
+    std::string vmallocinfo =
+            ::android::base::StringPrintf("%s/testdata1/vmallocinfo", exec_dir.c_str());
+    for (auto _ : state) {
+        CHECK_EQ(get_allocated_vmalloc_memory(vmallocinfo), 29884416);
+    }
+}
+BENCHMARK(BM_VmallocInfo_old_fixed);
+
+static void BM_VmallocInfo_new(benchmark::State& state) {
+    std::string exec_dir = ::android::base::GetExecutableDirectory();
+    std::string vmallocinfo =
+            ::android::base::StringPrintf("%s/testdata1/vmallocinfo", exec_dir.c_str());
+    for (auto _ : state) {
+        CHECK_EQ(::android::meminfo::ReadVmallocInfo(vmallocinfo), 29884416);
+    }
+}
+BENCHMARK(BM_VmallocInfo_new);
+
+// This implementation is picked up as-is from frameworks/base/core/jni/android_os_Debug.cpp
+// and only slightly modified to use std:unique_ptr.
+static bool get_smaps_rollup(const std::string path, MemUsage* rollup) {
+    char lineBuffer[1024];
+    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+    if (fp != nullptr) {
+        char* line;
+        while (true) {
+            if (fgets(lineBuffer, sizeof(lineBuffer), fp.get()) == NULL) {
+                break;
+            }
+            line = lineBuffer;
+
+            switch (line[0]) {
+                case 'P':
+                    if (strncmp(line, "Pss:", 4) == 0) {
+                        char* c = line + 4;
+                        while (*c != 0 && (*c < '0' || *c > '9')) {
+                            c++;
+                        }
+                        rollup->pss += atoi(c);
+                    } else if (strncmp(line, "Private_Clean:", 14) == 0 ||
+                               strncmp(line, "Private_Dirty:", 14) == 0) {
+                        char* c = line + 14;
+                        while (*c != 0 && (*c < '0' || *c > '9')) {
+                            c++;
+                        }
+                        rollup->uss += atoi(c);
+                    }
+                    break;
+                case 'R':
+                    if (strncmp(line, "Rss:", 4) == 0) {
+                        char* c = line + 4;
+                        while (*c != 0 && (*c < '0' || *c > '9')) {
+                            c++;
+                        }
+                        rollup->rss += atoi(c);
+                    }
+                    break;
+                case 'S':
+                    if (strncmp(line, "SwapPss:", 8) == 0) {
+                        char* c = line + 8;
+                        long lSwapPss;
+                        while (*c != 0 && (*c < '0' || *c > '9')) {
+                            c++;
+                        }
+                        lSwapPss = atoi(c);
+                        rollup->swap_pss += lSwapPss;
+                    }
+                    break;
+            }
+        }
+    } else {
+        return false;
+    }
+
+    return true;
+}
+
+static void BM_SmapsRollup_old(benchmark::State& state) {
+    std::string exec_dir = ::android::base::GetExecutableDirectory();
+    std::string path = ::android::base::StringPrintf("%s/testdata1/smaps", exec_dir.c_str());
+    for (auto _ : state) {
+        MemUsage stats;
+        CHECK_EQ(get_smaps_rollup(path, &stats), true);
+        CHECK_EQ(stats.pss, 108384);
+    }
+}
+BENCHMARK(BM_SmapsRollup_old);
+
+static void BM_SmapsRollup_new(benchmark::State& state) {
+    std::string exec_dir = ::android::base::GetExecutableDirectory();
+    std::string path = ::android::base::StringPrintf("%s/testdata1/smaps", exec_dir.c_str());
+    for (auto _ : state) {
+        MemUsage stats;
+        CHECK_EQ(SmapsOrRollupFromFile(path, &stats), true);
+        CHECK_EQ(stats.pss, 108384);
+    }
+}
+BENCHMARK(BM_SmapsRollup_new);
+
+BENCHMARK_MAIN();
diff --git a/libmeminfo/libmeminfo_test.cpp b/libmeminfo/libmeminfo_test.cpp
new file mode 100644
index 0000000..5451ca3
--- /dev/null
+++ b/libmeminfo/libmeminfo_test.cpp
@@ -0,0 +1,651 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+#include <meminfo/pageacct.h>
+#include <meminfo/procmeminfo.h>
+#include <meminfo/sysmeminfo.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+using namespace std;
+using namespace android::meminfo;
+
+pid_t pid = -1;
+
+TEST(ProcMemInfo, TestWorkingTestReset) {
+    // Expect reset to succeed
+    EXPECT_TRUE(ProcMemInfo::ResetWorkingSet(pid));
+}
+
+TEST(ProcMemInfo, UsageEmpty) {
+    // If we created the object for getting working set,
+    // the usage must be empty
+    ProcMemInfo proc_mem(pid, true);
+    const MemUsage& usage = proc_mem.Usage();
+    EXPECT_EQ(usage.rss, 0);
+    EXPECT_EQ(usage.vss, 0);
+    EXPECT_EQ(usage.pss, 0);
+    EXPECT_EQ(usage.uss, 0);
+    EXPECT_EQ(usage.swap, 0);
+}
+
+TEST(ProcMemInfo, MapsNotEmpty) {
+    // Make sure the process maps are never empty
+    ProcMemInfo proc_mem(pid);
+    const std::vector<Vma>& maps = proc_mem.Maps();
+    EXPECT_FALSE(maps.empty());
+}
+
+TEST(ProcMemInfo, WssEmpty) {
+    // If we created the object for getting usage,
+    // the working set must be empty
+    ProcMemInfo proc_mem(pid, false);
+    const MemUsage& wss = proc_mem.Wss();
+    EXPECT_EQ(wss.rss, 0);
+    EXPECT_EQ(wss.vss, 0);
+    EXPECT_EQ(wss.pss, 0);
+    EXPECT_EQ(wss.uss, 0);
+    EXPECT_EQ(wss.swap, 0);
+}
+
+TEST(ProcMemInfo, SwapOffsetsEmpty) {
+    // If we created the object for getting working set,
+    // the swap offsets must be empty
+    ProcMemInfo proc_mem(pid, true);
+    const std::vector<uint16_t>& swap_offsets = proc_mem.SwapOffsets();
+    EXPECT_EQ(swap_offsets.size(), 0);
+}
+
+TEST(ProcMemInfo, IsSmapsSupportedTest) {
+    // Get any pid and check if /proc/<pid>/smaps_rollup exists using the API.
+    // The API must return the appropriate value regardless of the after it succeeds
+    // once.
+    std::string path = ::android::base::StringPrintf("/proc/%d/smaps_rollup", pid);
+    bool supported = IsSmapsRollupSupported(pid);
+    EXPECT_EQ(!access(path.c_str(), F_OK | R_OK), supported);
+    // Second call must return what the first one returned regardless of the pid parameter.
+    // So, deliberately pass invalid pid.
+    EXPECT_EQ(supported, IsSmapsRollupSupported(-1));
+}
+
+TEST(ProcMemInfo, SmapsOrRollupTest) {
+    // Make sure we can parse 'smaps_rollup' correctly
+    std::string rollup =
+            R"rollup(12c00000-7fe859e000 ---p 00000000 00:00 0                                [rollup]
+Rss:              331908 kB
+Pss:              202052 kB
+Shared_Clean:     158492 kB
+Shared_Dirty:      18928 kB
+Private_Clean:     90472 kB
+Private_Dirty:     64016 kB
+Referenced:       318700 kB
+Anonymous:         81984 kB
+AnonHugePages:         0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:               5344 kB
+SwapPss:             442 kB
+Locked:          1523537 kB)rollup";
+
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(::android::base::WriteStringToFd(rollup, tf.fd));
+
+    MemUsage stats;
+    ASSERT_EQ(SmapsOrRollupFromFile(tf.path, &stats), true);
+    EXPECT_EQ(stats.rss, 331908);
+    EXPECT_EQ(stats.pss, 202052);
+    EXPECT_EQ(stats.uss, 154488);
+    EXPECT_EQ(stats.private_clean, 90472);
+    EXPECT_EQ(stats.private_dirty, 64016);
+    EXPECT_EQ(stats.swap_pss, 442);
+}
+
+TEST(ProcMemInfo, SmapsOrRollupSmapsTest) {
+    // Make sure /proc/<pid>/smaps is parsed correctly
+    std::string smaps =
+            R"smaps(12c00000-13440000 rw-p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:               8448 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                2652 kB
+Pss:                2652 kB
+Shared_Clean:        840 kB
+Shared_Dirty:         40 kB
+Private_Clean:        84 kB
+Private_Dirty:      2652 kB
+Referenced:         2652 kB
+Anonymous:          2652 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                102 kB
+SwapPss:              70 kB
+Locked:             2652 kB
+VmFlags: rd wr mr mw me ac 
+)smaps";
+
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(::android::base::WriteStringToFd(smaps, tf.fd));
+
+    MemUsage stats;
+    ASSERT_EQ(SmapsOrRollupFromFile(tf.path, &stats), true);
+    EXPECT_EQ(stats.rss, 2652);
+    EXPECT_EQ(stats.pss, 2652);
+    EXPECT_EQ(stats.uss, 2736);
+    EXPECT_EQ(stats.private_clean, 84);
+    EXPECT_EQ(stats.private_dirty, 2652);
+    EXPECT_EQ(stats.swap_pss, 70);
+}
+
+TEST(ProcMemInfo, SmapsOrRollupPssRollupTest) {
+    // Make sure /proc/<pid>/smaps is parsed correctly
+    // to get the PSS
+    std::string smaps =
+            R"smaps(12c00000-13440000 rw-p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:               8448 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                2652 kB
+Pss:                2652 kB
+Shared_Clean:        840 kB
+Shared_Dirty:         40 kB
+Private_Clean:        84 kB
+Private_Dirty:      2652 kB
+Referenced:         2652 kB
+Anonymous:          2652 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                102 kB
+SwapPss:              70 kB
+Locked:             2652 kB
+VmFlags: rd wr mr mw me ac 
+)smaps";
+
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(::android::base::WriteStringToFd(smaps, tf.fd));
+
+    uint64_t pss;
+    ASSERT_EQ(SmapsOrRollupPssFromFile(tf.path, &pss), true);
+    EXPECT_EQ(pss, 2652);
+}
+
+TEST(ProcMemInfo, SmapsOrRollupPssSmapsTest) {
+    // Correctly parse smaps file to gather pss
+    std::string exec_dir = ::android::base::GetExecutableDirectory();
+    std::string path = ::android::base::StringPrintf("%s/testdata1/smaps_short", exec_dir.c_str());
+
+    uint64_t pss;
+    ASSERT_EQ(SmapsOrRollupPssFromFile(path, &pss), true);
+    EXPECT_EQ(pss, 19119);
+}
+
+TEST(ProcMemInfo, ForEachVmaFromFileTest) {
+    // Parse smaps file correctly to make callbacks for each virtual memory area (vma)
+    std::string exec_dir = ::android::base::GetExecutableDirectory();
+    std::string path = ::android::base::StringPrintf("%s/testdata1/smaps_short", exec_dir.c_str());
+    ProcMemInfo proc_mem(pid);
+
+    std::vector<Vma> vmas;
+    auto collect_vmas = [&](const Vma& v) { vmas.push_back(v); };
+    ASSERT_TRUE(ForEachVmaFromFile(path, collect_vmas));
+
+    // We should get a total of 6 vmas
+    ASSERT_EQ(vmas.size(), 6);
+
+    // Expect values to be equal to what we have in testdata1/smaps_short
+    // Check for sizes first
+    ASSERT_EQ(vmas[0].usage.vss, 32768);
+    EXPECT_EQ(vmas[1].usage.vss, 11204);
+    EXPECT_EQ(vmas[2].usage.vss, 16896);
+    EXPECT_EQ(vmas[3].usage.vss, 260);
+    EXPECT_EQ(vmas[4].usage.vss, 6060);
+    EXPECT_EQ(vmas[5].usage.vss, 4);
+
+    // Check for names
+    EXPECT_EQ(vmas[0].name, "[anon:dalvik-zygote-jit-code-cache]");
+    EXPECT_EQ(vmas[1].name, "/system/framework/x86_64/boot-framework.art");
+    EXPECT_EQ(vmas[2].name, "[anon:libc_malloc]");
+    EXPECT_EQ(vmas[3].name, "/system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex");
+    EXPECT_EQ(vmas[4].name, "/system/lib64/libhwui.so");
+    EXPECT_EQ(vmas[5].name, "[vsyscall]");
+
+    EXPECT_EQ(vmas[0].usage.rss, 2048);
+    EXPECT_EQ(vmas[1].usage.rss, 11188);
+    EXPECT_EQ(vmas[2].usage.rss, 15272);
+    EXPECT_EQ(vmas[3].usage.rss, 260);
+    EXPECT_EQ(vmas[4].usage.rss, 4132);
+    EXPECT_EQ(vmas[5].usage.rss, 0);
+
+    EXPECT_EQ(vmas[0].usage.pss, 113);
+    EXPECT_EQ(vmas[1].usage.pss, 2200);
+    EXPECT_EQ(vmas[2].usage.pss, 15272);
+    EXPECT_EQ(vmas[3].usage.pss, 260);
+    EXPECT_EQ(vmas[4].usage.pss, 1274);
+    EXPECT_EQ(vmas[5].usage.pss, 0);
+
+    EXPECT_EQ(vmas[0].usage.uss, 0);
+    EXPECT_EQ(vmas[1].usage.uss, 1660);
+    EXPECT_EQ(vmas[2].usage.uss, 15272);
+    EXPECT_EQ(vmas[3].usage.uss, 260);
+    EXPECT_EQ(vmas[4].usage.uss, 0);
+    EXPECT_EQ(vmas[5].usage.uss, 0);
+
+    EXPECT_EQ(vmas[0].usage.private_clean, 0);
+    EXPECT_EQ(vmas[1].usage.private_clean, 0);
+    EXPECT_EQ(vmas[2].usage.private_clean, 0);
+    EXPECT_EQ(vmas[3].usage.private_clean, 260);
+    EXPECT_EQ(vmas[4].usage.private_clean, 0);
+    EXPECT_EQ(vmas[5].usage.private_clean, 0);
+
+    EXPECT_EQ(vmas[0].usage.private_dirty, 0);
+    EXPECT_EQ(vmas[1].usage.private_dirty, 1660);
+    EXPECT_EQ(vmas[2].usage.private_dirty, 15272);
+    EXPECT_EQ(vmas[3].usage.private_dirty, 0);
+    EXPECT_EQ(vmas[4].usage.private_dirty, 0);
+    EXPECT_EQ(vmas[5].usage.private_dirty, 0);
+
+    EXPECT_EQ(vmas[0].usage.shared_clean, 0);
+    EXPECT_EQ(vmas[1].usage.shared_clean, 80);
+    EXPECT_EQ(vmas[2].usage.shared_clean, 0);
+    EXPECT_EQ(vmas[3].usage.shared_clean, 0);
+    EXPECT_EQ(vmas[4].usage.shared_clean, 4132);
+    EXPECT_EQ(vmas[5].usage.shared_clean, 0);
+
+    EXPECT_EQ(vmas[0].usage.shared_dirty, 2048);
+    EXPECT_EQ(vmas[1].usage.shared_dirty, 9448);
+    EXPECT_EQ(vmas[2].usage.shared_dirty, 0);
+    EXPECT_EQ(vmas[3].usage.shared_dirty, 0);
+    EXPECT_EQ(vmas[4].usage.shared_dirty, 0);
+    EXPECT_EQ(vmas[5].usage.shared_dirty, 0);
+
+    EXPECT_EQ(vmas[0].usage.swap, 0);
+    EXPECT_EQ(vmas[1].usage.swap, 0);
+    EXPECT_EQ(vmas[2].usage.swap, 0);
+    EXPECT_EQ(vmas[3].usage.swap, 0);
+    EXPECT_EQ(vmas[4].usage.swap, 0);
+    EXPECT_EQ(vmas[5].usage.swap, 0);
+
+    EXPECT_EQ(vmas[0].usage.swap_pss, 0);
+    EXPECT_EQ(vmas[1].usage.swap_pss, 0);
+    EXPECT_EQ(vmas[2].usage.swap_pss, 0);
+    EXPECT_EQ(vmas[3].usage.swap_pss, 0);
+    EXPECT_EQ(vmas[4].usage.swap_pss, 0);
+    EXPECT_EQ(vmas[5].usage.swap_pss, 0);
+}
+
+TEST(ProcMemInfo, SmapsReturnTest) {
+    // Make sure Smaps() is never empty for any process
+    ProcMemInfo proc_mem(pid);
+    auto vmas = proc_mem.Smaps();
+    EXPECT_FALSE(vmas.empty());
+}
+
+TEST(ProcMemInfo, SmapsTest) {
+    std::string exec_dir = ::android::base::GetExecutableDirectory();
+    std::string path = ::android::base::StringPrintf("%s/testdata1/smaps_short", exec_dir.c_str());
+    ProcMemInfo proc_mem(pid);
+    auto vmas = proc_mem.Smaps(path);
+
+    ASSERT_FALSE(vmas.empty());
+    // We should get a total of 6 vmas
+    ASSERT_EQ(vmas.size(), 6);
+
+    // Expect values to be equal to what we have in testdata1/smaps_short
+    // Check for sizes first
+    ASSERT_EQ(vmas[0].usage.vss, 32768);
+    EXPECT_EQ(vmas[1].usage.vss, 11204);
+    EXPECT_EQ(vmas[2].usage.vss, 16896);
+    EXPECT_EQ(vmas[3].usage.vss, 260);
+    EXPECT_EQ(vmas[4].usage.vss, 6060);
+    EXPECT_EQ(vmas[5].usage.vss, 4);
+
+    // Check for names
+    EXPECT_EQ(vmas[0].name, "[anon:dalvik-zygote-jit-code-cache]");
+    EXPECT_EQ(vmas[1].name, "/system/framework/x86_64/boot-framework.art");
+    EXPECT_EQ(vmas[2].name, "[anon:libc_malloc]");
+    EXPECT_EQ(vmas[3].name, "/system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex");
+    EXPECT_EQ(vmas[4].name, "/system/lib64/libhwui.so");
+    EXPECT_EQ(vmas[5].name, "[vsyscall]");
+
+    EXPECT_EQ(vmas[0].usage.rss, 2048);
+    EXPECT_EQ(vmas[1].usage.rss, 11188);
+    EXPECT_EQ(vmas[2].usage.rss, 15272);
+    EXPECT_EQ(vmas[3].usage.rss, 260);
+    EXPECT_EQ(vmas[4].usage.rss, 4132);
+    EXPECT_EQ(vmas[5].usage.rss, 0);
+
+    EXPECT_EQ(vmas[0].usage.pss, 113);
+    EXPECT_EQ(vmas[1].usage.pss, 2200);
+    EXPECT_EQ(vmas[2].usage.pss, 15272);
+    EXPECT_EQ(vmas[3].usage.pss, 260);
+    EXPECT_EQ(vmas[4].usage.pss, 1274);
+    EXPECT_EQ(vmas[5].usage.pss, 0);
+
+    EXPECT_EQ(vmas[0].usage.uss, 0);
+    EXPECT_EQ(vmas[1].usage.uss, 1660);
+    EXPECT_EQ(vmas[2].usage.uss, 15272);
+    EXPECT_EQ(vmas[3].usage.uss, 260);
+    EXPECT_EQ(vmas[4].usage.uss, 0);
+    EXPECT_EQ(vmas[5].usage.uss, 0);
+
+    EXPECT_EQ(vmas[0].usage.private_clean, 0);
+    EXPECT_EQ(vmas[1].usage.private_clean, 0);
+    EXPECT_EQ(vmas[2].usage.private_clean, 0);
+    EXPECT_EQ(vmas[3].usage.private_clean, 260);
+    EXPECT_EQ(vmas[4].usage.private_clean, 0);
+    EXPECT_EQ(vmas[5].usage.private_clean, 0);
+
+    EXPECT_EQ(vmas[0].usage.private_dirty, 0);
+    EXPECT_EQ(vmas[1].usage.private_dirty, 1660);
+    EXPECT_EQ(vmas[2].usage.private_dirty, 15272);
+    EXPECT_EQ(vmas[3].usage.private_dirty, 0);
+    EXPECT_EQ(vmas[4].usage.private_dirty, 0);
+    EXPECT_EQ(vmas[5].usage.private_dirty, 0);
+
+    EXPECT_EQ(vmas[0].usage.shared_clean, 0);
+    EXPECT_EQ(vmas[1].usage.shared_clean, 80);
+    EXPECT_EQ(vmas[2].usage.shared_clean, 0);
+    EXPECT_EQ(vmas[3].usage.shared_clean, 0);
+    EXPECT_EQ(vmas[4].usage.shared_clean, 4132);
+    EXPECT_EQ(vmas[5].usage.shared_clean, 0);
+
+    EXPECT_EQ(vmas[0].usage.shared_dirty, 2048);
+    EXPECT_EQ(vmas[1].usage.shared_dirty, 9448);
+    EXPECT_EQ(vmas[2].usage.shared_dirty, 0);
+    EXPECT_EQ(vmas[3].usage.shared_dirty, 0);
+    EXPECT_EQ(vmas[4].usage.shared_dirty, 0);
+    EXPECT_EQ(vmas[5].usage.shared_dirty, 0);
+
+    EXPECT_EQ(vmas[0].usage.swap, 0);
+    EXPECT_EQ(vmas[1].usage.swap, 0);
+    EXPECT_EQ(vmas[2].usage.swap, 0);
+    EXPECT_EQ(vmas[3].usage.swap, 0);
+    EXPECT_EQ(vmas[4].usage.swap, 0);
+    EXPECT_EQ(vmas[5].usage.swap, 0);
+
+    EXPECT_EQ(vmas[0].usage.swap_pss, 0);
+    EXPECT_EQ(vmas[1].usage.swap_pss, 0);
+    EXPECT_EQ(vmas[2].usage.swap_pss, 0);
+    EXPECT_EQ(vmas[3].usage.swap_pss, 0);
+    EXPECT_EQ(vmas[4].usage.swap_pss, 0);
+    EXPECT_EQ(vmas[5].usage.swap_pss, 0);
+}
+
+TEST(SysMemInfo, TestSysMemInfoFile) {
+    std::string meminfo = R"meminfo(MemTotal:        3019740 kB
+MemFree:         1809728 kB
+MemAvailable:    2546560 kB
+Buffers:           54736 kB
+Cached:           776052 kB
+SwapCached:            0 kB
+Active:           445856 kB
+Inactive:         459092 kB
+Active(anon):      78492 kB
+Inactive(anon):     2240 kB
+Active(file):     367364 kB
+Inactive(file):   456852 kB
+Unevictable:        3096 kB
+Mlocked:            3096 kB
+SwapTotal:         32768 kB
+SwapFree:           4096 kB
+Dirty:                32 kB
+Writeback:             0 kB
+AnonPages:         74988 kB
+Mapped:            62624 kB
+Shmem:              4020 kB
+Slab:              86464 kB
+SReclaimable:      44432 kB
+SUnreclaim:        42032 kB
+KernelStack:        4880 kB
+PageTables:         2900 kB
+NFS_Unstable:          0 kB
+Bounce:                0 kB
+WritebackTmp:          0 kB
+CommitLimit:     1509868 kB
+Committed_AS:      80296 kB
+VmallocTotal:   263061440 kB
+VmallocUsed:       65536 kB
+VmallocChunk:          0 kB
+AnonHugePages:      6144 kB
+ShmemHugePages:        0 kB
+ShmemPmdMapped:        0 kB
+CmaTotal:         131072 kB
+CmaFree:          130380 kB
+HugePages_Total:       0
+HugePages_Free:        0
+HugePages_Rsvd:        0
+HugePages_Surp:        0
+Hugepagesize:       2048 kB)meminfo";
+
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(::android::base::WriteStringToFd(meminfo, tf.fd));
+
+    SysMemInfo mi;
+    ASSERT_TRUE(mi.ReadMemInfo(tf.path));
+    EXPECT_EQ(mi.mem_total_kb(), 3019740);
+    EXPECT_EQ(mi.mem_free_kb(), 1809728);
+    EXPECT_EQ(mi.mem_buffers_kb(), 54736);
+    EXPECT_EQ(mi.mem_cached_kb(), 776052);
+    EXPECT_EQ(mi.mem_shmem_kb(), 4020);
+    EXPECT_EQ(mi.mem_slab_kb(), 86464);
+    EXPECT_EQ(mi.mem_slab_reclaimable_kb(), 44432);
+    EXPECT_EQ(mi.mem_slab_unreclaimable_kb(), 42032);
+    EXPECT_EQ(mi.mem_swap_kb(), 32768);
+    EXPECT_EQ(mi.mem_swap_free_kb(), 4096);
+    EXPECT_EQ(mi.mem_mapped_kb(), 62624);
+    EXPECT_EQ(mi.mem_vmalloc_used_kb(), 65536);
+    EXPECT_EQ(mi.mem_page_tables_kb(), 2900);
+    EXPECT_EQ(mi.mem_kernel_stack_kb(), 4880);
+}
+
+TEST(SysMemInfo, TestEmptyFile) {
+    TemporaryFile tf;
+    std::string empty_string = "";
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(::android::base::WriteStringToFd(empty_string, tf.fd));
+
+    SysMemInfo mi;
+    EXPECT_TRUE(mi.ReadMemInfo(tf.path));
+    EXPECT_EQ(mi.mem_total_kb(), 0);
+}
+
+TEST(SysMemInfo, TestZramTotal) {
+    std::string exec_dir = ::android::base::GetExecutableDirectory();
+
+    SysMemInfo mi;
+    std::string zram_mmstat_dir = exec_dir + "/testdata1/";
+    EXPECT_EQ(mi.mem_zram_kb(zram_mmstat_dir), 30504);
+
+    std::string zram_memused_dir = exec_dir + "/testdata2/";
+    EXPECT_EQ(mi.mem_zram_kb(zram_memused_dir), 30504);
+}
+
+enum {
+    MEMINFO_TOTAL,
+    MEMINFO_FREE,
+    MEMINFO_BUFFERS,
+    MEMINFO_CACHED,
+    MEMINFO_SHMEM,
+    MEMINFO_SLAB,
+    MEMINFO_SLAB_RECLAIMABLE,
+    MEMINFO_SLAB_UNRECLAIMABLE,
+    MEMINFO_SWAP_TOTAL,
+    MEMINFO_SWAP_FREE,
+    MEMINFO_ZRAM_TOTAL,
+    MEMINFO_MAPPED,
+    MEMINFO_VMALLOC_USED,
+    MEMINFO_PAGE_TABLES,
+    MEMINFO_KERNEL_STACK,
+    MEMINFO_COUNT
+};
+
+TEST(SysMemInfo, TestZramWithTags) {
+    std::string meminfo = R"meminfo(MemTotal:        3019740 kB
+MemFree:         1809728 kB
+MemAvailable:    2546560 kB
+Buffers:           54736 kB
+Cached:           776052 kB
+SwapCached:            0 kB
+Active:           445856 kB
+Inactive:         459092 kB
+Active(anon):      78492 kB
+Inactive(anon):     2240 kB
+Active(file):     367364 kB
+Inactive(file):   456852 kB
+Unevictable:        3096 kB
+Mlocked:            3096 kB
+SwapTotal:         32768 kB
+SwapFree:           4096 kB
+Dirty:                32 kB
+Writeback:             0 kB
+AnonPages:         74988 kB
+Mapped:            62624 kB
+Shmem:              4020 kB
+Slab:              86464 kB
+SReclaimable:      44432 kB
+SUnreclaim:        42032 kB
+KernelStack:        4880 kB
+PageTables:         2900 kB
+NFS_Unstable:          0 kB
+Bounce:                0 kB
+WritebackTmp:          0 kB
+CommitLimit:     1509868 kB
+Committed_AS:      80296 kB
+VmallocTotal:   263061440 kB
+VmallocUsed:       65536 kB
+VmallocChunk:          0 kB
+AnonHugePages:      6144 kB
+ShmemHugePages:        0 kB
+ShmemPmdMapped:        0 kB
+CmaTotal:         131072 kB
+CmaFree:          130380 kB
+HugePages_Total:       0
+HugePages_Free:        0
+HugePages_Rsvd:        0
+HugePages_Surp:        0
+Hugepagesize:       2048 kB)meminfo";
+
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(::android::base::WriteStringToFd(meminfo, tf.fd));
+    std::string file = std::string(tf.path);
+    std::vector<uint64_t> mem(MEMINFO_COUNT);
+    std::vector<std::string> tags(SysMemInfo::kDefaultSysMemInfoTags);
+    auto it = tags.begin();
+    tags.insert(it + MEMINFO_ZRAM_TOTAL, "Zram:");
+    SysMemInfo mi;
+
+    // Read system memory info
+    EXPECT_TRUE(mi.ReadMemInfo(tags, &mem, file));
+
+    EXPECT_EQ(mem[MEMINFO_TOTAL], 3019740);
+    EXPECT_EQ(mem[MEMINFO_FREE], 1809728);
+    EXPECT_EQ(mem[MEMINFO_BUFFERS], 54736);
+    EXPECT_EQ(mem[MEMINFO_CACHED], 776052);
+    EXPECT_EQ(mem[MEMINFO_SHMEM], 4020);
+    EXPECT_EQ(mem[MEMINFO_SLAB], 86464);
+    EXPECT_EQ(mem[MEMINFO_SLAB_RECLAIMABLE], 44432);
+    EXPECT_EQ(mem[MEMINFO_SLAB_UNRECLAIMABLE], 42032);
+    EXPECT_EQ(mem[MEMINFO_SWAP_TOTAL], 32768);
+    EXPECT_EQ(mem[MEMINFO_SWAP_FREE], 4096);
+    EXPECT_EQ(mem[MEMINFO_MAPPED], 62624);
+    EXPECT_EQ(mem[MEMINFO_VMALLOC_USED], 65536);
+    EXPECT_EQ(mem[MEMINFO_PAGE_TABLES], 2900);
+    EXPECT_EQ(mem[MEMINFO_KERNEL_STACK], 4880);
+}
+
+TEST(SysMemInfo, TestVmallocInfoNoMemory) {
+    std::string vmallocinfo =
+            R"vmallocinfo(0x0000000000000000-0x0000000000000000   69632 of_iomap+0x78/0xb0 phys=17a00000 ioremap
+0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=b220000 ioremap
+0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=17c90000 ioremap
+0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=17ca0000 ioremap)vmallocinfo";
+
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(::android::base::WriteStringToFd(vmallocinfo, tf.fd));
+    std::string file = std::string(tf.path);
+
+    EXPECT_EQ(ReadVmallocInfo(file), 0);
+}
+
+TEST(SysMemInfo, TestVmallocInfoKernel) {
+    std::string vmallocinfo =
+            R"vmallocinfo(0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc)vmallocinfo";
+
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(::android::base::WriteStringToFd(vmallocinfo, tf.fd));
+    std::string file = std::string(tf.path);
+
+    EXPECT_EQ(ReadVmallocInfo(file), getpagesize());
+}
+
+TEST(SysMemInfo, TestVmallocInfoModule) {
+    std::string vmallocinfo =
+            R"vmallocinfo(0x0000000000000000-0x0000000000000000   28672 pktlog_alloc_buf+0xc4/0x15c [wlan] pages=6 vmalloc)vmallocinfo";
+
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(::android::base::WriteStringToFd(vmallocinfo, tf.fd));
+    std::string file = std::string(tf.path);
+
+    EXPECT_EQ(ReadVmallocInfo(file), 6 * getpagesize());
+}
+
+TEST(SysMemInfo, TestVmallocInfoAll) {
+    std::string vmallocinfo =
+            R"vmallocinfo(0x0000000000000000-0x0000000000000000   69632 of_iomap+0x78/0xb0 phys=17a00000 ioremap
+0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=b220000 ioremap
+0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=17c90000 ioremap
+0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=17ca0000 ioremap
+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000   28672 pktlog_alloc_buf+0xc4/0x15c [wlan] pages=6 vmalloc)vmallocinfo";
+
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(::android::base::WriteStringToFd(vmallocinfo, tf.fd));
+    std::string file = std::string(tf.path);
+
+    EXPECT_EQ(ReadVmallocInfo(file), 7 * getpagesize());
+}
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ::android::base::InitLogging(argv, android::base::StderrLogger);
+    pid = getpid();
+    return RUN_ALL_TESTS();
+}
diff --git a/libmeminfo/meminfo_private.h b/libmeminfo/meminfo_private.h
new file mode 100644
index 0000000..df5699c
--- /dev/null
+++ b/libmeminfo/meminfo_private.h
@@ -0,0 +1,34 @@
+/*
+ * 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>
+#include <unistd.h>
+
+#include <meminfo/meminfo.h>
+#include <meminfo/pageacct.h>
+#include <meminfo/procmeminfo.h>
+#include <meminfo/sysmeminfo.h>
+
+// Macros to do per-page flag manipulation
+#define _BITS(x, offset, bits) (((x) >> (offset)) & ((1LL << (bits)) - 1))
+#define PAGE_PRESENT(x) (_BITS(x, 63, 1))
+#define PAGE_SWAPPED(x) (_BITS(x, 62, 1))
+#define PAGE_SHIFT(x) (_BITS(x, 55, 6))
+#define PAGE_PFN(x) (_BITS(x, 0, 55))
+#define PAGE_SWAP_OFFSET(x) (_BITS(x, 5, 50))
+#define PAGE_SWAP_TYPE(x) (_BITS(x, 0, 5))
diff --git a/libmeminfo/pageacct.cpp b/libmeminfo/pageacct.cpp
new file mode 100644
index 0000000..cb17af8
--- /dev/null
+++ b/libmeminfo/pageacct.cpp
@@ -0,0 +1,157 @@
+/*
+ * 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 <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include "meminfo_private.h"
+
+using unique_fd = ::android::base::unique_fd;
+
+namespace android {
+namespace meminfo {
+
+static inline off64_t pfn_to_idle_bitmap_offset(uint64_t pfn) {
+    return static_cast<off64_t>((pfn >> 6) << 3);
+}
+
+uint64_t pagesize(void) {
+    static uint64_t pagesize = sysconf(_SC_PAGE_SIZE);
+    return pagesize;
+}
+
+bool PageAcct::InitPageAcct(bool pageidle_enable) {
+    if (pageidle_enable && !PageAcct::KernelHasPageIdle()) {
+        LOG(ERROR) << "Idle page tracking is not supported by the kernel";
+        return false;
+    }
+
+    if (kpagecount_fd_ < 0) {
+        unique_fd count_fd(TEMP_FAILURE_RETRY(open("/proc/kpagecount", O_RDONLY | O_CLOEXEC)));
+        if (count_fd < 0) {
+            PLOG(ERROR) << "Failed to open /proc/kpagecount";
+            return false;
+        }
+        kpagecount_fd_ = std::move(count_fd);
+    }
+
+    if (kpageflags_fd_ < 0) {
+        unique_fd flags_fd(TEMP_FAILURE_RETRY(open("/proc/kpageflags", O_RDONLY | O_CLOEXEC)));
+        if (flags_fd < 0) {
+            PLOG(ERROR) << "Failed to open /proc/kpageflags";
+            return false;
+        }
+        kpageflags_fd_ = std::move(flags_fd);
+    }
+
+    if (pageidle_enable && pageidle_fd_ < 0) {
+        unique_fd idle_fd(
+                TEMP_FAILURE_RETRY(open("/sys/kernel/mm/page_idle/bitmap", O_RDWR | O_CLOEXEC)));
+        if (idle_fd < 0) {
+            PLOG(ERROR) << "Failed to open page idle bitmap";
+            return false;
+        }
+        pageidle_fd_ = std::move(idle_fd);
+    }
+
+    return true;
+}
+
+bool PageAcct::PageFlags(uint64_t pfn, uint64_t* flags) {
+    if (!flags) return false;
+
+    if (kpageflags_fd_ < 0) {
+        if (!InitPageAcct()) return false;
+    }
+
+    if (pread64(kpageflags_fd_, flags, sizeof(uint64_t), pfn * sizeof(uint64_t)) !=
+        sizeof(uint64_t)) {
+        PLOG(ERROR) << "Failed to read page flags for page " << pfn;
+        return false;
+    }
+    return true;
+}
+
+bool PageAcct::PageMapCount(uint64_t pfn, uint64_t* mapcount) {
+    if (!mapcount) return false;
+
+    if (kpagecount_fd_ < 0) {
+        if (!InitPageAcct()) return false;
+    }
+
+    if (pread64(kpagecount_fd_, mapcount, sizeof(uint64_t), pfn * sizeof(uint64_t)) !=
+        sizeof(uint64_t)) {
+        PLOG(ERROR) << "Failed to read map count for page " << pfn;
+        return false;
+    }
+    return true;
+}
+
+int PageAcct::IsPageIdle(uint64_t pfn) {
+    if (pageidle_fd_ < 0) {
+        if (!InitPageAcct(true)) return -EOPNOTSUPP;
+    }
+
+    int idle_status = MarkPageIdle(pfn);
+    if (idle_status) return idle_status;
+
+    return GetPageIdle(pfn);
+}
+
+int PageAcct::MarkPageIdle(uint64_t pfn) const {
+    off64_t offset = pfn_to_idle_bitmap_offset(pfn);
+    // set the bit corresponding to page frame
+    uint64_t idle_bits = 1ULL << (pfn % 64);
+
+    if (pwrite64(pageidle_fd_, &idle_bits, sizeof(uint64_t), offset) < 0) {
+        PLOG(ERROR) << "Failed to write page idle bitmap for page " << pfn;
+        return -errno;
+    }
+
+    return 0;
+}
+
+int PageAcct::GetPageIdle(uint64_t pfn) const {
+    off64_t offset = pfn_to_idle_bitmap_offset(pfn);
+    uint64_t idle_bits;
+
+    if (pread64(pageidle_fd_, &idle_bits, sizeof(uint64_t), offset) != sizeof(uint64_t)) {
+        PLOG(ERROR) << "Failed to read page idle bitmap for page " << pfn;
+        return -errno;
+    }
+
+    return !!(idle_bits & (1ULL << (pfn % 64)));
+}
+
+// Public methods
+bool page_present(uint64_t pagemap_val) {
+    return PAGE_PRESENT(pagemap_val);
+}
+
+bool page_swapped(uint64_t pagemap_val) {
+    return PAGE_SWAPPED(pagemap_val);
+}
+
+uint64_t page_pfn(uint64_t pagemap_val) {
+    return PAGE_PFN(pagemap_val);
+}
+
+}  // namespace meminfo
+}  // namespace android
diff --git a/libmeminfo/procmeminfo.cpp b/libmeminfo/procmeminfo.cpp
new file mode 100644
index 0000000..a8b43c1
--- /dev/null
+++ b/libmeminfo/procmeminfo.cpp
@@ -0,0 +1,536 @@
+/*
+ * 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 <inttypes.h>
+#include <linux/kernel-page-flags.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <procinfo/process_map.h>
+
+#include "meminfo_private.h"
+
+namespace android {
+namespace meminfo {
+
+static void add_mem_usage(MemUsage* to, const MemUsage& from) {
+    to->vss += from.vss;
+    to->rss += from.rss;
+    to->pss += from.pss;
+    to->uss += from.uss;
+
+    to->swap += from.swap;
+
+    to->private_clean += from.private_clean;
+    to->private_dirty += from.private_dirty;
+
+    to->shared_clean += from.shared_clean;
+    to->shared_dirty += from.shared_dirty;
+}
+
+// Returns true if the line was valid smaps stats line false otherwise.
+static bool parse_smaps_field(const char* line, MemUsage* stats) {
+    char field[64];
+    int len;
+    if (sscanf(line, "%63s %n", field, &len) == 1 && *field && field[strlen(field) - 1] == ':') {
+        const char* c = line + len;
+        switch (field[0]) {
+            case 'P':
+                if (strncmp(field, "Pss:", 4) == 0) {
+                    stats->pss = strtoull(c, nullptr, 10);
+                } else if (strncmp(field, "Private_Clean:", 14) == 0) {
+                    uint64_t prcl = strtoull(c, nullptr, 10);
+                    stats->private_clean = prcl;
+                    stats->uss += prcl;
+                } else if (strncmp(field, "Private_Dirty:", 14) == 0) {
+                    uint64_t prdi = strtoull(c, nullptr, 10);
+                    stats->private_dirty = prdi;
+                    stats->uss += prdi;
+                }
+                break;
+            case 'S':
+                if (strncmp(field, "Size:", 5) == 0) {
+                    stats->vss = strtoull(c, nullptr, 10);
+                } else if (strncmp(field, "Shared_Clean:", 13) == 0) {
+                    stats->shared_clean = strtoull(c, nullptr, 10);
+                } else if (strncmp(field, "Shared_Dirty:", 13) == 0) {
+                    stats->shared_dirty = strtoull(c, nullptr, 10);
+                } else if (strncmp(field, "Swap:", 5) == 0) {
+                    stats->swap = strtoull(c, nullptr, 10);
+                } else if (strncmp(field, "SwapPss:", 8) == 0) {
+                    stats->swap_pss = strtoull(c, nullptr, 10);
+                }
+                break;
+            case 'R':
+                if (strncmp(field, "Rss:", 4) == 0) {
+                    stats->rss = strtoull(c, nullptr, 10);
+                }
+                break;
+        }
+        return true;
+    }
+
+    return false;
+}
+
+bool ProcMemInfo::ResetWorkingSet(pid_t pid) {
+    std::string clear_refs_path = ::android::base::StringPrintf("/proc/%d/clear_refs", pid);
+    if (!::android::base::WriteStringToFile("1\n", clear_refs_path)) {
+        PLOG(ERROR) << "Failed to write to " << clear_refs_path;
+        return false;
+    }
+
+    return true;
+}
+
+ProcMemInfo::ProcMemInfo(pid_t pid, bool get_wss, uint64_t pgflags, uint64_t pgflags_mask)
+    : pid_(pid), get_wss_(get_wss), pgflags_(pgflags), pgflags_mask_(pgflags_mask) {}
+
+const std::vector<Vma>& ProcMemInfo::Maps() {
+    if (maps_.empty() && !ReadMaps(get_wss_)) {
+        LOG(ERROR) << "Failed to read maps for Process " << pid_;
+    }
+
+    return maps_;
+}
+
+const std::vector<Vma>& ProcMemInfo::MapsWithPageIdle() {
+    if (maps_.empty() && !ReadMaps(get_wss_, true)) {
+        LOG(ERROR) << "Failed to read maps with page idle for Process " << pid_;
+    }
+
+    return maps_;
+}
+
+const std::vector<Vma>& ProcMemInfo::Smaps(const std::string& path) {
+    if (!maps_.empty()) {
+        return maps_;
+    }
+
+    auto collect_vmas = [&](const Vma& vma) { maps_.emplace_back(vma); };
+    if (path.empty() && !ForEachVma(collect_vmas)) {
+        LOG(ERROR) << "Failed to read smaps for Process " << pid_;
+        maps_.clear();
+    }
+
+    if (!path.empty() && !ForEachVmaFromFile(path, collect_vmas)) {
+        LOG(ERROR) << "Failed to read smaps from file " << path;
+        maps_.clear();
+    }
+
+    return maps_;
+}
+
+const MemUsage& ProcMemInfo::Usage() {
+    if (get_wss_) {
+        LOG(WARNING) << "Trying to read process memory usage for " << pid_
+                     << " using invalid object";
+        return usage_;
+    }
+
+    if (maps_.empty() && !ReadMaps(get_wss_)) {
+        LOG(ERROR) << "Failed to get memory usage for Process " << pid_;
+    }
+
+    return usage_;
+}
+
+const MemUsage& ProcMemInfo::Wss() {
+    if (!get_wss_) {
+        LOG(WARNING) << "Trying to read process working set for " << pid_
+                     << " using invalid object";
+        return usage_;
+    }
+
+    if (maps_.empty() && !ReadMaps(get_wss_)) {
+        LOG(ERROR) << "Failed to get working set for Process " << pid_;
+    }
+
+    return usage_;
+}
+
+bool ProcMemInfo::ForEachVma(const VmaCallback& callback) {
+    std::string path = ::android::base::StringPrintf("/proc/%d/smaps", pid_);
+    return ForEachVmaFromFile(path, callback);
+}
+
+bool ProcMemInfo::SmapsOrRollup(MemUsage* stats) const {
+    std::string path = ::android::base::StringPrintf(
+            "/proc/%d/%s", pid_, IsSmapsRollupSupported(pid_) ? "smaps_rollup" : "smaps");
+    return SmapsOrRollupFromFile(path, stats);
+}
+
+bool ProcMemInfo::SmapsOrRollupPss(uint64_t* pss) const {
+    std::string path = ::android::base::StringPrintf(
+            "/proc/%d/%s", pid_, IsSmapsRollupSupported(pid_) ? "smaps_rollup" : "smaps");
+    return SmapsOrRollupPssFromFile(path, pss);
+}
+
+const std::vector<uint16_t>& ProcMemInfo::SwapOffsets() {
+    if (get_wss_) {
+        LOG(WARNING) << "Trying to read process swap offsets for " << pid_
+                     << " using invalid object";
+        return swap_offsets_;
+    }
+
+    if (maps_.empty() && !ReadMaps(get_wss_)) {
+        LOG(ERROR) << "Failed to get swap offsets for Process " << pid_;
+    }
+
+    return swap_offsets_;
+}
+
+bool ProcMemInfo::PageMap(const Vma& vma, std::vector<uint64_t>* pagemap) {
+    pagemap->clear();
+    std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid_);
+    ::android::base::unique_fd pagemap_fd(
+            TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (pagemap_fd < 0) {
+        PLOG(ERROR) << "Failed to open " << pagemap_file;
+        return false;
+    }
+
+    uint64_t nr_pages = (vma.end - vma.start) / getpagesize();
+    pagemap->reserve(nr_pages);
+
+    uint64_t idx = vma.start / getpagesize();
+    uint64_t last = idx + nr_pages;
+    uint64_t val;
+    for (; idx < last; idx++) {
+        if (pread64(pagemap_fd, &val, sizeof(uint64_t), idx * sizeof(uint64_t)) < 0) {
+            PLOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_;
+            return false;
+        }
+        pagemap->emplace_back(val);
+    }
+
+    return true;
+}
+
+bool ProcMemInfo::ReadMaps(bool get_wss, bool use_pageidle) {
+    // Each object reads /proc/<pid>/maps only once. This is done to make sure programs that are
+    // running for the lifetime of the system can recycle the objects and don't have to
+    // unnecessarily retain and update this object in memory (which can get significantly large).
+    // E.g. A program that only needs to reset the working set will never all ->Maps(), ->Usage().
+    // E.g. A program that is monitoring smaps_rollup, may never call ->maps(), Usage(), so it
+    // doesn't make sense for us to parse and retain unnecessary memory accounting stats by default.
+    if (!maps_.empty()) return true;
+
+    // parse and read /proc/<pid>/maps
+    std::string maps_file = ::android::base::StringPrintf("/proc/%d/maps", pid_);
+    if (!::android::procinfo::ReadMapFile(
+                maps_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t,
+                               const char* name) {
+                    maps_.emplace_back(Vma(start, end, pgoff, flags, name));
+                })) {
+        LOG(ERROR) << "Failed to parse " << maps_file;
+        maps_.clear();
+        return false;
+    }
+
+    std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid_);
+    ::android::base::unique_fd pagemap_fd(
+            TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (pagemap_fd < 0) {
+        PLOG(ERROR) << "Failed to open " << pagemap_file;
+        return false;
+    }
+
+    for (auto& vma : maps_) {
+        if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss, use_pageidle)) {
+            LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start << "-"
+                       << vma.end << "]";
+            maps_.clear();
+            return false;
+        }
+        add_mem_usage(&usage_, vma.usage);
+    }
+
+    return true;
+}
+
+bool ProcMemInfo::ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss, bool use_pageidle) {
+    PageAcct& pinfo = PageAcct::Instance();
+    if (get_wss && use_pageidle && !pinfo.InitPageAcct(true)) {
+        LOG(ERROR) << "Failed to init idle page accounting";
+        return false;
+    }
+
+    uint64_t pagesz = getpagesize();
+    size_t num_pages = (vma.end - vma.start) / pagesz;
+    size_t first_page = vma.start / pagesz;
+
+    std::vector<uint64_t> page_cache;
+    size_t cur_page_cache_index = 0;
+    size_t num_in_page_cache = 0;
+    size_t num_leftover_pages = num_pages;
+    for (size_t cur_page = first_page; cur_page < first_page + num_pages; ++cur_page) {
+        if (!get_wss) {
+            vma.usage.vss += pagesz;
+        }
+
+        // Cache page map data.
+        if (cur_page_cache_index == num_in_page_cache) {
+            static constexpr size_t kMaxPages = 2048;
+            num_leftover_pages -= num_in_page_cache;
+            if (num_leftover_pages > kMaxPages) {
+                num_in_page_cache = kMaxPages;
+            } else {
+                num_in_page_cache = num_leftover_pages;
+            }
+            page_cache.resize(num_in_page_cache);
+            size_t total_bytes = page_cache.size() * sizeof(uint64_t);
+            ssize_t bytes = pread64(pagemap_fd, page_cache.data(), total_bytes,
+                                    cur_page * sizeof(uint64_t));
+            if (bytes != total_bytes) {
+                if (bytes == -1) {
+                    PLOG(ERROR) << "Failed to read page data at offset 0x" << std::hex
+                                << cur_page * sizeof(uint64_t);
+                } else {
+                    LOG(ERROR) << "Failed to read page data at offset 0x" << std::hex
+                               << cur_page * sizeof(uint64_t) << std::dec << " read bytes " << bytes
+                               << " expected bytes " << total_bytes;
+                }
+                return false;
+            }
+            cur_page_cache_index = 0;
+        }
+
+        uint64_t page_info = page_cache[cur_page_cache_index++];
+        if (!PAGE_PRESENT(page_info) && !PAGE_SWAPPED(page_info)) continue;
+
+        if (PAGE_SWAPPED(page_info)) {
+            vma.usage.swap += pagesz;
+            swap_offsets_.emplace_back(PAGE_SWAP_OFFSET(page_info));
+            continue;
+        }
+
+        uint64_t page_frame = PAGE_PFN(page_info);
+        uint64_t cur_page_flags;
+        if (!pinfo.PageFlags(page_frame, &cur_page_flags)) {
+            LOG(ERROR) << "Failed to get page flags for " << page_frame << " in process " << pid_;
+            swap_offsets_.clear();
+            return false;
+        }
+
+        // skip unwanted pages from the count
+        if ((cur_page_flags & pgflags_mask_) != pgflags_) continue;
+
+        uint64_t cur_page_counts;
+        if (!pinfo.PageMapCount(page_frame, &cur_page_counts)) {
+            LOG(ERROR) << "Failed to get page count for " << page_frame << " in process " << pid_;
+            swap_offsets_.clear();
+            return false;
+        }
+
+        // Page was unmapped between the presence check at the beginning of the loop and here.
+        if (cur_page_counts == 0) {
+            continue;
+        }
+
+        bool is_dirty = !!(cur_page_flags & (1 << KPF_DIRTY));
+        bool is_private = (cur_page_counts == 1);
+        // Working set
+        if (get_wss) {
+            bool is_referenced = use_pageidle ? (pinfo.IsPageIdle(page_frame) == 1)
+                                              : !!(cur_page_flags & (1 << KPF_REFERENCED));
+            if (!is_referenced) {
+                continue;
+            }
+            // This effectively makes vss = rss for the working set is requested.
+            // The libpagemap implementation returns vss > rss for
+            // working set, which doesn't make sense.
+            vma.usage.vss += pagesz;
+        }
+
+        vma.usage.rss += pagesz;
+        vma.usage.uss += is_private ? pagesz : 0;
+        vma.usage.pss += pagesz / cur_page_counts;
+        if (is_private) {
+            vma.usage.private_dirty += is_dirty ? pagesz : 0;
+            vma.usage.private_clean += is_dirty ? 0 : pagesz;
+        } else {
+            vma.usage.shared_dirty += is_dirty ? pagesz : 0;
+            vma.usage.shared_clean += is_dirty ? 0 : pagesz;
+        }
+    }
+    return true;
+}
+
+// Public APIs
+bool ForEachVmaFromFile(const std::string& path, const VmaCallback& callback) {
+    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+    if (fp == nullptr) {
+        return false;
+    }
+
+    char* line = nullptr;
+    bool parsing_vma = false;
+    ssize_t line_len;
+    size_t line_alloc = 0;
+    Vma vma;
+    while ((line_len = getline(&line, &line_alloc, fp.get())) > 0) {
+        // Make sure the line buffer terminates like a C string for ReadMapFile
+        line[line_len] = '\0';
+
+        if (parsing_vma) {
+            if (parse_smaps_field(line, &vma.usage)) {
+                // This was a stats field
+                continue;
+            }
+
+            // Done collecting stats, make the call back
+            callback(vma);
+            parsing_vma = false;
+        }
+
+        vma.clear();
+        // If it has, we are looking for the vma stats
+        // 00400000-00409000 r-xp 00000000 fc:00 426998  /usr/lib/gvfs/gvfsd-http
+        if (!::android::procinfo::ReadMapFileContent(
+                    line, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t,
+                              const char* name) {
+                        vma.start = start;
+                        vma.end = end;
+                        vma.flags = flags;
+                        vma.offset = pgoff;
+                        vma.name = name;
+                    })) {
+            LOG(ERROR) << "Failed to parse " << path;
+            return false;
+        }
+        parsing_vma = true;
+    }
+
+    // free getline() managed buffer
+    free(line);
+
+    if (parsing_vma) {
+        callback(vma);
+    }
+
+    return true;
+}
+
+enum smaps_rollup_support { UNTRIED, SUPPORTED, UNSUPPORTED };
+
+static std::atomic<smaps_rollup_support> g_rollup_support = UNTRIED;
+
+bool IsSmapsRollupSupported(pid_t pid) {
+    // Similar to OpenSmapsOrRollup checks from android_os_Debug.cpp, except
+    // the method only checks if rollup is supported and returns the status
+    // right away.
+    enum smaps_rollup_support rollup_support = g_rollup_support.load(std::memory_order_relaxed);
+    if (rollup_support != UNTRIED) {
+        return rollup_support == SUPPORTED;
+    }
+    std::string rollup_file = ::android::base::StringPrintf("/proc/%d/smaps_rollup", pid);
+    if (access(rollup_file.c_str(), F_OK | R_OK)) {
+        // No check for errno = ENOENT necessary here. The caller MUST fallback to
+        // using /proc/<pid>/smaps instead anyway.
+        g_rollup_support.store(UNSUPPORTED, std::memory_order_relaxed);
+        return false;
+    }
+
+    g_rollup_support.store(SUPPORTED, std::memory_order_relaxed);
+    LOG(INFO) << "Using smaps_rollup for pss collection";
+    return true;
+}
+
+bool SmapsOrRollupFromFile(const std::string& path, MemUsage* stats) {
+    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+    if (fp == nullptr) {
+        return false;
+    }
+
+    char* line = nullptr;
+    size_t line_alloc = 0;
+    stats->clear();
+    while (getline(&line, &line_alloc, fp.get()) > 0) {
+        switch (line[0]) {
+            case 'P':
+                if (strncmp(line, "Pss:", 4) == 0) {
+                    char* c = line + 4;
+                    stats->pss += strtoull(c, nullptr, 10);
+                } else if (strncmp(line, "Private_Clean:", 14) == 0) {
+                    char* c = line + 14;
+                    uint64_t prcl = strtoull(c, nullptr, 10);
+                    stats->private_clean += prcl;
+                    stats->uss += prcl;
+                } else if (strncmp(line, "Private_Dirty:", 14) == 0) {
+                    char* c = line + 14;
+                    uint64_t prdi = strtoull(c, nullptr, 10);
+                    stats->private_dirty += prdi;
+                    stats->uss += prdi;
+                }
+                break;
+            case 'R':
+                if (strncmp(line, "Rss:", 4) == 0) {
+                    char* c = line + 4;
+                    stats->rss += strtoull(c, nullptr, 10);
+                }
+                break;
+            case 'S':
+                if (strncmp(line, "SwapPss:", 8) == 0) {
+                    char* c = line + 8;
+                    stats->swap_pss += strtoull(c, nullptr, 10);
+                }
+                break;
+        }
+    }
+
+    // free getline() managed buffer
+    free(line);
+    return true;
+}
+
+bool SmapsOrRollupPssFromFile(const std::string& path, uint64_t* pss) {
+    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+    if (fp == nullptr) {
+        return false;
+    }
+    *pss = 0;
+    char* line = nullptr;
+    size_t line_alloc = 0;
+    while (getline(&line, &line_alloc, fp.get()) > 0) {
+        uint64_t v;
+        if (sscanf(line, "Pss: %" SCNu64 " kB", &v) == 1) {
+            *pss += v;
+        }
+    }
+
+    // free getline() managed buffer
+    free(line);
+    return true;
+}
+
+}  // namespace meminfo
+}  // namespace android
diff --git a/libmeminfo/sysmeminfo.cpp b/libmeminfo/sysmeminfo.cpp
new file mode 100644
index 0000000..5cfa6c3
--- /dev/null
+++ b/libmeminfo/sysmeminfo.cpp
@@ -0,0 +1,275 @@
+/*
+ * 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 <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <cctype>
+#include <cstdio>
+#include <fstream>
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <utility>
+#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>
+#include <android-base/unique_fd.h>
+
+#include "meminfo_private.h"
+
+namespace android {
+namespace meminfo {
+
+const std::vector<std::string> SysMemInfo::kDefaultSysMemInfoTags = {
+        SysMemInfo::kMemTotal,      SysMemInfo::kMemFree,        SysMemInfo::kMemBuffers,
+        SysMemInfo::kMemCached,     SysMemInfo::kMemShmem,       SysMemInfo::kMemSlab,
+        SysMemInfo::kMemSReclaim,   SysMemInfo::kMemSUnreclaim,  SysMemInfo::kMemSwapTotal,
+        SysMemInfo::kMemSwapFree,   SysMemInfo::kMemMapped,      SysMemInfo::kMemVmallocUsed,
+        SysMemInfo::kMemPageTables, SysMemInfo::kMemKernelStack,
+};
+
+bool SysMemInfo::ReadMemInfo(const std::string& path) {
+    return ReadMemInfo(SysMemInfo::kDefaultSysMemInfoTags, path,
+                       [&](const std::string& tag, uint64_t val) { mem_in_kb_[tag] = val; });
+}
+
+bool SysMemInfo::ReadMemInfo(std::vector<uint64_t>* out, const std::string& path) {
+    return ReadMemInfo(SysMemInfo::kDefaultSysMemInfoTags, out, path);
+}
+
+bool SysMemInfo::ReadMemInfo(const std::vector<std::string>& tags, std::vector<uint64_t>* out,
+                             const std::string& path) {
+    out->clear();
+    out->resize(tags.size());
+
+    return ReadMemInfo(tags, path, [&]([[maybe_unused]] const std::string& tag, uint64_t val) {
+        auto it = std::find(tags.begin(), tags.end(), tag);
+        if (it == tags.end()) {
+            LOG(ERROR) << "Tried to store invalid tag: " << tag;
+            return;
+        }
+        auto index = std::distance(tags.begin(), it);
+        // store the values in the same order as the tags
+        out->at(index) = val;
+    });
+}
+
+uint64_t SysMemInfo::ReadVmallocInfo() {
+    return ::android::meminfo::ReadVmallocInfo();
+}
+
+// TODO: Delete this function if it can't match up with the c-like implementation below.
+// Currently, this added about 50 % extra overhead on hikey.
+#if 0
+bool SysMemInfo::ReadMemInfo(const std::vector<std::string>& tags, const std::string& path) {
+    std::string buffer;
+    if (!::android::base::ReadFileToString(path, &buffer)) {
+        PLOG(ERROR) << "Failed to read : " << path;
+        return false;
+    }
+
+    uint32_t total_found = 0;
+    for (auto s = buffer.begin(); s < buffer.end() && total_found < tags.size();) {
+        for (auto& tag : tags) {
+            if (tag == std::string(s, s + tag.size())) {
+                s += tag.size();
+                while (isspace(*s)) s++;
+                auto num_start = s;
+                while (std::isdigit(*s)) s++;
+
+                std::string number(num_start, num_start + (s - num_start));
+                if (!::android::base::ParseUint(number, &mem_in_kb_[tag])) {
+                    LOG(ERROR) << "Failed to parse uint";
+                    return false;
+                }
+                total_found++;
+                break;
+            }
+        }
+        while (s < buffer.end() && *s != '\n') s++;
+        if (s < buffer.end()) s++;
+    }
+
+    return true;
+}
+
+#else
+bool SysMemInfo::ReadMemInfo(const std::vector<std::string>& tags, const std::string& path,
+                             std::function<void(const std::string&, uint64_t)> store_val) {
+    char buffer[4096];
+    int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
+    if (fd < 0) {
+        PLOG(ERROR) << "Failed to open file :" << path;
+        return false;
+    }
+
+    const int len = read(fd, buffer, sizeof(buffer) - 1);
+    close(fd);
+    if (len < 0) {
+        return false;
+    }
+
+    buffer[len] = '\0';
+    char* p = buffer;
+    uint32_t found = 0;
+    uint32_t lineno = 0;
+    bool zram_tag_found = false;
+    while (*p && found < tags.size()) {
+        for (auto& tag : tags) {
+            // Special case for "Zram:" tag that android_os_Debug and friends look
+            // up along with the rest of the numbers from /proc/meminfo
+            if (!zram_tag_found && tag == "Zram:") {
+                store_val(tag, mem_zram_kb());
+                zram_tag_found = true;
+                found++;
+                continue;
+            }
+
+            if (strncmp(p, tag.c_str(), tag.size()) == 0) {
+                p += tag.size();
+                while (*p == ' ') p++;
+                char* endptr = nullptr;
+                uint64_t val = strtoull(p, &endptr, 10);
+                if (p == endptr) {
+                    PLOG(ERROR) << "Failed to parse line:" << lineno + 1 << " in file: " << path;
+                    return false;
+                }
+                store_val(tag, val);
+                p = endptr;
+                found++;
+                break;
+            }
+        }
+
+        while (*p && *p != '\n') {
+            p++;
+        }
+        if (*p) p++;
+        lineno++;
+    }
+
+    return true;
+}
+#endif
+
+uint64_t SysMemInfo::mem_zram_kb(const std::string& zram_dev) {
+    uint64_t mem_zram_total = 0;
+    if (!zram_dev.empty()) {
+        if (!MemZramDevice(zram_dev, &mem_zram_total)) {
+            return 0;
+        }
+        return mem_zram_total / 1024;
+    }
+
+    constexpr uint32_t kMaxZramDevices = 256;
+    for (uint32_t i = 0; i < kMaxZramDevices; i++) {
+        std::string zram_dev = ::android::base::StringPrintf("/sys/block/zram%u/", i);
+        if (access(zram_dev.c_str(), F_OK)) {
+            // We assume zram devices appear in range 0-255 and appear always in sequence
+            // under /sys/block. So, stop looking for them once we find one is missing.
+            break;
+        }
+
+        uint64_t mem_zram_dev;
+        if (!MemZramDevice(zram_dev, &mem_zram_dev)) {
+            return 0;
+        }
+
+        mem_zram_total += mem_zram_dev;
+    }
+
+    return mem_zram_total / 1024;
+}
+
+bool SysMemInfo::MemZramDevice(const std::string& zram_dev, uint64_t* mem_zram_dev) {
+    std::string mmstat = ::android::base::StringPrintf("%s/%s", zram_dev.c_str(), "mm_stat");
+    auto mmstat_fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(mmstat.c_str(), "re"), fclose};
+    if (mmstat_fp != nullptr) {
+        // only if we do have mmstat, use it. Otherwise, fall through to trying out the old
+        // 'mem_used_total'
+        if (fscanf(mmstat_fp.get(), "%*" SCNu64 " %*" SCNu64 " %" SCNu64, mem_zram_dev) != 1) {
+            PLOG(ERROR) << "Malformed mm_stat file in: " << zram_dev;
+            return false;
+        }
+        return true;
+    }
+
+    std::string content;
+    if (::android::base::ReadFileToString(zram_dev + "mem_used_total", &content)) {
+        *mem_zram_dev = strtoull(content.c_str(), NULL, 10);
+        if (*mem_zram_dev == ULLONG_MAX) {
+            PLOG(ERROR) << "Malformed mem_used_total file for zram dev: " << zram_dev
+                        << " content: " << content;
+            return false;
+        }
+
+        return true;
+    }
+
+    LOG(ERROR) << "Can't find memory status under: " << zram_dev;
+    return false;
+}
+
+// Public methods
+uint64_t ReadVmallocInfo(const std::string& path) {
+    uint64_t vmalloc_total = 0;
+    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+    if (fp == nullptr) {
+        return vmalloc_total;
+    }
+
+    char* line = nullptr;
+    size_t line_alloc = 0;
+    while (getline(&line, &line_alloc, fp.get()) > 0) {
+        // We are looking for lines like
+        //
+        // 0x0000000000000000-0x0000000000000000   12288 drm_property_create_blob+0x44/0xec pages=2 vmalloc
+        // 0x0000000000000000-0x0000000000000000    8192 wlan_logging_sock_init_svc+0xf8/0x4f0 [wlan] pages=1 vmalloc
+        //
+        // Notice that if the caller is coming from a module, the kernel prints and extra
+        // "[module_name]" after the address and the symbol of the call site. This means we can't
+        // use the old sscanf() method of getting the # of pages.
+        char* p_start = strstr(line, "pages=");
+        if (p_start == nullptr) {
+            // we didn't find anything
+            continue;
+        }
+
+        uint64_t nr_pages;
+        if (sscanf(p_start, "pages=%" SCNu64 "", &nr_pages) == 1) {
+            vmalloc_total += (nr_pages * getpagesize());
+        }
+    }
+
+    free(line);
+
+    return vmalloc_total;
+}
+
+}  // namespace meminfo
+}  // namespace android
diff --git a/libmeminfo/testdata1/mm_stat b/libmeminfo/testdata1/mm_stat
new file mode 100644
index 0000000..32b325f
--- /dev/null
+++ b/libmeminfo/testdata1/mm_stat
@@ -0,0 +1 @@
+ 145674240 26801454 31236096        0 45772800     3042     1887      517
diff --git a/libmeminfo/testdata1/showmap_test.sh b/libmeminfo/testdata1/showmap_test.sh
new file mode 100755
index 0000000..8ee713e
--- /dev/null
+++ b/libmeminfo/testdata1/showmap_test.sh
@@ -0,0 +1,86 @@
+#! /system/bin/sh
+
+TESTDATA_PATH=/data/nativetest64/libmeminfo_test/testdata1
+SMAPS=$TESTDATA_PATH/smaps
+OUT1=$TMPDIR/1.txt
+OUT2=$TMPDIR/2.txt
+
+showmap -f $SMAPS > $OUT1
+showmap2 -f $SMAPS > $OUT2
+diff $OUT1 $OUT2 > /dev/null
+ret=$?
+if [[ $ret != 0 ]]; then
+    echo "fail: showmap -f <smaps>";
+else
+    echo "pass: showmap -f <smaps>"
+fi
+
+showmap -q -f $SMAPS > $OUT1
+showmap2 -q -f $SMAPS > $OUT2
+diff $OUT1 $OUT2 > /dev/null
+ret=$?
+if [[ $ret != 0 ]]; then
+    echo "fail: showmap -q -f <smaps>";
+else
+    echo "pass: showmap -q -f <smaps>"
+fi
+
+showmap -v -f $SMAPS > $OUT1
+showmap2 -v -f $SMAPS > $OUT2
+diff $OUT1 $OUT2 > /dev/null
+ret=$?
+if [[ $ret != 0 ]]; then
+    echo "fail: showmap -v -f <smaps>";
+else
+    echo "pass: showmap -v -f <smaps>"
+fi
+
+showmap -a -f $SMAPS > $OUT1
+showmap2 -a -f $SMAPS > $OUT2
+diff $OUT1 $OUT2 > /dev/null
+ret=$?
+if [[ $ret != 0 ]]; then
+    echo "fail: showmap -a -f <smaps>";
+else
+    echo "pass: showmap -a -f <smaps>"
+fi
+
+# Note that all tests from here down that have the option
+# '-a' added to the command are expected to fail as
+# 'showmap2' actually fixes the 64-bit address truncating
+# that was already happening with showmap
+showmap -a -t -f $SMAPS > $OUT1
+showmap2 -a -t -f $SMAPS > $OUT2
+diff $OUT1 $OUT2 > /dev/null
+ret=$?
+if [[ $ret != 0 ]]; then
+    echo "fail: showmap -a -t -f <smaps>";
+else
+    echo "pass: showmap -a -t -f <smaps>"
+fi
+
+showmap -a -t -v -f $SMAPS > $OUT1
+showmap2 -a -t -v -f $SMAPS > $OUT2
+diff $OUT1 $OUT2 > /dev/null
+ret=$?
+if [[ $ret != 0 ]]; then
+    echo "fail: showmap -a -t -v -f <smaps>";
+else
+    echo "pass: showmap -a -t -v -f <smaps>"
+fi
+
+# Note: This test again is expected to fail as the new
+# showmap fixes an issue with -t where the tool was only
+# showing maps with private dirty pages. The '-t' option was however
+# supposed to show all maps that have 'private' pages, clean or dirty.
+showmap -t -f $SMAPS > $OUT1
+showmap2 -t -f $SMAPS > $OUT2
+diff $OUT1 $OUT2 > /dev/null
+ret=$?
+if [[ $ret != 0 ]]; then
+    echo "fail: showmap -t -f <smaps>";
+else
+    echo "pass: showmap -t -f <smaps>"
+fi
+
+
diff --git a/libmeminfo/testdata1/smaps b/libmeminfo/testdata1/smaps
new file mode 100644
index 0000000..23aa2af
--- /dev/null
+++ b/libmeminfo/testdata1/smaps
@@ -0,0 +1,56297 @@
+12c00000-13440000 rw-p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:               8448 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                2652 kB
+Pss:                2652 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:      2652 kB
+Referenced:         2652 kB
+Anonymous:          2652 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             2652 kB
+VmFlags: rd wr mr mw me ac 
+13440000-13500000 ---p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:                768 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+13500000-13540000 rw-p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:                256 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 256 kB
+Pss:                 256 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       256 kB
+Referenced:          256 kB
+Anonymous:           256 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              256 kB
+VmFlags: rd wr mr mw me ac 
+13540000-137c0000 ---p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:               2560 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+137c0000-13880000 rw-p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:                768 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 740 kB
+Pss:                 740 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       740 kB
+Referenced:          740 kB
+Anonymous:           740 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              740 kB
+VmFlags: rd wr mr mw me ac 
+13880000-13900000 ---p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:                512 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+13900000-13940000 rw-p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:                256 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 256 kB
+Pss:                 256 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       256 kB
+Referenced:          256 kB
+Anonymous:           256 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              256 kB
+VmFlags: rd wr mr mw me ac 
+13940000-13a40000 ---p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:               1024 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+13a40000-13a80000 rw-p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:                256 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 256 kB
+Pss:                 256 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       256 kB
+Referenced:          256 kB
+Anonymous:           256 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              256 kB
+VmFlags: rd wr mr mw me ac 
+13a80000-13b40000 ---p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:                768 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+13b40000-13b80000 rw-p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:                256 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 256 kB
+Pss:                 256 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       256 kB
+Referenced:          256 kB
+Anonymous:           256 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              256 kB
+VmFlags: rd wr mr mw me ac 
+13b80000-13bc0000 ---p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:                256 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+13bc0000-13d80000 rw-p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:               1792 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                1792 kB
+Pss:                1792 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:      1792 kB
+Referenced:         1792 kB
+Anonymous:          1792 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             1792 kB
+VmFlags: rd wr mr mw me ac 
+13d80000-13dc0000 ---p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:                256 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+13dc0000-13e00000 rw-p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:                256 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 256 kB
+Pss:                 256 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       256 kB
+Referenced:          256 kB
+Anonymous:           256 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              256 kB
+VmFlags: rd wr mr mw me ac 
+13e00000-13e40000 ---p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:                256 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+13e40000-13e80000 rw-p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:                256 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 256 kB
+Pss:                 256 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       256 kB
+Referenced:          256 kB
+Anonymous:           256 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              256 kB
+VmFlags: rd wr mr mw me ac 
+13e80000-13ec0000 ---p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:                256 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+13ec0000-52c00000 rw-p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:            1029376 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 768 kB
+Pss:                 768 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       768 kB
+Referenced:          768 kB
+Anonymous:           768 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              768 kB
+VmFlags: rd wr mr mw me ac 
+52c00000-54c00000 rw-p 00000000 00:00 0                                  [anon:dalvik-zygote-data-code-cache]
+Name:           [anon:dalvik-zygote-data-code-cache]
+Size:              32768 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                2048 kB
+Pss:                 512 kB
+Shared_Clean:          0 kB
+Shared_Dirty:       2048 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:         2048 kB
+Anonymous:          2048 kB
+AnonHugePages:      2048 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              512 kB
+VmFlags: rd wr mr mw me ac 
+54c00000-56c00000 r-xp 00000000 00:00 0                                  [anon:dalvik-zygote-jit-code-cache]
+Name:           [anon:dalvik-zygote-jit-code-cache]
+Size:              32768 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                2048 kB
+Pss:                 113 kB
+Shared_Clean:          0 kB
+Shared_Dirty:       2048 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:         2048 kB
+Anonymous:          2048 kB
+AnonHugePages:      2048 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              113 kB
+VmFlags: rd ex mr mw me ac 
+56c00000-56c05000 rw-p 00000000 00:00 0                                  [anon:dalvik-large object space allocation]
+Name:           [anon:dalvik-large object space allocation]
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+56c05000-56c37000 rw-p 00000000 00:00 0                                  [anon:dalvik-large object space allocation]
+Name:           [anon:dalvik-large object space allocation]
+Size:                200 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 200 kB
+Pss:                  11 kB
+Shared_Clean:          0 kB
+Shared_Dirty:        200 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:           200 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               11 kB
+VmFlags: rd wr mr mw me ac 
+56c37000-56c45000 rw-p 00000000 00:00 0                                  [anon:dalvik-large object space allocation]
+Name:           [anon:dalvik-large object space allocation]
+Size:                 56 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+56c45000-56c4e000 rw-p 00000000 00:00 0                                  [anon:dalvik-large object space allocation]
+Name:           [anon:dalvik-large object space allocation]
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  36 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         36 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            36 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd wr mr mw me ac 
+56c52000-56c5c000 rw-p 00000000 00:00 0                                  [anon:dalvik-large object space allocation]
+Name:           [anon:dalvik-large object space allocation]
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  40 kB
+Pss:                   2 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         40 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            40 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd wr mr mw me ac 
+56c5c000-56c61000 rw-p 00000000 00:00 0                                  [anon:dalvik-large object space allocation]
+Name:           [anon:dalvik-large object space allocation]
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                  20 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        20 kB
+Referenced:           20 kB
+Anonymous:            20 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               20 kB
+VmFlags: rd wr mr mw me ac 
+56c63000-56c6e000 rw-p 00000000 00:00 0                                  [anon:dalvik-large object space allocation]
+Name:           [anon:dalvik-large object space allocation]
+Size:                 44 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  44 kB
+Pss:                   2 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         44 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            44 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd wr mr mw me ac 
+56c6e000-56c77000 rw-p 00000000 00:00 0                                  [anon:dalvik-large object space allocation]
+Name:           [anon:dalvik-large object space allocation]
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+56c79000-56d05000 rw-p 00000000 00:00 0                                  [anon:dalvik-large object space allocation]
+Name:           [anon:dalvik-large object space allocation]
+Size:                560 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 560 kB
+Pss:                  31 kB
+Shared_Clean:          0 kB
+Shared_Dirty:        560 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:           560 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               31 kB
+VmFlags: rd wr mr mw me ac 
+56d05000-56d0e000 rw-p 00000000 00:00 0                                  [anon:dalvik-large object space allocation]
+Name:           [anon:dalvik-large object space allocation]
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+56d10000-56d16000 rw-p 00000000 00:00 0                                  [anon:dalvik-large object space allocation]
+Name:           [anon:dalvik-large object space allocation]
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         24 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            24 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd wr mr mw me ac 
+56d1d000-56d21000 rw-p 00000000 00:00 0                                  [anon:dalvik-large object space allocation]
+Name:           [anon:dalvik-large object space allocation]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         16 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+56d21000-58d21000 rw-s 00000000 00:05 9572                               /memfd:/jit-cache (deleted)
+Size:              32768 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                  20 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        20 kB
+Referenced:           20 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               20 kB
+VmFlags: rd wr sh mr mw me ms 
+58d21000-5ad21000 r-xs 02000000 00:05 9572                               /memfd:/jit-cache (deleted)
+Size:              32768 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         24 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           24 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd ex sh mr mw me ms 
+5ad21000-5ae6f000 rw-p 00000000 00:00 0                                  [anon:dalvik-/system/framework/oat/x86_64/services.art]
+Name:           [anon:dalvik-/system/framework/oat/x86_64/services.art]
+Size:               1336 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                1336 kB
+Pss:                1336 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:      1336 kB
+Referenced:         1336 kB
+Anonymous:          1336 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             1336 kB
+VmFlags: rd wr mr mw me ac 
+5ae6f000-5aeb5000 rw-p 00000000 00:00 0                                  [anon:dalvik-large object space allocation]
+Name:           [anon:dalvik-large object space allocation]
+Size:                280 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 112 kB
+Pss:                 112 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       112 kB
+Referenced:          112 kB
+Anonymous:           112 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              112 kB
+VmFlags: rd wr mr mw me ac 
+5aeb6000-5aebf000 rw-p 00000000 00:00 0                                  [anon:dalvik-large object space allocation]
+Name:           [anon:dalvik-large object space allocation]
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me ac 
+6fc4d000-6ff1e000 rw-p 00000000 fe:00 3184                               /system/framework/x86_64/boot.art
+Size:               2884 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                2884 kB
+Pss:                 472 kB
+Shared_Clean:          4 kB
+Shared_Dirty:       2552 kB
+Private_Clean:         0 kB
+Private_Dirty:       328 kB
+Referenced:         2728 kB
+Anonymous:          2880 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              472 kB
+VmFlags: rd wr mr mw me ac 
+6ff1e000-70050000 rw-p 00000000 fe:00 3174                               /system/framework/x86_64/boot-core-libart.art
+Size:               1224 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                1212 kB
+Pss:                 234 kB
+Shared_Clean:          4 kB
+Shared_Dirty:       1032 kB
+Private_Clean:         0 kB
+Private_Dirty:       176 kB
+Referenced:         1092 kB
+Anonymous:          1208 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              234 kB
+VmFlags: rd wr mr mw me ac 
+70050000-70051000 rw-p 00000000 fe:00 3197                               /system/framework/x86_64/boot-core-simple.art
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70051000-70091000 rw-p 00000000 fe:00 3168                               /system/framework/x86_64/boot-conscrypt.art
+Size:                256 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 256 kB
+Pss:                  89 kB
+Shared_Clean:          4 kB
+Shared_Dirty:        172 kB
+Private_Clean:         0 kB
+Private_Dirty:        80 kB
+Referenced:          236 kB
+Anonymous:           252 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               89 kB
+VmFlags: rd wr mr mw me ac 
+70091000-700ce000 rw-p 00000000 fe:00 3166                               /system/framework/x86_64/boot-okhttp.art
+Size:                244 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 240 kB
+Pss:                  28 kB
+Shared_Clean:          0 kB
+Shared_Dirty:        224 kB
+Private_Clean:         0 kB
+Private_Dirty:        16 kB
+Referenced:          200 kB
+Anonymous:           240 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               28 kB
+VmFlags: rd wr mr mw me ac 
+700ce000-70136000 rw-p 00000000 fe:00 3175                               /system/framework/x86_64/boot-bouncycastle.art
+Size:                416 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 412 kB
+Pss:                  22 kB
+Shared_Clean:          0 kB
+Shared_Dirty:        412 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          236 kB
+Anonymous:           412 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               22 kB
+VmFlags: rd wr mr mw me ac 
+70136000-7019d000 rw-p 00000000 fe:00 3167                               /system/framework/x86_64/boot-apache-xml.art
+Size:                412 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 408 kB
+Pss:                  22 kB
+Shared_Clean:          0 kB
+Shared_Dirty:        408 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          276 kB
+Anonymous:           408 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               22 kB
+VmFlags: rd wr mr mw me ac 
+7019d000-701ea000 rw-p 00000000 fe:00 3196                               /system/framework/x86_64/boot-ext.art
+Size:                308 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 308 kB
+Pss:                  20 kB
+Shared_Clean:          4 kB
+Shared_Dirty:        300 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:          256 kB
+Anonymous:           304 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               20 kB
+VmFlags: rd wr mr mw me ac 
+701ea000-70cdb000 rw-p 00000000 fe:00 3165                               /system/framework/x86_64/boot-framework.art
+Size:              11204 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:               11188 kB
+Pss:                2200 kB
+Shared_Clean:         80 kB
+Shared_Dirty:       9448 kB
+Private_Clean:         0 kB
+Private_Dirty:      1660 kB
+Referenced:         9892 kB
+Anonymous:         11108 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             2200 kB
+VmFlags: rd wr mr mw me ac 
+70cdb000-70df1000 rw-p 00000000 fe:00 3178                               /system/framework/x86_64/boot-telephony-common.art
+Size:               1112 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                1112 kB
+Pss:                  63 kB
+Shared_Clean:          4 kB
+Shared_Dirty:       1108 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          524 kB
+Anonymous:          1108 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               63 kB
+VmFlags: rd wr mr mw me ac 
+70df1000-70e02000 rw-p 00000000 fe:00 3198                               /system/framework/x86_64/boot-voip-common.art
+Size:                 68 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  68 kB
+Pss:                   3 kB
+Shared_Clean:          4 kB
+Shared_Dirty:         64 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           28 kB
+Anonymous:            64 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd wr mr mw me ac 
+70e02000-70e1e000 rw-p 00000000 fe:00 3176                               /system/framework/x86_64/boot-ims-common.art
+Size:                112 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 112 kB
+Pss:                   6 kB
+Shared_Clean:          0 kB
+Shared_Dirty:        112 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           52 kB
+Anonymous:           112 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd wr mr mw me ac 
+70e1e000-70e20000 rw-p 00000000 fe:00 3201                               /system/framework/x86_64/boot-framework-oahl-backward-compatibility.art
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70e20000-70e26000 rw-p 00000000 fe:00 3170                               /system/framework/x86_64/boot-android.test.base.impl.art
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         24 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:            24 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd wr mr mw me ac 
+70e26000-70f04000 r--p 00000000 fe:00 3199                               /system/framework/x86_64/boot.oat
+Size:                888 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 856 kB
+Pss:                  66 kB
+Shared_Clean:        856 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          856 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               66 kB
+VmFlags: rd mr mw me 
+70f04000-71279000 r-xp 000de000 fe:00 3199                               /system/framework/x86_64/boot.oat
+Size:               3540 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                3476 kB
+Pss:                 308 kB
+Shared_Clean:       3412 kB
+Shared_Dirty:          0 kB
+Private_Clean:        64 kB
+Private_Dirty:         0 kB
+Referenced:         3476 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              308 kB
+VmFlags: rd ex mr mw me 
+71279000-7127a000 rw-p 00000000 00:00 0                                  [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7127a000-71700000 r--s 00000000 fe:00 3396                               /system/framework/boot.vdex
+Size:               4632 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                3592 kB
+Pss:                 423 kB
+Shared_Clean:       3592 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:         3592 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              423 kB
+VmFlags: rd mr me ms 
+71700000-71701000 r--p 00453000 fe:00 3199                               /system/framework/x86_64/boot.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+71701000-71702000 rw-p 00454000 fe:00 3199                               /system/framework/x86_64/boot.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+71702000-7175d000 r--p 00000000 fe:00 3181                               /system/framework/x86_64/boot-core-libart.oat
+Size:                364 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 356 kB
+Pss:                  20 kB
+Shared_Clean:        356 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          356 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               20 kB
+VmFlags: rd mr mw me 
+7175d000-718b6000 r-xp 0005b000 fe:00 3181                               /system/framework/x86_64/boot-core-libart.oat
+Size:               1380 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                1360 kB
+Pss:                 127 kB
+Shared_Clean:       1360 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:         1360 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              127 kB
+VmFlags: rd ex mr mw me 
+718b6000-718b7000 rw-p 00000000 00:00 0                                  [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+718b7000-71bcb000 r--s 00000000 fe:00 3146                               /system/framework/boot-core-libart.vdex
+Size:               3152 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                2228 kB
+Pss:                 238 kB
+Shared_Clean:       2228 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:         2228 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              238 kB
+VmFlags: rd mr me ms 
+71bcb000-71bcc000 r--p 001b4000 fe:00 3181                               /system/framework/x86_64/boot-core-libart.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+71bcc000-71bcd000 rw-p 001b5000 fe:00 3181                               /system/framework/x86_64/boot-core-libart.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+71bcd000-71bcf000 r--p 00000000 fe:00 3182                               /system/framework/x86_64/boot-core-simple.oat
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+71bcf000-71bd0000 r-xp 00002000 fe:00 3182                               /system/framework/x86_64/boot-core-simple.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+71bd0000-71bd1000 r--s 00000000 fe:00 3216                               /system/framework/boot-core-simple.vdex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+71bd1000-71bd2000 r--p 00003000 fe:00 3182                               /system/framework/x86_64/boot-core-simple.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+71bd2000-71bd3000 rw-p 00004000 fe:00 3182                               /system/framework/x86_64/boot-core-simple.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+71bd3000-71be2000 r--p 00000000 fe:00 3205                               /system/framework/x86_64/boot-conscrypt.oat
+Size:                 60 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  60 kB
+Pss:                   3 kB
+Shared_Clean:         60 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           60 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me 
+71be2000-71c12000 r-xp 0000f000 fe:00 3205                               /system/framework/x86_64/boot-conscrypt.oat
+Size:                192 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 184 kB
+Pss:                  25 kB
+Shared_Clean:        184 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          184 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               25 kB
+VmFlags: rd ex mr mw me 
+71c12000-71c13000 rw-p 00000000 00:00 0                                  [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+71c13000-71c78000 r--s 00000000 fe:00 3142                               /system/framework/boot-conscrypt.vdex
+Size:                404 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 404 kB
+Pss:                  47 kB
+Shared_Clean:        404 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          404 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               47 kB
+VmFlags: rd mr me ms 
+71c78000-71c79000 r--p 0003f000 fe:00 3205                               /system/framework/x86_64/boot-conscrypt.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+71c79000-71c7a000 rw-p 00040000 fe:00 3205                               /system/framework/x86_64/boot-conscrypt.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+71c7a000-71c8e000 r--p 00000000 fe:00 3183                               /system/framework/x86_64/boot-okhttp.oat
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  80 kB
+Pss:                  13 kB
+Shared_Clean:         80 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           80 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               13 kB
+VmFlags: rd mr mw me 
+71c8e000-71cd1000 r-xp 00014000 fe:00 3183                               /system/framework/x86_64/boot-okhttp.oat
+Size:                268 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 228 kB
+Pss:                 134 kB
+Shared_Clean:        188 kB
+Shared_Dirty:          0 kB
+Private_Clean:        40 kB
+Private_Dirty:         0 kB
+Referenced:          228 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              134 kB
+VmFlags: rd ex mr mw me 
+71cd1000-71cd2000 rw-p 00000000 00:00 0                                  [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+71cd2000-71d32000 r--s 00000000 fe:00 3135                               /system/framework/boot-okhttp.vdex
+Size:                384 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 312 kB
+Pss:                  71 kB
+Shared_Clean:        288 kB
+Shared_Dirty:          0 kB
+Private_Clean:        24 kB
+Private_Dirty:         0 kB
+Referenced:          312 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               71 kB
+VmFlags: rd mr me ms 
+71d32000-71d33000 r--p 00057000 fe:00 3183                               /system/framework/x86_64/boot-okhttp.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+71d33000-71d34000 rw-p 00058000 fe:00 3183                               /system/framework/x86_64/boot-okhttp.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+71d34000-71d48000 r--p 00000000 fe:00 3200                               /system/framework/x86_64/boot-bouncycastle.oat
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                   3 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me 
+71d48000-71d84000 r-xp 00014000 fe:00 3200                               /system/framework/x86_64/boot-bouncycastle.oat
+Size:                240 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 188 kB
+Pss:                  98 kB
+Shared_Clean:        180 kB
+Shared_Dirty:          0 kB
+Private_Clean:         8 kB
+Private_Dirty:         0 kB
+Referenced:          188 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               98 kB
+VmFlags: rd ex mr mw me 
+71d84000-71d85000 rw-p 00000000 00:00 0                                  [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+71d85000-71ec6000 r--s 00000000 fe:00 3145                               /system/framework/boot-bouncycastle.vdex
+Size:               1284 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 444 kB
+Pss:                  76 kB
+Shared_Clean:        444 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          444 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               76 kB
+VmFlags: rd mr me ms 
+71ec6000-71ec7000 r--p 00050000 fe:00 3200                               /system/framework/x86_64/boot-bouncycastle.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+71ec7000-71ec8000 rw-p 00051000 fe:00 3200                               /system/framework/x86_64/boot-bouncycastle.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+71ec8000-71ed2000 r--p 00000000 fe:00 3187                               /system/framework/x86_64/boot-apache-xml.oat
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  40 kB
+Pss:                   2 kB
+Shared_Clean:         40 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           40 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me 
+71ed2000-71eec000 r-xp 0000a000 fe:00 3187                               /system/framework/x86_64/boot-apache-xml.oat
+Size:                104 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+71eec000-71eed000 rw-p 00000000 00:00 0                                  [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+71eed000-72010000 r--s 00000000 fe:00 3212                               /system/framework/boot-apache-xml.vdex
+Size:               1164 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 320 kB
+Pss:                  35 kB
+Shared_Clean:        320 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          320 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               35 kB
+VmFlags: rd mr me ms 
+72010000-72011000 r--p 00024000 fe:00 3187                               /system/framework/x86_64/boot-apache-xml.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+72011000-72012000 rw-p 00025000 fe:00 3187                               /system/framework/x86_64/boot-apache-xml.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+72012000-7201d000 r--p 00000000 fe:00 3188                               /system/framework/x86_64/boot-ext.oat
+Size:                 44 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  44 kB
+Pss:                   2 kB
+Shared_Clean:         44 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           44 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me 
+7201d000-72038000 r-xp 0000b000 fe:00 3188                               /system/framework/x86_64/boot-ext.oat
+Size:                108 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 108 kB
+Pss:                  37 kB
+Shared_Clean:        108 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          108 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               37 kB
+VmFlags: rd ex mr mw me 
+72038000-72039000 rw-p 00000000 00:00 0                                  [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+72039000-72124000 r--s 00000000 fe:00 3221                               /system/framework/boot-ext.vdex
+Size:                940 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 340 kB
+Pss:                  48 kB
+Shared_Clean:        340 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          340 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               48 kB
+VmFlags: rd mr me ms 
+72124000-72125000 r--p 00026000 fe:00 3188                               /system/framework/x86_64/boot-ext.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+72125000-72126000 rw-p 00027000 fe:00 3188                               /system/framework/x86_64/boot-ext.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+72126000-7240e000 r--p 00000000 fe:00 3189                               /system/framework/x86_64/boot-framework.oat
+Size:               2976 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                2944 kB
+Pss:                 349 kB
+Shared_Clean:       2944 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:         2944 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              349 kB
+VmFlags: rd mr mw me 
+7240e000-72ee4000 r-xp 002e8000 fe:00 3189                               /system/framework/x86_64/boot-framework.oat
+Size:              11096 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:               10444 kB
+Pss:                1678 kB
+Shared_Clean:      10252 kB
+Shared_Dirty:          0 kB
+Private_Clean:       192 kB
+Private_Dirty:         0 kB
+Referenced:        10444 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             1678 kB
+VmFlags: rd ex mr mw me 
+72ee4000-72ee6000 rw-p 00000000 00:00 0                                  [anon:.bss]
+Name:           [anon:.bss]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+72ee6000-7449a000 r--s 00000000 fe:00 3156                               /system/framework/boot-framework.vdex
+Size:              22224 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:               16932 kB
+Pss:                2196 kB
+Shared_Clean:      16872 kB
+Shared_Dirty:          0 kB
+Private_Clean:        60 kB
+Private_Dirty:         0 kB
+Referenced:        16932 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             2196 kB
+VmFlags: rd mr me ms 
+7449a000-7449b000 r--p 00dbe000 fe:00 3189                               /system/framework/x86_64/boot-framework.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+7449b000-7449c000 rw-p 00dbf000 fe:00 3189                               /system/framework/x86_64/boot-framework.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7449c000-744f8000 r--p 00000000 fe:00 3202                               /system/framework/x86_64/boot-telephony-common.oat
+Size:                368 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  80 kB
+Pss:                   5 kB
+Shared_Clean:         80 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           80 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                5 kB
+VmFlags: rd mr mw me 
+744f8000-7463c000 r-xp 0005c000 fe:00 3202                               /system/framework/x86_64/boot-telephony-common.oat
+Size:               1296 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7463c000-7463d000 rw-p 00000000 00:00 0                                  [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7463d000-74974000 r--s 00000000 fe:00 3209                               /system/framework/boot-telephony-common.vdex
+Size:               3292 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 344 kB
+Pss:                  21 kB
+Shared_Clean:        344 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          344 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               21 kB
+VmFlags: rd mr me ms 
+74974000-74975000 r--p 001a0000 fe:00 3202                               /system/framework/x86_64/boot-telephony-common.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+74975000-74976000 rw-p 001a1000 fe:00 3202                               /system/framework/x86_64/boot-telephony-common.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+74976000-74978000 r--p 00000000 fe:00 3193                               /system/framework/x86_64/boot-voip-common.oat
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+74978000-7497a000 r-xp 00002000 fe:00 3193                               /system/framework/x86_64/boot-voip-common.oat
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7497a000-7497b000 rw-p 00000000 00:00 0                                  [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7497b000-749a1000 r--s 00000000 fe:00 3397                               /system/framework/boot-voip-common.vdex
+Size:                152 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 144 kB
+Pss:                  14 kB
+Shared_Clean:        144 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          144 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               14 kB
+VmFlags: rd mr me ms 
+749a1000-749a2000 r--p 00004000 fe:00 3193                               /system/framework/x86_64/boot-voip-common.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+749a2000-749a3000 rw-p 00005000 fe:00 3193                               /system/framework/x86_64/boot-voip-common.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+749a3000-749a9000 r--p 00000000 fe:00 3191                               /system/framework/x86_64/boot-ims-common.oat
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                   1 kB
+Shared_Clean:         20 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           20 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+749a9000-749b7000 r-xp 00006000 fe:00 3191                               /system/framework/x86_64/boot-ims-common.oat
+Size:                 56 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+749b7000-749b8000 rw-p 00000000 00:00 0                                  [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+749b8000-749db000 r--s 00000000 fe:00 3152                               /system/framework/boot-ims-common.vdex
+Size:                140 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 104 kB
+Pss:                   6 kB
+Shared_Clean:        104 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          104 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd mr me ms 
+749db000-749dc000 r--p 00014000 fe:00 3191                               /system/framework/x86_64/boot-ims-common.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+749dc000-749dd000 rw-p 00015000 fe:00 3191                               /system/framework/x86_64/boot-ims-common.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+749dd000-749df000 r--p 00000000 fe:00 3194                               /system/framework/x86_64/boot-framework-oahl-backward-compatibility.oat
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+749df000-749e0000 r-xp 00002000 fe:00 3194                               /system/framework/x86_64/boot-framework-oahl-backward-compatibility.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+749e0000-749e1000 r--s 00000000 fe:00 3147                               /system/framework/boot-framework-oahl-backward-compatibility.vdex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+749e1000-749e2000 r--p 00003000 fe:00 3194                               /system/framework/x86_64/boot-framework-oahl-backward-compatibility.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+749e2000-749e3000 rw-p 00004000 fe:00 3194                               /system/framework/x86_64/boot-framework-oahl-backward-compatibility.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+749e3000-749e5000 r--p 00000000 fe:00 3180                               /system/framework/x86_64/boot-android.test.base.impl.oat
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+749e5000-749e6000 r-xp 00002000 fe:00 3180                               /system/framework/x86_64/boot-android.test.base.impl.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+749e6000-749ee000 r--s 00000000 fe:00 3350                               /system/framework/boot-android.test.base.impl.vdex
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+749ee000-749ef000 r--p 00003000 fe:00 3180                               /system/framework/x86_64/boot-android.test.base.impl.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+749ef000-749f0000 rw-p 00004000 fe:00 3180                               /system/framework/x86_64/boot-android.test.base.impl.oat
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+749f0000-74b3d000 rw-p 00000000 00:00 0                                  [anon:dalvik-zygote space]
+Name:           [anon:dalvik-zygote space]
+Size:               1332 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                1332 kB
+Pss:                 497 kB
+Shared_Clean:          0 kB
+Shared_Dirty:        884 kB
+Private_Clean:         0 kB
+Private_Dirty:       448 kB
+Referenced:          564 kB
+Anonymous:          1332 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              497 kB
+VmFlags: rd wr mr mw me ac 
+74b3d000-74b3e000 rw-p 00000000 00:00 0                                  [anon:dalvik-non moving space]
+Name:           [anon:dalvik-non moving space]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+74b3e000-74b71000 rw-p 00000000 00:00 0                                  [anon:dalvik-non moving space]
+Name:           [anon:dalvik-non moving space]
+Size:                204 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 204 kB
+Pss:                 204 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       204 kB
+Referenced:          204 kB
+Anonymous:           204 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              204 kB
+VmFlags: rd wr mr mw me ac 
+74b71000-781f1000 ---p 00000000 00:00 0                                  [anon:dalvik-non moving space]
+Name:           [anon:dalvik-non moving space]
+Size:              55808 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+781f1000-789f0000 rw-p 00000000 00:00 0                                  [anon:dalvik-non moving space]
+Name:           [anon:dalvik-non moving space]
+Size:               8188 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+ebad6000-ebad7000 ---p 00000000 00:00 0                                  [anon:dalvik-Sentinel fault page]
+Name:           [anon:dalvik-Sentinel fault page]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+578de5770000-578de5773000 r--p 00000000 fe:00 408                        /system/bin/app_process64
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   1 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me dw 
+578de5773000-578de5777000 r-xp 00003000 fe:00 408                        /system/bin/app_process64
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                   0 kB
+Shared_Clean:         16 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me dw 
+578de5777000-578de5778000 r--p 00007000 fe:00 408                        /system/bin/app_process64
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me dw ac 
+578de5778000-578de577a000 rw-p 00000000 00:00 0 
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700749747000-7007497f6000 rw-s 00000000 00:05 12329                      /dev/ashmem/gralloc-1332.3 (deleted)
+Size:                700 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 656 kB
+Pss:                 656 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       656 kB
+Referenced:          656 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              656 kB
+VmFlags: rd wr sh mr mw me ms 
+7007497f6000-7007497f7000 ---s 000af000 00:05 12329                      /dev/ashmem/gralloc-1332.3 (deleted)
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: sh mr mw me ms 
+7007497f7000-7007498a6000 rw-s 00000000 00:05 12330                      /dev/ashmem/gralloc-1332.4 (deleted)
+Size:                700 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 656 kB
+Pss:                 656 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       656 kB
+Referenced:          656 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              656 kB
+VmFlags: rd wr sh mr mw me ms 
+7007498a6000-7007498a7000 ---s 000af000 00:05 12330                      /dev/ashmem/gralloc-1332.4 (deleted)
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: sh mr mw me ms 
+7007498a7000-700749956000 rw-s 00000000 00:05 12331                      /dev/ashmem/gralloc-1332.5 (deleted)
+Size:                700 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 656 kB
+Pss:                 328 kB
+Shared_Clean:          0 kB
+Shared_Dirty:        656 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          656 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              328 kB
+VmFlags: rd wr sh mr mw me ms 
+700749956000-700749957000 ---s 000af000 00:05 12331                      /dev/ashmem/gralloc-1332.5 (deleted)
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: sh mr mw me ms 
+700749957000-70074995b000 r--p 00000000 fe:30 346                        /vendor/lib64/hw/gralloc.vsoc.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                   2 kB
+Shared_Clean:         16 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me 
+70074995b000-70074995e000 r-xp 00004000 fe:30 346                        /vendor/lib64/hw/gralloc.vsoc.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   1 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd ex mr mw me 
+70074995e000-70074995f000 rw-p 00007000 fe:30 346                        /vendor/lib64/hw/gralloc.vsoc.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70074995f000-700749960000 r--p 00008000 fe:30 346                        /vendor/lib64/hw/gralloc.vsoc.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+700749960000-700749961000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700749981000-70074998a000 r--p 00000000 fe:30 381                        /vendor/lib64/vsoc_lib.so
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  36 kB
+Pss:                   3 kB
+Shared_Clean:         36 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           36 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me 
+70074998a000-700749992000 r-xp 00009000 fe:30 381                        /vendor/lib64/vsoc_lib.so
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700749992000-700749993000 rw-p 00011000 fe:30 381                        /vendor/lib64/vsoc_lib.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700749993000-700749994000 r--p 00012000 fe:30 381                        /vendor/lib64/vsoc_lib.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+700749994000-700749995000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007499f7000-70074a5f7000 rw-p 00000000 00:00 0                          [anon:libc_malloc]
+Name:           [anon:libc_malloc]
+Size:              12288 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70074a909000-70074a90a000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70074a90a000-70074a90b000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70074a90b000-70074aa0f000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+70074aa0f000-70074ab0f000 rw-s 00000000 00:05 11658                      /dev/ashmem/MemoryHeapBase (deleted)
+Size:               1024 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        16 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd wr sh mr mw me ms 
+70074ab0f000-70074ac0f000 rw-s 00000000 00:05 11656                      /dev/ashmem/MemoryHeapBase (deleted)
+Size:               1024 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr sh mr mw me ms 
+70074ac0f000-70074c78d000 r--s 00bc3000 fe:00 3346                       /system/framework/framework-res.apk
+Size:              28152 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:               19268 kB
+Pss:                 933 kB
+Shared_Clean:      19268 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:        19268 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              933 kB
+VmFlags: rd mr me ms 
+70074c78d000-70074c78e000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70074c78e000-70074c78f000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70074c78f000-70074c893000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+70074c893000-70074d293000 rw-p 00000000 00:00 0                          [anon:libc_malloc]
+Name:           [anon:libc_malloc]
+Size:              10240 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 536 kB
+Pss:                 536 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       536 kB
+Referenced:          208 kB
+Anonymous:           536 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              536 kB
+VmFlags: rd wr mr mw me ac 
+70074d293000-70074d294000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70074d294000-70074d295000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70074d295000-70074d399000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+70074d399000-70074db99000 rw-p 00000000 00:00 0                          [anon:libc_malloc]
+Name:           [anon:libc_malloc]
+Size:               8192 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70074db99000-70074db9a000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70074db9a000-70074dc93000 rw-p 00000000 00:00 0 
+Size:                996 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me nr 
+70074dc93000-70074dc94000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70074dc94000-70074dd8d000 rw-p 00000000 00:00 0 
+Size:                996 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+70074dd8d000-70074ee0d000 rw-p 00000000 00:00 0                          [anon:libc_malloc]
+Name:           [anon:libc_malloc]
+Size:              16896 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:               15272 kB
+Pss:               15272 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:     15272 kB
+Referenced:        11156 kB
+Anonymous:         15272 kB
+AnonHugePages:      6144 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:            15272 kB
+VmFlags: rd wr mr mw me ac 
+70074ee0d000-70074ee0e000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70074ee0e000-70074ee0f000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70074ee0f000-70074ef13000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+70074ef13000-70074f493000 rw-p 00000000 00:00 0                          [anon:libc_malloc]
+Name:           [anon:libc_malloc]
+Size:               5632 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                5348 kB
+Pss:                5348 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:      5348 kB
+Referenced:         3364 kB
+Anonymous:          5348 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             5348 kB
+VmFlags: rd wr mr mw me ac 
+70074f493000-70074f8a0000 r--s 001a6000 fe:00 1821                       /system/priv-app/Telecom/Telecom.apk
+Size:               4148 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         4 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr me ms 
+70074f8e1000-70074f9e1000 rw-s 00000000 00:05 11617                      /dev/ashmem/MemoryHeapBase (deleted)
+Size:               1024 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr sh mr mw me ms 
+70074f9e1000-70074fae1000 rw-s 00000000 00:05 11565                      /dev/ashmem/MemoryHeapBase (deleted)
+Size:               1024 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr sh mr mw me ms 
+70074fae1000-70074fae2000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70074fae2000-70074fae3000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70074fae3000-70074fbe7000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+70074fbe7000-70074fca2000 r--s 0287a000 fe:00 3346                       /system/framework/framework-res.apk
+Size:                748 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 748 kB
+Pss:                 160 kB
+Shared_Clean:        748 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          748 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              160 kB
+VmFlags: rd mr me ms 
+70074fca2000-70074fca4000 r--p 00000000 fe:00 2040                       /system/priv-app/FusedLocation/oat/x86_64/FusedLocation.odex
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         8 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me 
+70074fca4000-70074fca7000 r-xp 00002000 fe:00 2040                       /system/priv-app/FusedLocation/oat/x86_64/FusedLocation.odex
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        12 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd ex mr mw me 
+70074fca7000-70074fca8000 r--p 00005000 fe:00 2040                       /system/priv-app/FusedLocation/oat/x86_64/FusedLocation.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+70074fca8000-70074fca9000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70074fca9000-70074fcaa000 r--s 00000000 fe:00 2039                       /system/priv-app/FusedLocation/oat/x86_64/FusedLocation.vdex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         4 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr me ms 
+70074fcaa000-70074fcab000 r--p 00006000 fe:00 2040                       /system/priv-app/FusedLocation/oat/x86_64/FusedLocation.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         4 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me 
+70074fcab000-70074fcac000 rw-p 00007000 fe:00 2040                       /system/priv-app/FusedLocation/oat/x86_64/FusedLocation.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70074fcef000-70074fcf2000 r--p 00000000 fe:00 3277                       /system/framework/oat/x86_64/com.android.location.provider.impl.odex
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   6 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd mr mw me 
+70074fcf2000-70074fcf6000 r-xp 00003000 fe:00 3277                       /system/framework/oat/x86_64/com.android.location.provider.impl.odex
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        16 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd ex mr mw me 
+70074fcf6000-70074fcf7000 r--p 00007000 fe:00 3277                       /system/framework/oat/x86_64/com.android.location.provider.impl.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+70074fcf7000-70074fcf8000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70074fcf8000-70074fcfd000 r--s 00000000 fe:00 3249                       /system/framework/oat/x86_64/com.android.location.provider.impl.vdex
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                  10 kB
+Shared_Clean:         20 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           20 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               10 kB
+VmFlags: rd mr me ms 
+70074fcfd000-70074fcfe000 r--p 00008000 fe:00 3277                       /system/framework/oat/x86_64/com.android.location.provider.impl.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   2 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me 
+70074fcfe000-70074fcff000 rw-p 00009000 fe:00 3277                       /system/framework/oat/x86_64/com.android.location.provider.impl.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70074fd15000-70074fe15000 rw-s 00000000 00:05 11532                      /dev/ashmem/MemoryHeapBase (deleted)
+Size:               1024 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr sh mr mw me ms 
+70074fe15000-70074fe16000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70074fe16000-70074fe17000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70074fe17000-70074ff1b000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+70074ff1b000-70074ffa5000 r--p 00000000 fe:00 1819                       /system/priv-app/Telecom/oat/x86_64/Telecom.odex
+Size:                552 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  72 kB
+Pss:                  72 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        72 kB
+Private_Dirty:         0 kB
+Referenced:           72 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               72 kB
+VmFlags: rd mr mw me 
+70074ffa5000-7007501f2000 r-xp 0008a000 fe:00 1819                       /system/priv-app/Telecom/oat/x86_64/Telecom.odex
+Size:               2356 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                1784 kB
+Pss:                1784 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:      1784 kB
+Private_Dirty:         0 kB
+Referenced:         1784 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             1784 kB
+VmFlags: rd ex mr mw me 
+7007501f2000-7007501f4000 r--p 002d7000 fe:00 1819                       /system/priv-app/Telecom/oat/x86_64/Telecom.odex
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+7007501f4000-7007501f9000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                  20 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        20 kB
+Referenced:           20 kB
+Anonymous:            20 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               20 kB
+VmFlags: rd wr mr mw me ac 
+7007501f9000-7007501fe000 r--s 00000000 fe:00 1820                       /system/priv-app/Telecom/oat/x86_64/Telecom.vdex
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                  20 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        20 kB
+Private_Dirty:         0 kB
+Referenced:           20 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               20 kB
+VmFlags: rd mr me ms 
+7007501fe000-7007501ff000 r--p 002d9000 fe:00 1819                       /system/priv-app/Telecom/oat/x86_64/Telecom.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         4 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me 
+7007501ff000-700750200000 rw-p 002da000 fe:00 1819                       /system/priv-app/Telecom/oat/x86_64/Telecom.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700750296000-700750297000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700750297000-700750298000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700750298000-70075039c000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+70075039c000-70075039d000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075039d000-70075039e000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075039e000-700750496000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700750496000-70075056f000 r--s 003f5000 fe:00 1805                       /system/priv-app/Launcher3QuickStep/Launcher3QuickStep.apk
+Size:                868 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 384 kB
+Pss:                 192 kB
+Shared_Clean:        384 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          384 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              192 kB
+VmFlags: rd mr me ms 
+7007509c2000-700750dc2000 rw-p 00000000 00:00 0                          [anon:libc_malloc]
+Name:           [anon:libc_malloc]
+Size:               4096 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                1980 kB
+Pss:                1980 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:      1980 kB
+Referenced:          176 kB
+Anonymous:          1980 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             1980 kB
+VmFlags: rd wr mr mw me ac 
+70075185d000-70075185e000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075185e000-70075185f000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075185f000-700751957000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700751adb000-700751adc000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700751adc000-700751add000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700751add000-700751be1000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                  28 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        28 kB
+Referenced:           28 kB
+Anonymous:            28 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               28 kB
+VmFlags: rd wr mr mw me nr 
+700751d30000-700751d31000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700751d31000-700751d32000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700751d32000-700751e2a000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700751e50000-700751e51000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700751e51000-700751e52000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700751e52000-700751f56000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700751f56000-700751f57000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700751f57000-700751f58000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700751f58000-70075205c000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+70075205c000-70075205d000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075205d000-70075205e000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075205e000-700752162000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700752162000-700752163000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752163000-700752164000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752164000-70075225c000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+70075225c000-70075225d000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075225d000-70075225e000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075225e000-700752362000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700752362000-700752363000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752363000-700752364000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752364000-700752468000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+700752474000-700752475000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752475000-700752476000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752476000-70075256e000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+70075256e000-70075266e000 rw-s 00000000 00:05 11287                      /dev/ashmem/MemoryHeapBase (deleted)
+Size:               1024 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                  20 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        20 kB
+Referenced:           20 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               20 kB
+VmFlags: rd wr sh mr mw me ms 
+70075266e000-70075266f000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075266f000-700752670000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752670000-700752774000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+700752780000-700752781000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752781000-700752782000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752782000-70075287a000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+70075287a000-70075287b000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075287b000-70075287c000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075287c000-700752980000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+700752980000-700752981000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752981000-700752982000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752982000-700752a7a000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700752a7a000-700752a7b000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752a7b000-700752a7c000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752a7c000-700752b74000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+700752b74000-700752b75000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752b75000-700752b76000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752b76000-700752c6e000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        16 kB
+Referenced:           16 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd wr mr mw me nr 
+700752cb5000-700752cb8000 r--p 00000000 fe:00 1482                       /system/lib64/vndk-sp-Q/hw/android.hidl.memory@1.0-impl.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   6 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd mr mw me 
+700752cb8000-700752cb9000 r-xp 00003000 fe:00 1482                       /system/lib64/vndk-sp-Q/hw/android.hidl.memory@1.0-impl.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   2 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd ex mr mw me 
+700752cb9000-700752cba000 rw-p 00004000 fe:00 1482                       /system/lib64/vndk-sp-Q/hw/android.hidl.memory@1.0-impl.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700752cba000-700752cbb000 r--p 00005000 fe:00 1482                       /system/lib64/vndk-sp-Q/hw/android.hidl.memory@1.0-impl.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+700752cbb000-700752cbc000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700752cf5000-700752cf6000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752cf6000-700752cf7000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752cf7000-700752dfb000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+700752dfb000-700752dfc000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752dfc000-700752dfd000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752dfd000-700752ef5000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        16 kB
+Referenced:           16 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd wr mr mw me nr 
+700752ef5000-700752ef6000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752ef6000-700752ef7000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752ef7000-700752fef000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        16 kB
+Referenced:           16 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd wr mr mw me nr 
+700752fef000-700752ff0000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752ff0000-700752ff1000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700752ff1000-7007530e9000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        16 kB
+Referenced:           16 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd wr mr mw me nr 
+700753145000-700753148000 r--p 00000000 fe:30 382                        /vendor/lib64/libcuttlefish_fs.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   1 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+700753148000-70075314d000 r-xp 00003000 fe:30 382                        /vendor/lib64/libcuttlefish_fs.so
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+70075314d000-70075314e000 rw-p 00008000 fe:30 382                        /vendor/lib64/libcuttlefish_fs.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70075314e000-70075314f000 r--p 00009000 fe:30 382                        /vendor/lib64/libcuttlefish_fs.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+70075319d000-70075319e000 r--p 00000000 fe:30 408                        /vendor/lib64/cuttlefish_auto_resources.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+70075319e000-70075319f000 r-xp 00001000 fe:30 408                        /vendor/lib64/cuttlefish_auto_resources.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+70075319f000-7007531a0000 rw-p 00002000 fe:30 408                        /vendor/lib64/cuttlefish_auto_resources.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007531a0000-7007531a1000 r--p 00003000 fe:30 408                        /vendor/lib64/cuttlefish_auto_resources.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007531e3000-7007531e4000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007531e4000-7007531e5000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007531e5000-7007532e9000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        16 kB
+Referenced:           16 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd wr mr mw me nr 
+7007532e9000-700753669000 rw-p 00000000 00:00 0                          [anon:libc_malloc]
+Name:           [anon:libc_malloc]
+Size:               3584 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                3060 kB
+Pss:                3060 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:      3060 kB
+Referenced:         1816 kB
+Anonymous:          3060 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             3060 kB
+VmFlags: rd wr mr mw me ac 
+700753669000-70075366a000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075366a000-70075366b000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075366b000-70075376f000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+70075376f000-700753770000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700753770000-700753771000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700753771000-700753875000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700753875000-700753876000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700753876000-700753877000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700753877000-70075397b000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+70075397b000-70075397c000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075397c000-70075397d000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075397d000-700753a81000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+700753a81000-700753a82000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700753a82000-700753a83000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700753a83000-700753b87000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700753b87000-700753b88000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700753b88000-700753b89000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700753b89000-700753c8d000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+700753c8d000-700753c8e000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700753c8e000-700753c8f000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700753c8f000-700753d93000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+700753d93000-700753d94000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700753d94000-700753d95000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700753d95000-700753e99000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700753e99000-700753e9a000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700753e9a000-700753e9b000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700753e9b000-700753f9f000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                  28 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        28 kB
+Referenced:           28 kB
+Anonymous:            28 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               28 kB
+VmFlags: rd wr mr mw me nr 
+700753f9f000-700753fa0000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700753fa0000-700753fa1000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700753fa1000-700754099000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+700754099000-70075409a000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075409a000-70075409b000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075409b000-70075419f000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+70075419f000-7007541a0000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007541a0000-7007541a1000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007541a1000-7007542a5000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+7007542a5000-7007542a6000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007542a6000-7007542a7000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007542a7000-7007543ab000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+7007543ab000-7007543ac000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007543ac000-7007543ad000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007543ad000-7007544b1000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+7007544b1000-7007544b2000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007544b2000-7007544b3000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007544b3000-7007545b7000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+7007545b7000-7007545b8000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007545b8000-7007545b9000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007545b9000-7007546bd000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+7007546bd000-7007546be000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007546be000-7007546bf000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007546bf000-7007547c3000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        16 kB
+Referenced:           16 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd wr mr mw me nr 
+7007547c3000-7007547c4000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007547c4000-7007547c5000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007547c5000-7007548c9000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+7007548c9000-7007548ca000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007548ca000-7007548cb000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007548cb000-7007549cf000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+7007549cf000-7007549d0000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007549d0000-7007549d1000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007549d1000-700754ad5000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+700754ad5000-700754ad6000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700754ad6000-700754ad7000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700754ad7000-700754bdb000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        16 kB
+Referenced:           16 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd wr mr mw me nr 
+700754bdb000-700754bdc000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700754bdc000-700754bdd000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700754bdd000-700754ce1000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+700754ce1000-700754ce2000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700754ce2000-700754ce3000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700754ce3000-700754de7000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700754de7000-700754de8000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700754de8000-700754de9000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700754de9000-700754eed000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        16 kB
+Referenced:           16 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd wr mr mw me nr 
+700754eed000-700754eee000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700754eee000-700754eef000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700754eef000-700754ff3000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700754ff3000-700754ff4000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700754ff4000-700754ff5000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700754ff5000-7007550f9000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+7007550f9000-7007550fa000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007550fa000-7007550fb000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007550fb000-7007551ff000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+7007551ff000-700755200000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700755200000-700755201000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700755201000-700755305000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700755305000-700755306000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700755306000-700755307000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700755307000-70075540b000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+70075540b000-70075540c000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075540c000-70075540d000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075540d000-700755511000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+700755511000-700755512000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700755512000-700755513000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700755513000-70075560b000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                  20 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        20 kB
+Referenced:           20 kB
+Anonymous:            20 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               20 kB
+VmFlags: rd wr mr mw me nr 
+70075560b000-70075560c000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075560c000-70075560d000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075560d000-700755705000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700755705000-700755706000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700755706000-700755707000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700755707000-70075580b000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+70075580b000-70075580c000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075580c000-70075580d000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075580d000-700755911000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700755911000-700755912000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700755912000-700755913000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700755913000-700755a17000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700755a17000-700755a2d000 r--p 00000000 fe:00 1947                       /system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex
+Size:                 88 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  88 kB
+Pss:                  88 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        88 kB
+Private_Dirty:         0 kB
+Referenced:           88 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               88 kB
+VmFlags: rd mr mw me 
+700755a2d000-700755a6e000 r-xp 00016000 fe:00 1947                       /system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex
+Size:                260 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 260 kB
+Pss:                 260 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:       260 kB
+Private_Dirty:         0 kB
+Referenced:          260 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              260 kB
+VmFlags: rd ex mr mw me 
+700755a6e000-700755a6f000 r--p 00057000 fe:00 1947                       /system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+700755a6f000-700755a70000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700755a70000-700755a71000 r--s 00000000 fe:00 1946                       /system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.vdex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         4 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr me ms 
+700755a71000-700755a72000 r--p 00058000 fe:00 1947                       /system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         4 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me 
+700755a72000-700755a73000 rw-p 00059000 fe:00 1947                       /system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700755aae000-700755aaf000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700755aaf000-700755ab0000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700755ab0000-700755bb4000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+700755bb4000-700755bb5000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700755bb5000-700755bb6000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700755bb6000-700755cba000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700755cba000-700755cbb000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700755cbb000-700755cbc000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700755cbc000-700755db4000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+700755db4000-700755db5000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700755db5000-700755db6000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700755db6000-700755eae000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+700755f18000-700755f28000 r--p 00000000 fe:00 1483                       /system/lib64/vndk-sp-Q/android.hidl.memory@1.0.so
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  32 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               32 kB
+VmFlags: rd mr mw me 
+700755f28000-700755f3c000 r-xp 00010000 fe:00 1483                       /system/lib64/vndk-sp-Q/android.hidl.memory@1.0.so
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  80 kB
+Pss:                  40 kB
+Shared_Clean:         80 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           80 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               40 kB
+VmFlags: rd ex mr mw me 
+700755f3c000-700755f3d000 rw-p 00024000 fe:00 1483                       /system/lib64/vndk-sp-Q/android.hidl.memory@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700755f3d000-700755f40000 r--p 00025000 fe:00 1483                       /system/lib64/vndk-sp-Q/android.hidl.memory@1.0.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd mr mw me ac 
+700755f71000-7007560ba000 r--p 00000000 fe:00 1821                       /system/priv-app/Telecom/Telecom.apk
+Size:               1316 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 128 kB
+Pss:                 128 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:       128 kB
+Private_Dirty:         0 kB
+Referenced:          128 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              128 kB
+VmFlags: rd mr mw me ac 
+7007560ba000-7007560bb000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007560bb000-7007560bc000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007560bc000-7007561c0000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+7007561c0000-7007561c1000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007561c1000-7007561c2000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007561c2000-7007562c6000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+7007562c6000-7007562c7000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007562c7000-7007562c8000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007562c8000-7007563cc000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+7007563d8000-7007563d9000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007563d9000-7007563da000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007563da000-7007564d2000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  72 kB
+Pss:                  72 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        72 kB
+Referenced:           72 kB
+Anonymous:            72 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               72 kB
+VmFlags: rd wr mr mw me nr 
+7007564d2000-7007564d3000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007564d3000-7007564d4000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007564d4000-7007565d8000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+7007565d8000-7007565d9000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007565d9000-7007565da000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007565da000-7007566de000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+7007566de000-7007566df000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007566df000-7007566e0000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007566e0000-7007567e4000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+7007567e4000-7007567e5000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007567e5000-7007567e6000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007567e6000-7007568de000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+7007568de000-7007568df000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007568df000-7007568e0000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007568e0000-7007569e4000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+7007569e4000-7007569e5000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007569e5000-7007569e6000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007569e6000-700756aea000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700756aea000-700756aeb000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700756aeb000-700756aec000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700756aec000-700756bf0000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  76 kB
+Pss:                  76 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        76 kB
+Referenced:           76 kB
+Anonymous:            76 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               76 kB
+VmFlags: rd wr mr mw me nr 
+700756bf0000-700756bf1000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700756bf1000-700756bf2000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700756bf2000-700756cf6000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700756cf6000-700756cf7000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700756cf7000-700756cf8000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700756cf8000-700756dfc000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700756dfc000-700756dfd000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700756dfd000-700756dfe000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700756dfe000-700756f02000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+700756f02000-700757000000 r--p 00000000 00:13 6792                       /dev/hwbinder
+Size:               1016 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         8 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr me dc nr mm 
+700757000000-700757400000 rw-p 00000000 00:00 0                          [anon:libc_malloc]
+Name:           [anon:libc_malloc]
+Size:               4096 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                2048 kB
+Pss:                2048 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:      2048 kB
+Referenced:         2048 kB
+Anonymous:          2048 kB
+AnonHugePages:      2048 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             2048 kB
+VmFlags: rd wr mr mw me ac 
+700757442000-700757443000 r--p 00000000 fe:00 1554                       /system/lib64/libwifi-service.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         4 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me 
+700757443000-700757444000 r-xp 00001000 fe:00 1554                       /system/lib64/libwifi-service.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         4 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd ex mr mw me 
+700757444000-700757445000 rw-p 00002000 fe:00 1554                       /system/lib64/libwifi-service.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700757445000-700757446000 r--p 00003000 fe:00 1554                       /system/lib64/libwifi-service.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+70075749f000-7007574a0000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007574a0000-7007574a1000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007574a1000-7007575a5000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+7007575a5000-7007578a5000 rw-p 00000000 00:00 0                          [anon:libc_malloc]
+Name:           [anon:libc_malloc]
+Size:               3072 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                2728 kB
+Pss:                2728 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:      2728 kB
+Referenced:         1524 kB
+Anonymous:          2728 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             2728 kB
+VmFlags: rd wr mr mw me ac 
+7007578a5000-7007578a6000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007578a6000-7007578a7000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007578a7000-7007579ab000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+7007579ab000-7007579ac000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007579ac000-7007579ad000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+7007579ad000-700757ab1000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+700757ab1000-700757ab2000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700757ab2000-700757ab3000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700757ab3000-700757bb7000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                  20 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        20 kB
+Referenced:           20 kB
+Anonymous:            20 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               20 kB
+VmFlags: rd wr mr mw me nr 
+700757bb7000-700757bb8000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700757bb8000-700757bb9000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700757bb9000-700757cbd000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                  24 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        24 kB
+Referenced:           24 kB
+Anonymous:            24 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               24 kB
+VmFlags: rd wr mr mw me nr 
+700757cbd000-700757cbe000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700757cbe000-700757cbf000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700757cbf000-700757dc3000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                  28 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        28 kB
+Referenced:           28 kB
+Anonymous:            28 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               28 kB
+VmFlags: rd wr mr mw me nr 
+700757dcf000-700757dd0000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700757dd0000-700757dd1000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700757dd1000-700757ec9000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+700757ec9000-700757ed4000 r--p 00000000 fe:00 1737                       /system/lib64/android.hardware.vibrator@1.0.so
+Size:                 44 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        16 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd mr mw me 
+700757ed4000-700757ee0000 r-xp 0000b000 fe:00 1737                       /system/lib64/android.hardware.vibrator@1.0.so
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  48 kB
+Pss:                  48 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        48 kB
+Private_Dirty:         0 kB
+Referenced:           48 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               48 kB
+VmFlags: rd ex mr mw me 
+700757ee0000-700757ee1000 rw-p 00017000 fe:00 1737                       /system/lib64/android.hardware.vibrator@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700757ee1000-700757ee3000 r--p 00018000 fe:00 1737                       /system/lib64/android.hardware.vibrator@1.0.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+700757f0c000-700757f3c000 r--p 00000000 fe:00 1129                       /system/lib64/libinputflinger.so
+Size:                192 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 112 kB
+Pss:                 112 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:       112 kB
+Private_Dirty:         0 kB
+Referenced:          112 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              112 kB
+VmFlags: rd mr mw me 
+700757f3c000-700757f6f000 r-xp 00030000 fe:00 1129                       /system/lib64/libinputflinger.so
+Size:                204 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 200 kB
+Pss:                 200 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:       200 kB
+Private_Dirty:         0 kB
+Referenced:          200 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              200 kB
+VmFlags: rd ex mr mw me 
+700757f6f000-700757f70000 rw-p 00063000 fe:00 1129                       /system/lib64/libinputflinger.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700757f70000-700757f74000 r--p 00064000 fe:00 1129                       /system/lib64/libinputflinger.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        16 kB
+Referenced:           16 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd mr mw me ac 
+700757f74000-700757f75000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700757f82000-700757faa000 r--p 00000000 fe:00 1625                       /system/lib64/android.hardware.gnss@1.1.so
+Size:                160 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  68 kB
+Pss:                  68 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        68 kB
+Private_Dirty:         0 kB
+Referenced:           68 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               68 kB
+VmFlags: rd mr mw me 
+700757faa000-700757fe1000 r-xp 00028000 fe:00 1625                       /system/lib64/android.hardware.gnss@1.1.so
+Size:                220 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 216 kB
+Pss:                 216 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:       216 kB
+Private_Dirty:         0 kB
+Referenced:          216 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              216 kB
+VmFlags: rd ex mr mw me 
+700757fe1000-700757fe2000 rw-p 0005f000 fe:00 1625                       /system/lib64/android.hardware.gnss@1.1.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700757fe2000-700757fea000 r--p 00060000 fe:00 1625                       /system/lib64/android.hardware.gnss@1.1.so
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  32 kB
+Pss:                  32 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        32 kB
+Referenced:           32 kB
+Anonymous:            32 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               32 kB
+VmFlags: rd mr mw me ac 
+700758023000-70075802d000 r--p 00000000 fe:00 1510                       /system/lib64/android.hardware.tetheroffload.config@1.0.so
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                  20 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        20 kB
+Private_Dirty:         0 kB
+Referenced:           20 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               20 kB
+VmFlags: rd mr mw me 
+70075802d000-700758036000 r-xp 0000a000 fe:00 1510                       /system/lib64/android.hardware.tetheroffload.config@1.0.so
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  36 kB
+Pss:                  36 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        36 kB
+Private_Dirty:         0 kB
+Referenced:           36 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               36 kB
+VmFlags: rd ex mr mw me 
+700758036000-700758037000 rw-p 00013000 fe:00 1510                       /system/lib64/android.hardware.tetheroffload.config@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700758037000-700758039000 r--p 00014000 fe:00 1510                       /system/lib64/android.hardware.tetheroffload.config@1.0.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+700758043000-700758053000 r--p 00000000 fe:00 1259                       /system/lib64/libkeymaster4support.so
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  16 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd mr mw me 
+700758053000-700758069000 r-xp 00010000 fe:00 1259                       /system/lib64/libkeymaster4support.so
+Size:                 88 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700758069000-70075806a000 rw-p 00026000 fe:00 1259                       /system/lib64/libkeymaster4support.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70075806a000-70075806c000 r--p 00027000 fe:00 1259                       /system/lib64/libkeymaster4support.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+700758089000-700758099000 r--p 00000000 fe:00 1175                       /system/lib64/android.hardware.tv.input@1.0.so
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        12 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd mr mw me 
+700758099000-7007580ab000 r-xp 00010000 fe:00 1175                       /system/lib64/android.hardware.tv.input@1.0.so
+Size:                 72 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  72 kB
+Pss:                  72 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        72 kB
+Private_Dirty:         0 kB
+Referenced:           72 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               72 kB
+VmFlags: rd ex mr mw me 
+7007580ab000-7007580ac000 rw-p 00022000 fe:00 1175                       /system/lib64/android.hardware.tv.input@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007580ac000-7007580af000 r--p 00023000 fe:00 1175                       /system/lib64/android.hardware.tv.input@1.0.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd mr mw me ac 
+7007580d7000-7007580d8000 r--p 00000000 fe:00 1648                       /system/lib64/android.hardware.audio.common@2.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         4 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me 
+7007580d8000-7007580d9000 r-xp 00001000 fe:00 1648                       /system/lib64/android.hardware.audio.common@2.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007580d9000-7007580da000 rw-p 00002000 fe:00 1648                       /system/lib64/android.hardware.audio.common@2.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007580da000-7007580db000 r--p 00003000 fe:00 1648                       /system/lib64/android.hardware.audio.common@2.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+700758110000-700758122000 r--p 00000000 fe:00 1680                       /system/lib64/android.hardware.tv.cec@1.0.so
+Size:                 72 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        12 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd mr mw me 
+700758122000-70075813c000 r-xp 00012000 fe:00 1680                       /system/lib64/android.hardware.tv.cec@1.0.so
+Size:                104 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 104 kB
+Pss:                 104 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:       104 kB
+Private_Dirty:         0 kB
+Referenced:          104 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              104 kB
+VmFlags: rd ex mr mw me 
+70075813c000-70075813d000 rw-p 0002c000 fe:00 1680                       /system/lib64/android.hardware.tv.cec@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70075813d000-700758140000 r--p 0002d000 fe:00 1680                       /system/lib64/android.hardware.tv.cec@1.0.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd mr mw me ac 
+700758153000-70075815e000 r--p 00000000 fe:00 1725                       /system/lib64/android.hardware.thermal@1.0.so
+Size:                 44 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  44 kB
+Pss:                  14 kB
+Shared_Clean:         44 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           44 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               14 kB
+VmFlags: rd mr mw me 
+70075815e000-700758169000 r-xp 0000b000 fe:00 1725                       /system/lib64/android.hardware.thermal@1.0.so
+Size:                 44 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  44 kB
+Pss:                  14 kB
+Shared_Clean:         44 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           44 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               14 kB
+VmFlags: rd ex mr mw me 
+700758169000-70075816a000 rw-p 00016000 fe:00 1725                       /system/lib64/android.hardware.thermal@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70075816a000-70075816c000 r--p 00017000 fe:00 1725                       /system/lib64/android.hardware.thermal@1.0.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+700758198000-7007581a3000 r--p 00000000 fe:00 1171                       /system/lib64/android.hardware.power@1.0.so
+Size:                 44 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  44 kB
+Pss:                  14 kB
+Shared_Clean:         44 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           44 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               14 kB
+VmFlags: rd mr mw me 
+7007581a3000-7007581ae000 r-xp 0000b000 fe:00 1171                       /system/lib64/android.hardware.power@1.0.so
+Size:                 44 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  44 kB
+Pss:                  14 kB
+Shared_Clean:         44 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           44 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               14 kB
+VmFlags: rd ex mr mw me 
+7007581ae000-7007581af000 rw-p 00016000 fe:00 1171                       /system/lib64/android.hardware.power@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007581af000-7007581b1000 r--p 00017000 fe:00 1171                       /system/lib64/android.hardware.power@1.0.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+7007581c1000-7007581e1000 r--s 00000000 00:13 6677                       /dev/__properties__/u:object_r:boottime_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   6 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         12 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd mr me ms 
+7007581e1000-7007581ec000 r--p 00000000 fe:00 1568                       /system/lib64/android.hardware.vibrator@1.1.so
+Size:                 44 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        16 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd mr mw me 
+7007581ec000-7007581f7000 r-xp 0000b000 fe:00 1568                       /system/lib64/android.hardware.vibrator@1.1.so
+Size:                 44 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  44 kB
+Pss:                  44 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        44 kB
+Private_Dirty:         0 kB
+Referenced:           44 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               44 kB
+VmFlags: rd ex mr mw me 
+7007581f7000-7007581f8000 rw-p 00016000 fe:00 1568                       /system/lib64/android.hardware.vibrator@1.1.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007581f8000-7007581fa000 r--p 00017000 fe:00 1568                       /system/lib64/android.hardware.vibrator@1.1.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+700758202000-700758212000 r--p 00000000 fe:00 1534                       /system/lib64/android.hardware.keymaster@3.0.so
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  16 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd mr mw me 
+700758212000-70075822d000 r-xp 00010000 fe:00 1534                       /system/lib64/android.hardware.keymaster@3.0.so
+Size:                108 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 108 kB
+Pss:                  27 kB
+Shared_Clean:        108 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          108 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               27 kB
+VmFlags: rd ex mr mw me 
+70075822d000-70075822e000 rw-p 0002b000 fe:00 1534                       /system/lib64/android.hardware.keymaster@3.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70075822e000-700758231000 r--p 0002c000 fe:00 1534                       /system/lib64/android.hardware.keymaster@3.0.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd mr mw me ac 
+700758243000-70075824d000 r--p 00000000 fe:00 1257                       /system/lib64/android.hardware.vr@1.0.so
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                  20 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        20 kB
+Private_Dirty:         0 kB
+Referenced:           20 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               20 kB
+VmFlags: rd mr mw me 
+70075824d000-700758256000 r-xp 0000a000 fe:00 1257                       /system/lib64/android.hardware.vr@1.0.so
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  36 kB
+Pss:                  36 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        36 kB
+Private_Dirty:         0 kB
+Referenced:           36 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               36 kB
+VmFlags: rd ex mr mw me 
+700758256000-700758257000 rw-p 00013000 fe:00 1257                       /system/lib64/android.hardware.vr@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700758257000-700758259000 r--p 00014000 fe:00 1257                       /system/lib64/android.hardware.vr@1.0.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+700758267000-700758287000 r--s 00000000 00:13 6710                       /dev/__properties__/u:object_r:firstboot_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   2 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr me ms 
+700758287000-700758299000 r--p 00000000 fe:00 1586                       /system/lib64/android.hardware.keymaster@4.0.so
+Size:                 72 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  16 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd mr mw me 
+700758299000-7007582ba000 r-xp 00012000 fe:00 1586                       /system/lib64/android.hardware.keymaster@4.0.so
+Size:                132 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 104 kB
+Pss:                  26 kB
+Shared_Clean:        104 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          104 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               26 kB
+VmFlags: rd ex mr mw me 
+7007582ba000-7007582bb000 rw-p 00033000 fe:00 1586                       /system/lib64/android.hardware.keymaster@4.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007582bb000-7007582be000 r--p 00034000 fe:00 1586                       /system/lib64/android.hardware.keymaster@4.0.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd mr mw me ac 
+7007582ce000-7007582ea000 r--p 00000000 fe:00 1729                       /system/lib64/android.frameworks.sensorservice@1.0.so
+Size:                112 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         8 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me 
+7007582ea000-70075830d000 r-xp 0001c000 fe:00 1729                       /system/lib64/android.frameworks.sensorservice@1.0.so
+Size:                140 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 140 kB
+Pss:                 140 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:       140 kB
+Private_Dirty:         0 kB
+Referenced:          140 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              140 kB
+VmFlags: rd ex mr mw me 
+70075830d000-70075830e000 rw-p 0003f000 fe:00 1729                       /system/lib64/android.frameworks.sensorservice@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70075830e000-700758313000 r--p 00040000 fe:00 1729                       /system/lib64/android.frameworks.sensorservice@1.0.so
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                  20 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        20 kB
+Referenced:           20 kB
+Anonymous:            20 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               20 kB
+VmFlags: rd mr mw me ac 
+700758367000-7007583d9000 r--p 00000000 fe:00 1138                       /system/lib64/android.hardware.gnss@1.0.so
+Size:                456 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  64 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        64 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               64 kB
+VmFlags: rd mr mw me 
+7007583d9000-700758494000 r-xp 00072000 fe:00 1138                       /system/lib64/android.hardware.gnss@1.0.so
+Size:                748 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 724 kB
+Pss:                 724 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:       724 kB
+Private_Dirty:         0 kB
+Referenced:          724 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              724 kB
+VmFlags: rd ex mr mw me 
+700758494000-700758495000 rw-p 0012d000 fe:00 1138                       /system/lib64/android.hardware.gnss@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700758495000-7007584ac000 r--p 0012e000 fe:00 1138                       /system/lib64/android.hardware.gnss@1.0.so
+Size:                 92 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  92 kB
+Pss:                  92 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        92 kB
+Referenced:           92 kB
+Anonymous:            92 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               92 kB
+VmFlags: rd mr mw me ac 
+7007584f4000-7007584f7000 r--p 00000000 fe:00 1550                       /system/lib64/libtinyalsa.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        12 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd mr mw me 
+7007584f7000-7007584fb000 r-xp 00003000 fe:00 1550                       /system/lib64/libtinyalsa.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007584fb000-7007584fc000 rw-p 00007000 fe:00 1550                       /system/lib64/libtinyalsa.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007584fc000-7007584fd000 r--p 00008000 fe:00 1550                       /system/lib64/libtinyalsa.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+700758501000-70075850d000 r--p 00000000 fe:00 1197                       /system/lib64/android.hardware.power@1.1.so
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  48 kB
+Pss:                  15 kB
+Shared_Clean:         48 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           48 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               15 kB
+VmFlags: rd mr mw me 
+70075850d000-700758519000 r-xp 0000c000 fe:00 1197                       /system/lib64/android.hardware.power@1.1.so
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  48 kB
+Pss:                  15 kB
+Shared_Clean:         48 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           48 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               15 kB
+VmFlags: rd ex mr mw me 
+700758519000-70075851a000 rw-p 00018000 fe:00 1197                       /system/lib64/android.hardware.power@1.1.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70075851a000-70075851c000 r--p 00019000 fe:00 1197                       /system/lib64/android.hardware.power@1.1.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+700758521000-700758541000 r--s 00000000 00:13 6676                       /dev/__properties__/u:object_r:bootloader_boot_reason_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   2 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr me ms 
+700758541000-700758552000 r--p 00000000 fe:00 1517                       /system/lib64/libsensorservice.so
+Size:                 68 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                  20 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        20 kB
+Private_Dirty:         0 kB
+Referenced:           20 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               20 kB
+VmFlags: rd mr mw me 
+700758552000-700758572000 r-xp 00011000 fe:00 1517                       /system/lib64/libsensorservice.so
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 128 kB
+Pss:                 128 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:       128 kB
+Private_Dirty:         0 kB
+Referenced:          128 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              128 kB
+VmFlags: rd ex mr mw me 
+700758572000-700758573000 rw-p 00031000 fe:00 1517                       /system/lib64/libsensorservice.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700758573000-700758577000 r--p 00032000 fe:00 1517                       /system/lib64/libsensorservice.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        16 kB
+Referenced:           16 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd mr mw me ac 
+700758577000-700758578000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700758586000-700758591000 r--p 00000000 fe:00 1649                       /system/lib64/android.frameworks.schedulerservice@1.0.so
+Size:                 44 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        16 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd mr mw me 
+700758591000-70075859b000 r-xp 0000b000 fe:00 1649                       /system/lib64/android.frameworks.schedulerservice@1.0.so
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  40 kB
+Pss:                  40 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        40 kB
+Private_Dirty:         0 kB
+Referenced:           40 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               40 kB
+VmFlags: rd ex mr mw me 
+70075859b000-70075859c000 rw-p 00015000 fe:00 1649                       /system/lib64/android.frameworks.schedulerservice@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70075859c000-70075859e000 r--p 00016000 fe:00 1649                       /system/lib64/android.frameworks.schedulerservice@1.0.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+7007585ae000-7007585ce000 rw-p 00000000 00:00 0                          [anon:dalvik-LinearAlloc]
+Name:           [anon:dalvik-LinearAlloc]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007585ce000-7007585d1000 r--p 00000000 fe:00 1634                       /system/lib64/libnetutils.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   6 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd mr mw me 
+7007585d1000-7007585d5000 r-xp 00003000 fe:00 1634                       /system/lib64/libnetutils.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007585d5000-7007585d6000 rw-p 00007000 fe:00 1634                       /system/lib64/libnetutils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007585d6000-7007585d7000 r--p 00008000 fe:00 1634                       /system/lib64/libnetutils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007585d7000-7007585d8000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007585e6000-7007585e7000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007585e7000-7007585ef000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007585ef000-7007585f0000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007585f0000-7007585f3000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007585f3000-7007585f4000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007585f4000-7007585fc000 rw-s 00000000 fe:10 24584                      /data/system_ce/0/accounts_ce.db-shm
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr sh mr mw me ms 
+7007585fc000-7007585fd000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007585fd000-700758605000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700758605000-70075862a000 r--p 00000000 fe:00 1626                       /system/lib64/android.hardware.broadcastradio@1.1.so
+Size:                148 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  68 kB
+Pss:                  68 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        68 kB
+Private_Dirty:         0 kB
+Referenced:           68 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               68 kB
+VmFlags: rd mr mw me 
+70075862a000-70075865c000 r-xp 00025000 fe:00 1626                       /system/lib64/android.hardware.broadcastradio@1.1.so
+Size:                200 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 196 kB
+Pss:                 196 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:       196 kB
+Private_Dirty:         0 kB
+Referenced:          196 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              196 kB
+VmFlags: rd ex mr mw me 
+70075865c000-70075865d000 rw-p 00057000 fe:00 1626                       /system/lib64/android.hardware.broadcastradio@1.1.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70075865d000-700758664000 r--p 00058000 fe:00 1626                       /system/lib64/android.hardware.broadcastradio@1.1.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                  28 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        28 kB
+Referenced:           28 kB
+Anonymous:            28 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               28 kB
+VmFlags: rd mr mw me ac 
+700758666000-700758667000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700758667000-70075866f000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70075866f000-700758674000 r--s 004e8000 fe:00 1805                       /system/priv-app/Launcher3QuickStep/Launcher3QuickStep.apk
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                  10 kB
+Shared_Clean:         20 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           20 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               10 kB
+VmFlags: rd mr me ms 
+70075869d000-7007586a6000 r--p 00000000 fe:00 1249                       /system/lib64/libinputservice.so
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                  24 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        24 kB
+Private_Dirty:         0 kB
+Referenced:           24 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               24 kB
+VmFlags: rd mr mw me 
+7007586a6000-7007586ad000 r-xp 00009000 fe:00 1249                       /system/lib64/libinputservice.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007586ad000-7007586ae000 rw-p 00010000 fe:00 1249                       /system/lib64/libinputservice.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007586ae000-7007586af000 r--p 00011000 fe:00 1249                       /system/lib64/libinputservice.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007586b2000-7007586b3000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007586b3000-7007586b6000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007586b6000-7007586b7000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007586d2000-7007586df000 r--p 00000000 fe:00 1211                       /system/lib64/android.hardware.sensors@1.0.so
+Size:                 52 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        16 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd mr mw me 
+7007586df000-7007586f1000 r-xp 0000d000 fe:00 1211                       /system/lib64/android.hardware.sensors@1.0.so
+Size:                 72 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  72 kB
+Pss:                  72 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        72 kB
+Private_Dirty:         0 kB
+Referenced:           72 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               72 kB
+VmFlags: rd ex mr mw me 
+7007586f1000-7007586f2000 rw-p 0001f000 fe:00 1211                       /system/lib64/android.hardware.sensors@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007586f2000-7007586f4000 r--p 00020000 fe:00 1211                       /system/lib64/android.hardware.sensors@1.0.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+7007586f4000-7007586f5000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007586f5000-7007586f8000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007586f8000-7007586f9000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700758721000-70075872e000 r--p 00000000 fe:00 1212                       /system/lib64/libkeystore_binder.so
+Size:                 52 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  52 kB
+Pss:                  17 kB
+Shared_Clean:         52 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           52 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               17 kB
+VmFlags: rd mr mw me 
+70075872e000-700758739000 r-xp 0000d000 fe:00 1212                       /system/lib64/libkeystore_binder.so
+Size:                 44 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  44 kB
+Pss:                  14 kB
+Shared_Clean:         44 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           44 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               14 kB
+VmFlags: rd ex mr mw me 
+700758739000-70075873a000 rw-p 00018000 fe:00 1212                       /system/lib64/libkeystore_binder.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70075873a000-70075873d000 r--p 00019000 fe:00 1212                       /system/lib64/libkeystore_binder.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd mr mw me ac 
+70075873d000-70075873e000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700758740000-700758742000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700758742000-700758754000 r--p 00000000 fe:00 1521                       /system/lib64/android.hardware.contexthub@1.0.so
+Size:                 72 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        12 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd mr mw me 
+700758754000-70075876e000 r-xp 00012000 fe:00 1521                       /system/lib64/android.hardware.contexthub@1.0.so
+Size:                104 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 100 kB
+Pss:                 100 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:       100 kB
+Private_Dirty:         0 kB
+Referenced:          100 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              100 kB
+VmFlags: rd ex mr mw me 
+70075876e000-70075876f000 rw-p 0002c000 fe:00 1521                       /system/lib64/android.hardware.contexthub@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70075876f000-700758772000 r--p 0002d000 fe:00 1521                       /system/lib64/android.hardware.contexthub@1.0.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd mr mw me ac 
+7007587a6000-7007587a8000 r--p 00000000 fe:00 1270                       /system/lib64/libschedulerservicehidl.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         4 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me 
+7007587a8000-7007587a9000 r-xp 00002000 fe:00 1270                       /system/lib64/libschedulerservicehidl.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         4 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd ex mr mw me 
+7007587a9000-7007587aa000 rw-p 00003000 fe:00 1270                       /system/lib64/libschedulerservicehidl.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007587aa000-7007587ab000 r--p 00004000 fe:00 1270                       /system/lib64/libschedulerservicehidl.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007587ab000-7007587ae000 r-xp 00000000 00:00 0 
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd ex mr mw me ac 
+7007587ae000-7007587af000 rw-p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007587af000-7007587b2000 r-xp 00000000 00:00 0 
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd ex mr mw me ac 
+7007587b2000-7007587b3000 rw-p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007587b3000-7007587b6000 r-xp 00000000 00:00 0 
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd ex mr mw me ac 
+7007587b6000-7007587b7000 rw-p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007587b7000-7007587ba000 r-xp 00000000 00:00 0 
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd ex mr mw me ac 
+7007587ba000-7007587bb000 rw-p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007587bb000-7007587be000 r-xp 00000000 00:00 0 
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd ex mr mw me ac 
+7007587be000-7007587bf000 rw-p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007587bf000-7007587c2000 r-xp 00000000 00:00 0 
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd ex mr mw me ac 
+7007587c2000-7007587c3000 rw-p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007587c3000-7007587cd000 r--p 00000000 fe:00 1157                       /system/lib64/android.hardware.light@2.0.so
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        16 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd mr mw me 
+7007587cd000-7007587d7000 r-xp 0000a000 fe:00 1157                       /system/lib64/android.hardware.light@2.0.so
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  40 kB
+Pss:                  40 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        40 kB
+Private_Dirty:         0 kB
+Referenced:           40 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               40 kB
+VmFlags: rd ex mr mw me 
+7007587d7000-7007587d8000 rw-p 00014000 fe:00 1157                       /system/lib64/android.hardware.light@2.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007587d8000-7007587da000 r--p 00015000 fe:00 1157                       /system/lib64/android.hardware.light@2.0.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+7007587db000-7007587dd000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007587dd000-7007587e0000 r-xp 00000000 00:00 0 
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd ex mr mw me ac 
+7007587e0000-7007587e1000 rw-p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007587e1000-7007587e4000 r-xp 00000000 00:00 0 
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd ex mr mw me ac 
+7007587e4000-7007587e5000 rw-p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007587e5000-7007587e8000 r-xp 00000000 00:00 0 
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd ex mr mw me ac 
+7007587e8000-7007587e9000 rw-p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007587e9000-7007587ec000 r-xp 00000000 00:00 0 
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd ex mr mw me ac 
+7007587ec000-7007587ed000 rw-p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007587ed000-7007587f0000 r-xp 00000000 00:00 0 
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd ex mr mw me ac 
+7007587f0000-7007587f1000 rw-p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700758808000-700758814000 r--p 00000000 fe:00 1677                       /system/lib64/android.hardware.vibrator@1.2.so
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        12 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd mr mw me 
+700758814000-70075881f000 r-xp 0000c000 fe:00 1677                       /system/lib64/android.hardware.vibrator@1.2.so
+Size:                 44 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  44 kB
+Pss:                  44 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        44 kB
+Private_Dirty:         0 kB
+Referenced:           44 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               44 kB
+VmFlags: rd ex mr mw me 
+70075881f000-700758820000 rw-p 00017000 fe:00 1677                       /system/lib64/android.hardware.vibrator@1.2.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700758820000-700758822000 r--p 00018000 fe:00 1677                       /system/lib64/android.hardware.vibrator@1.2.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+700758830000-700758850000 rw-p 00000000 00:00 0                          [anon:dalvik-LinearAlloc]
+Name:           [anon:dalvik-LinearAlloc]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  32 kB
+Pss:                  32 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        32 kB
+Referenced:           32 kB
+Anonymous:            32 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               32 kB
+VmFlags: rd wr mr mw me ac 
+700758850000-700758858000 r--p 00000000 fe:00 1546                       /system/lib64/libsensorservicehidl.so
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                  24 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        24 kB
+Private_Dirty:         0 kB
+Referenced:           24 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               24 kB
+VmFlags: rd mr mw me 
+700758858000-70075885c000 r-xp 00008000 fe:00 1546                       /system/lib64/libsensorservicehidl.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        16 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd ex mr mw me 
+70075885c000-70075885d000 rw-p 0000c000 fe:00 1546                       /system/lib64/libsensorservicehidl.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70075885d000-70075885f000 r--p 0000d000 fe:00 1546                       /system/lib64/libsensorservicehidl.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+700758860000-700758880000 r--s 00000000 00:13 6738                       /dev/__properties__/u:object_r:system_boot_reason_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   2 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr me ms 
+700758880000-700758897000 r--p 00000000 fe:00 1225                       /system/lib64/libkeystore_aidl.so
+Size:                 92 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  92 kB
+Pss:                  32 kB
+Shared_Clean:         92 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           92 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               32 kB
+VmFlags: rd mr mw me 
+700758897000-7007588a4000 r-xp 00017000 fe:00 1225                       /system/lib64/libkeystore_aidl.so
+Size:                 52 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  52 kB
+Pss:                  17 kB
+Shared_Clean:         52 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           52 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               17 kB
+VmFlags: rd ex mr mw me 
+7007588a4000-7007588a5000 rw-p 00024000 fe:00 1225                       /system/lib64/libkeystore_aidl.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007588a5000-7007588ad000 r--p 00025000 fe:00 1225                       /system/lib64/libkeystore_aidl.so
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  32 kB
+Pss:                  32 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        32 kB
+Referenced:           32 kB
+Anonymous:            32 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               32 kB
+VmFlags: rd mr mw me ac 
+7007588ad000-7007588ae000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007588ae000-7007588b0000 r-xp 00000000 00:00 0 
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd ex mr mw me ac 
+7007588b0000-7007588d0000 rw-p 00000000 00:00 0                          [anon:dalvik-LinearAlloc]
+Name:           [anon:dalvik-LinearAlloc]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me ac 
+7007588d0000-7007588d6000 r--p 00000000 fe:00 1704                       /system/lib64/libkeystore_parcelables.so
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                   7 kB
+Shared_Clean:         24 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           24 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                7 kB
+VmFlags: rd mr mw me 
+7007588d6000-7007588da000 r-xp 00006000 fe:00 1704                       /system/lib64/libkeystore_parcelables.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007588da000-7007588db000 rw-p 0000a000 fe:00 1704                       /system/lib64/libkeystore_parcelables.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007588db000-7007588dc000 r--p 0000b000 fe:00 1704                       /system/lib64/libkeystore_parcelables.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007588dd000-7007588de000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007588de000-7007588e6000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007588e6000-700758906000 rw-p 00000000 00:00 0                          [anon:dalvik-LinearAlloc]
+Name:           [anon:dalvik-LinearAlloc]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+700758906000-70075890e000 rw-s 00000000 fe:10 188581                     /data/system/notification_log.db-shm
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr sh mr mw me ms 
+70075890e000-70075892d000 r--p 00000000 fe:00 1578                       /system/lib64/android.hardware.broadcastradio@1.0.so
+Size:                124 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         8 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me 
+70075892d000-70075895a000 r-xp 0001f000 fe:00 1578                       /system/lib64/android.hardware.broadcastradio@1.0.so
+Size:                180 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 176 kB
+Pss:                 176 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:       176 kB
+Private_Dirty:         0 kB
+Referenced:          176 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              176 kB
+VmFlags: rd ex mr mw me 
+70075895a000-70075895b000 rw-p 0004c000 fe:00 1578                       /system/lib64/android.hardware.broadcastradio@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70075895b000-700758961000 r--p 0004d000 fe:00 1578                       /system/lib64/android.hardware.broadcastradio@1.0.so
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                  24 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        24 kB
+Referenced:           24 kB
+Anonymous:            24 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               24 kB
+VmFlags: rd mr mw me ac 
+700758961000-700758962000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700758962000-700758965000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700758965000-700758966000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700758966000-700758968000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700758983000-700758984000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700758984000-70075898c000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70075898c000-70075898d000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70075898d000-700758990000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+700758990000-700758991000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700758991000-700758993000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007589b3000-7007589b4000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007589b4000-7007589bc000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007589bc000-7007589f8000 r--p 00000000 fe:00 1622                       /system/lib64/libandroid_servers.so
+Size:                240 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 100 kB
+Pss:                 100 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:       100 kB
+Private_Dirty:         0 kB
+Referenced:          100 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              100 kB
+VmFlags: rd mr mw me 
+7007589f8000-700758a36000 r-xp 0003c000 fe:00 1622                       /system/lib64/libandroid_servers.so
+Size:                248 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 244 kB
+Pss:                 244 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:       244 kB
+Private_Dirty:         0 kB
+Referenced:          244 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              244 kB
+VmFlags: rd ex mr mw me 
+700758a36000-700758a37000 rw-p 0007a000 fe:00 1622                       /system/lib64/libandroid_servers.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700758a37000-700758a3f000 r--p 0007b000 fe:00 1622                       /system/lib64/libandroid_servers.so
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  32 kB
+Pss:                  32 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        32 kB
+Referenced:           32 kB
+Anonymous:            32 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               32 kB
+VmFlags: rd mr mw me ac 
+700758a3f000-700758a40000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700758a40000-700758a41000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700758a41000-700758a49000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700758a49000-700758a4a000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700758a4a000-700758a52000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700758a52000-700758a5c000 r--p 00000000 fe:00 1507                       /system/lib64/android.hardware.ir@1.0.so
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        16 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd mr mw me 
+700758a5c000-700758a66000 r-xp 0000a000 fe:00 1507                       /system/lib64/android.hardware.ir@1.0.so
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  40 kB
+Pss:                  40 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        40 kB
+Private_Dirty:         0 kB
+Referenced:           40 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               40 kB
+VmFlags: rd ex mr mw me 
+700758a66000-700758a67000 rw-p 00014000 fe:00 1507                       /system/lib64/android.hardware.ir@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700758a67000-700758a69000 r--p 00015000 fe:00 1507                       /system/lib64/android.hardware.ir@1.0.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+700758a69000-700758a6a000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700758a6a000-700758a6d000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700758a6d000-700758a6e000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700758a6e000-700758a8e000 rw-p 00000000 00:00 0                          [anon:dalvik-LinearAlloc]
+Name:           [anon:dalvik-LinearAlloc]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  36 kB
+Pss:                  36 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        36 kB
+Referenced:           36 kB
+Anonymous:            36 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               36 kB
+VmFlags: rd wr mr mw me ac 
+700758a8e000-700758a8f000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700758a8f000-700758a90000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700758a90000-700758b88000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                  20 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        20 kB
+Referenced:           20 kB
+Anonymous:            20 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               20 kB
+VmFlags: rd wr mr mw me nr 
+700758b88000-700758b89000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700758b89000-700758b8a000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+700758b8a000-700758c82000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        16 kB
+Referenced:           16 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd wr mr mw me nr 
+700758c82000-700758c85000 r--p 00000000 fe:00 3277                       /system/framework/oat/x86_64/com.android.location.provider.impl.odex
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   6 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd mr mw me 
+700758c85000-700758c89000 r-xp 00003000 fe:00 3277                       /system/framework/oat/x86_64/com.android.location.provider.impl.odex
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700758c89000-700758c8a000 r--p 00007000 fe:00 3277                       /system/framework/oat/x86_64/com.android.location.provider.impl.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+700758c8a000-700758c8b000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700758c8b000-700758c90000 r--s 00000000 fe:00 3249                       /system/framework/oat/x86_64/com.android.location.provider.impl.vdex
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                  10 kB
+Shared_Clean:         20 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           20 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               10 kB
+VmFlags: rd mr me ms 
+700758c90000-700758c91000 r--p 00008000 fe:00 3277                       /system/framework/oat/x86_64/com.android.location.provider.impl.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   2 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me 
+700758c91000-700758c92000 rw-p 00009000 fe:00 3277                       /system/framework/oat/x86_64/com.android.location.provider.impl.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700758c93000-700758c94000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700758c94000-700758c97000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700758c97000-700758c98000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700758c98000-700758c99000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700758c99000-700758ca1000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700758ca1000-700758ca2000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700758ca2000-700758caa000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700758caa000-700758cca000 rw-p 00000000 00:00 0                          [anon:dalvik-LinearAlloc]
+Name:           [anon:dalvik-LinearAlloc]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 128 kB
+Pss:                 128 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       128 kB
+Referenced:          128 kB
+Anonymous:           128 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              128 kB
+VmFlags: rd wr mr mw me ac 
+700758cca000-700758d93000 r--p 00000000 fe:00 3287                       /system/framework/oat/x86_64/wifi-service.odex
+Size:                804 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 116 kB
+Pss:                 116 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:       116 kB
+Private_Dirty:         0 kB
+Referenced:          116 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              116 kB
+VmFlags: rd mr mw me 
+700758d93000-70075910d000 r-xp 000c9000 fe:00 3287                       /system/framework/oat/x86_64/wifi-service.odex
+Size:               3560 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                3184 kB
+Pss:                3184 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:      3184 kB
+Private_Dirty:         0 kB
+Referenced:         3184 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             3184 kB
+VmFlags: rd ex mr mw me 
+70075910d000-70075910f000 r--p 00443000 fe:00 3287                       /system/framework/oat/x86_64/wifi-service.odex
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+70075910f000-700759118000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  36 kB
+Pss:                  36 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        36 kB
+Referenced:           36 kB
+Anonymous:            36 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               36 kB
+VmFlags: rd wr mr mw me ac 
+700759118000-70075932e000 r--s 00000000 fe:00 3231                       /system/framework/oat/x86_64/wifi-service.vdex
+Size:               2136 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  64 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        64 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               64 kB
+VmFlags: rd mr me ms 
+70075932e000-70075932f000 r--p 00445000 fe:00 3287                       /system/framework/oat/x86_64/wifi-service.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         4 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me 
+70075932f000-700759330000 rw-p 00446000 fe:00 3287                       /system/framework/oat/x86_64/wifi-service.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700759330000-700759332000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700759332000-700759348000 r--s 00602000 fe:00 1821                       /system/priv-app/Telecom/Telecom.apk
+Size:                 88 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  88 kB
+Pss:                  88 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        88 kB
+Private_Dirty:         0 kB
+Referenced:           88 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               88 kB
+VmFlags: rd mr me ms 
+700759348000-700759349000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700759349000-700759351000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700759351000-700759355000 r--p 00000000 fe:00 3250                       /system/framework/oat/x86_64/ethernet-service.odex
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        16 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd mr mw me 
+700759355000-700759361000 r-xp 00004000 fe:00 3250                       /system/framework/oat/x86_64/ethernet-service.odex
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  48 kB
+Pss:                  48 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        48 kB
+Private_Dirty:         0 kB
+Referenced:           48 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               48 kB
+VmFlags: rd ex mr mw me 
+700759361000-700759362000 r--p 00010000 fe:00 3250                       /system/framework/oat/x86_64/ethernet-service.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+700759362000-700759363000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700759363000-70075936b000 r--s 00000000 fe:00 3243                       /system/framework/oat/x86_64/ethernet-service.vdex
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  32 kB
+Pss:                  32 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        32 kB
+Private_Dirty:         0 kB
+Referenced:           32 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               32 kB
+VmFlags: rd mr me ms 
+70075936b000-70075936c000 r--p 00011000 fe:00 3250                       /system/framework/oat/x86_64/ethernet-service.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         4 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me 
+70075936c000-70075936d000 rw-p 00012000 fe:00 3250                       /system/framework/oat/x86_64/ethernet-service.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70075936d000-70075936f000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70075936f000-700759370000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700759370000-700759373000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700759373000-700759375000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700759375000-700759378000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700759378000-700759379000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700759379000-70075937a000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+70075937a000-700759382000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700759382000-700759383000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700759383000-700759386000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700759386000-700759387000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700759387000-70075983a000 r--p 00000000 fe:00 3248                       /system/framework/oat/x86_64/services.odex
+Size:               4812 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                2576 kB
+Pss:                2576 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:      2576 kB
+Private_Dirty:         0 kB
+Referenced:         2576 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             2576 kB
+VmFlags: rd mr mw me 
+70075983a000-70075a9b3000 r-xp 004b3000 fe:00 3248                       /system/framework/oat/x86_64/services.odex
+Size:              17892 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:               17060 kB
+Pss:               17060 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:     17060 kB
+Private_Dirty:         0 kB
+Referenced:        17060 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:            17060 kB
+VmFlags: rd ex mr mw me 
+70075a9b3000-70075a9ba000 r--p 0162c000 fe:00 3248                       /system/framework/oat/x86_64/services.odex
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                  28 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        28 kB
+Referenced:           28 kB
+Anonymous:            28 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               28 kB
+VmFlags: rd mr mw me ac 
+70075a9ba000-70075a9e2000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                160 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 160 kB
+Pss:                 160 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       160 kB
+Referenced:          160 kB
+Anonymous:           160 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              160 kB
+VmFlags: rd wr mr mw me ac 
+70075a9e2000-70075b335000 r--s 00000000 fe:00 3271                       /system/framework/oat/x86_64/services.vdex
+Size:               9548 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                2856 kB
+Pss:                2856 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:      2856 kB
+Private_Dirty:         0 kB
+Referenced:         2856 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             2856 kB
+VmFlags: rd mr me ms 
+70075b335000-70075b336000 r--p 01633000 fe:00 3248                       /system/framework/oat/x86_64/services.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         4 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me 
+70075b336000-70075b337000 rw-p 01634000 fe:00 3248                       /system/framework/oat/x86_64/services.odex
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70075b338000-70075b33a000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70075b33a000-70075b33b000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+70075b33b000-70075b343000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70075b344000-70075b346000 r-xp 00000000 00:00 0 
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd ex mr mw me ac 
+70075b34b000-70075b34c000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70075b34c000-70075b34f000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70075b34f000-70075b350000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70075b350000-70075b352000 r-xp 00000000 00:00 0 
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd ex mr mw me ac 
+70075b354000-70075b355000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70075b355000-70075b358000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70075b358000-70075b359000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70075b359000-70075b35a000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+70075b35a000-70075b362000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70075b362000-70075b363000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70075b363000-70075b366000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70075b366000-70075b367000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70075b367000-70075b369000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70075b369000-70075b36a000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+70075b36a000-70075b372000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70075b372000-70075b373000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+70075b373000-70075b37b000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70075b37b000-70075b479000 r--p 00000000 00:13 6783                       /dev/binder
+Size:               1016 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                  24 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        24 kB
+Private_Dirty:         0 kB
+Referenced:           24 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               24 kB
+VmFlags: rd mr me dc nr mm 
+70075b479000-70075b47a000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075b47a000-70075b47b000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075b47b000-70075b57f000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+70075b57f000-70075b580000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075b580000-70075b581000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075b581000-70075b685000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+70075b685000-70075b686000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075b686000-70075b687000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075b687000-70075b78b000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me nr 
+70075b78b000-70075b78c000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075b78c000-70075b78d000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70075b78d000-70075b891000 rw-p 00000000 00:00 0 
+Size:               1040 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+70075b891000-70075d891000 r--s 02000000 00:05 9572                       /memfd:/jit-cache (deleted)
+Size:              32768 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         24 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           24 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd sh mr mw me ms 
+70075d891000-700766592000 ---p 00000000 00:00 0 
+Size:             144388 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766592000-700766593000 r--p 00000000 fe:00 1106                       /system/lib64/libwebviewchromium_loader.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+700766593000-700766594000 r-xp 00001000 fe:00 1106                       /system/lib64/libwebviewchromium_loader.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700766594000-700766595000 rw-p 00002000 fe:00 1106                       /system/lib64/libwebviewchromium_loader.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766595000-700766596000 r--p 00003000 fe:00 1106                       /system/lib64/libwebviewchromium_loader.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700766596000-700766597000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766597000-700766598000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700766598000-7007665a0000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007665a0000-7007665a1000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007665a1000-7007665a4000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007665a4000-7007665a5000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007665a5000-7007665a9000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+7007665ab000-7007665af000 r--s 00002000 fe:00 2036                       /system/priv-app/FusedLocation/FusedLocation.apk
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  14 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:        12 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               14 kB
+VmFlags: rd mr me ms 
+7007665af000-7007665b2000 r--p 00000000 fe:00 2036                       /system/priv-app/FusedLocation/FusedLocation.apk
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  10 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         8 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               10 kB
+VmFlags: rd mr mw me ac 
+7007665b5000-7007665b7000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007665b7000-7007665b8000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007665b8000-7007665bb000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007665bb000-7007665bc000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007665bc000-7007665be000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007665be000-7007665bf000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007665bf000-7007665c7000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007665c7000-7007665c8000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007665c8000-7007665d0000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007665d0000-7007665d1000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007665d1000-7007665d4000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007665d4000-7007665d6000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007665d6000-7007665d9000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007665d9000-7007665da000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007665da000-7007665db000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007665db000-7007665e3000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007665e3000-7007665e4000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007665e4000-7007665ec000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007665ed000-7007665ef000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007665ef000-7007665f0000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007665f0000-7007665f8000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007665f8000-700766646000 r--s 00000000 fe:00 1030                       /system/usr/hyphen-data/hyph-hu.hyb
+Size:                312 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+700766646000-70076664f000 r--p 00000000 fe:00 1712                       /system/lib64/libcompiler_rt.so
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  36 kB
+Pss:                   9 kB
+Shared_Clean:         36 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           36 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                9 kB
+VmFlags: rd mr mw me 
+70076664f000-700766664000 r-xp 00009000 fe:00 1712                       /system/lib64/libcompiler_rt.so
+Size:                 84 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700766664000-700766665000 rw-p 0001e000 fe:00 1712                       /system/lib64/libcompiler_rt.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766665000-700766666000 r--p 0001f000 fe:00 1712                       /system/lib64/libcompiler_rt.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700766666000-700766690000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                168 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766691000-700766699000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+700766699000-70076669b000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076669b000-70076669c000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70076669c000-70076669f000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076669f000-7007666a0000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007666a0000-7007666b0000 rw-s 00000000 00:05 11661                      /dev/ashmem/RemoteDataSource (deleted)
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   2 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd wr sh mr mw me ms 
+7007666b0000-7007666b1000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007666b1000-7007666b9000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007666b9000-7007666ba000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007666ba000-7007666bd000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007666bd000-7007666be000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007666be000-7007666c0000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007666c0000-700766751000 r--p 00000000 fe:30 333                        /vendor/lib64/egl/libGLESv1_CM_swiftshader.so
+Size:                580 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 192 kB
+Pss:                  38 kB
+Shared_Clean:        192 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          192 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               38 kB
+VmFlags: rd mr mw me 
+700766751000-70076690e000 r-xp 00091000 fe:30 333                        /vendor/lib64/egl/libGLESv1_CM_swiftshader.so
+Size:               1780 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 116 kB
+Pss:                  24 kB
+Shared_Clean:        116 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          116 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               24 kB
+VmFlags: rd ex mr mw me 
+70076690e000-70076690f000 rw-p 0024e000 fe:30 333                        /vendor/lib64/egl/libGLESv1_CM_swiftshader.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076690f000-70076691f000 r--p 0024f000 fe:30 333                        /vendor/lib64/egl/libGLESv1_CM_swiftshader.so
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                   3 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         64 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:            64 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me ac 
+70076691f000-70076696d000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                312 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 312 kB
+Pss:                  17 kB
+Shared_Clean:          0 kB
+Shared_Dirty:        312 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:           312 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               17 kB
+VmFlags: rd wr mr mw me ac 
+70076696d000-70076696e000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+70076696e000-700766976000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766976000-700766977000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700766977000-70076697f000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076697f000-700766980000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766980000-700766983000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766983000-700766985000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766985000-700766988000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766988000-70076698a000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70076698a000-70076698d000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076698d000-70076698f000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70076698f000-700766992000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766992000-700766993000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766993000-700766994000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700766994000-70076699c000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076699c000-700766a5f000 r--p 00000000 fe:30 335                        /vendor/lib64/egl/libGLESv2_swiftshader.so
+Size:                780 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 256 kB
+Pss:                  51 kB
+Shared_Clean:        256 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          256 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               51 kB
+VmFlags: rd mr mw me 
+700766a5f000-700766c8d000 r-xp 000c3000 fe:30 335                        /vendor/lib64/egl/libGLESv2_swiftshader.so
+Size:               2232 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                2212 kB
+Pss:                 624 kB
+Shared_Clean:       2212 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:         2212 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              624 kB
+VmFlags: rd ex mr mw me 
+700766c8d000-700766c8e000 rw-p 002f1000 fe:30 335                        /vendor/lib64/egl/libGLESv2_swiftshader.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700766c8e000-700766ca2000 r--p 002f2000 fe:30 335                        /vendor/lib64/egl/libGLESv2_swiftshader.so
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  80 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         80 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           80 kB
+Anonymous:            80 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+700766ca2000-700766cf0000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                312 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 312 kB
+Pss:                  32 kB
+Shared_Clean:          0 kB
+Shared_Dirty:        296 kB
+Private_Clean:         0 kB
+Private_Dirty:        16 kB
+Referenced:           44 kB
+Anonymous:           312 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               32 kB
+VmFlags: rd wr mr mw me ac 
+700766cf1000-700766cf3000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766cf3000-700766cf4000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766cf4000-700766cf7000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766cf7000-700766cf9000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766cf9000-700766cfc000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766cfc000-700766cfd000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766cfd000-700766cfe000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700766cfe000-700766d06000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766d06000-700766d13000 r--p 00000000 fe:30 334                        /vendor/lib64/egl/libEGL_swiftshader.so
+Size:                 52 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  52 kB
+Pss:                  10 kB
+Shared_Clean:         52 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           52 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               10 kB
+VmFlags: rd mr mw me 
+700766d13000-700766d2f000 r-xp 0000d000 fe:30 334                        /vendor/lib64/egl/libEGL_swiftshader.so
+Size:                112 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 112 kB
+Pss:                  22 kB
+Shared_Clean:        112 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          112 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               22 kB
+VmFlags: rd ex mr mw me 
+700766d2f000-700766d30000 rw-p 00029000 fe:30 334                        /vendor/lib64/egl/libEGL_swiftshader.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766d30000-700766d31000 r--p 0002a000 fe:30 334                        /vendor/lib64/egl/libEGL_swiftshader.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700766d31000-700766d78000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                284 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 284 kB
+Pss:                  19 kB
+Shared_Clean:          0 kB
+Shared_Dirty:        280 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:           284 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               19 kB
+VmFlags: rd wr mr mw me ac 
+700766d78000-700766d7e000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me ac 
+700766d7e000-700766d7f000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766d7f000-700766d82000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766d82000-700766d83000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766d83000-700766d84000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700766d84000-700766d8c000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766d8c000-700766d8d000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766d8d000-700766d90000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766d90000-700766d91000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766d91000-700766db1000 rw-p 00000000 00:00 0                          [anon:dalvik-LinearAlloc]
+Name:           [anon:dalvik-LinearAlloc]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 124 kB
+Pss:                 124 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       124 kB
+Referenced:          124 kB
+Anonymous:           124 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              124 kB
+VmFlags: rd wr mr mw me ac 
+700766db1000-700766db3000 r--p 00000000 fe:00 1488                       /system/lib64/vndk-sp-Q/libbinderthreadstate.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+700766db3000-700766db5000 r-xp 00002000 fe:00 1488                       /system/lib64/vndk-sp-Q/libbinderthreadstate.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700766db5000-700766db6000 rw-p 00004000 fe:00 1488                       /system/lib64/vndk-sp-Q/libbinderthreadstate.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766db6000-700766db7000 r--p 00005000 fe:00 1488                       /system/lib64/vndk-sp-Q/libbinderthreadstate.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700766db7000-700766db8000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766db8000-700766dbc000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+700766dbc000-700766dbd000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700766dbd000-700766dc5000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766dc5000-700766dc6000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700766dc6000-700766dce000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766dce000-700766e26000 r--p 00000000 fe:00 1497                       /system/lib64/vndk-sp-Q/libc++.so
+Size:                352 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 252 kB
+Pss:                  11 kB
+Shared_Clean:        252 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          252 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               11 kB
+VmFlags: rd mr mw me 
+700766e26000-700766e9a000 r-xp 00058000 fe:00 1497                       /system/lib64/vndk-sp-Q/libc++.so
+Size:                464 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 408 kB
+Pss:                  18 kB
+Shared_Clean:        408 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          408 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               18 kB
+VmFlags: rd ex mr mw me 
+700766e9a000-700766e9b000 rw-p 000cc000 fe:00 1497                       /system/lib64/vndk-sp-Q/libc++.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766e9b000-700766ea3000 r--p 000cd000 fe:00 1497                       /system/lib64/vndk-sp-Q/libc++.so
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  32 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         32 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:            32 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me ac 
+700766ea3000-700766ea7000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           16 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me ac 
+700766ea8000-700766ea9000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766ea9000-700766eac000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766eac000-700766ead000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766ead000-700766eae000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700766eae000-700766eb6000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766eb6000-700766ed6000 r--s 00000000 00:13 6703                       /dev/__properties__/u:object_r:device_logging_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr me ms 
+700766ed6000-700766ee9000 r--p 00000000 fe:00 1476                       /system/lib64/vndk-sp-Q/libhwbinder.so
+Size:                 76 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                   3 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me 
+700766ee9000-700766efa000 r-xp 00013000 fe:00 1476                       /system/lib64/vndk-sp-Q/libhwbinder.so
+Size:                 68 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700766efa000-700766efb000 rw-p 00024000 fe:00 1476                       /system/lib64/vndk-sp-Q/libhwbinder.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766efb000-700766efd000 r--p 00025000 fe:00 1476                       /system/lib64/vndk-sp-Q/libhwbinder.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700766efd000-700766efe000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766eff000-700766f01000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700766f01000-700766f02000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766f02000-700766f05000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766f05000-700766f07000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766f07000-700766f0a000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766f0a000-700766f0b000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766f0c000-700766f0d000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766f0d000-700766f10000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766f10000-700766f11000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766f11000-700766f13000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700766f13000-700766f14000 r--p 00000000 fe:00 1492                       /system/lib64/vndk-sp-Q/libhardware.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+700766f14000-700766f15000 r-xp 00001000 fe:00 1492                       /system/lib64/vndk-sp-Q/libhardware.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700766f15000-700766f16000 rw-p 00002000 fe:00 1492                       /system/lib64/vndk-sp-Q/libhardware.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766f16000-700766f17000 r--p 00003000 fe:00 1492                       /system/lib64/vndk-sp-Q/libhardware.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700766f17000-700766f37000 r--s 00000000 00:13 6734                       /dev/__properties__/u:object_r:safemode_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   2 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr me ms 
+700766f37000-700766f57000 rw-p 00000000 00:00 0                          [anon:dalvik-LinearAlloc]
+Name:           [anon:dalvik-LinearAlloc]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 128 kB
+Pss:                 128 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       128 kB
+Referenced:          128 kB
+Anonymous:           128 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              128 kB
+VmFlags: rd wr mr mw me ac 
+700766f57000-700766f63000 r--p 00000000 fe:00 1491                       /system/lib64/vndk-sp-Q/android.hardware.graphics.mapper@2.0.so
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  48 kB
+Pss:                   6 kB
+Shared_Clean:         48 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           48 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd mr mw me 
+700766f63000-700766f6f000 r-xp 0000c000 fe:00 1491                       /system/lib64/vndk-sp-Q/android.hardware.graphics.mapper@2.0.so
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  48 kB
+Pss:                   6 kB
+Shared_Clean:         48 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           48 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd ex mr mw me 
+700766f6f000-700766f70000 rw-p 00018000 fe:00 1491                       /system/lib64/vndk-sp-Q/android.hardware.graphics.mapper@2.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766f70000-700766f72000 r--p 00019000 fe:00 1491                       /system/lib64/vndk-sp-Q/android.hardware.graphics.mapper@2.0.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700766f72000-700766f74000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700766f74000-700766f75000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700766f75000-700766f7d000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766f7d000-700766f7e000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766f7e000-700766f81000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766f81000-700766f82000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766f82000-700766f83000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700766f83000-700766f8b000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766f8b000-700766f8e000 r--p 00000000 fe:30 348                        /vendor/lib64/hw/android.hardware.graphics.mapper@2.0-impl.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   1 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+700766f8e000-700766f91000 r-xp 00003000 fe:30 348                        /vendor/lib64/hw/android.hardware.graphics.mapper@2.0-impl.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   2 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd ex mr mw me 
+700766f91000-700766f92000 rw-p 00006000 fe:30 348                        /vendor/lib64/hw/android.hardware.graphics.mapper@2.0-impl.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766f92000-700766f93000 r--p 00007000 fe:30 348                        /vendor/lib64/hw/android.hardware.graphics.mapper@2.0-impl.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700766f93000-700766f94000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700766f95000-700766f99000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+700766f99000-700766f9a000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700766f9a000-700766fa2000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766fa2000-700766fa3000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700766fa3000-700766fab000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766fab000-700766fcb000 rw-p 00000000 00:00 0                          [anon:dalvik-LinearAlloc]
+Name:           [anon:dalvik-LinearAlloc]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 128 kB
+Pss:                 128 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       128 kB
+Referenced:          128 kB
+Anonymous:           128 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              128 kB
+VmFlags: rd wr mr mw me ac 
+700766fcb000-700766fd2000 r--p 00000000 fe:00 1487                       /system/lib64/vndk-sp-Q/libcutils.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                   1 kB
+Shared_Clean:         28 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           28 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+700766fd2000-700766fdb000 r-xp 00007000 fe:00 1487                       /system/lib64/vndk-sp-Q/libcutils.so
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  36 kB
+Pss:                   2 kB
+Shared_Clean:         36 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           36 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd ex mr mw me 
+700766fdb000-700766fdc000 rw-p 00010000 fe:00 1487                       /system/lib64/vndk-sp-Q/libcutils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766fdc000-700766fde000 r--p 00011000 fe:00 1487                       /system/lib64/vndk-sp-Q/libcutils.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700766fde000-700766fdf000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766fe0000-700766fe1000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766fe1000-700766fe4000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700766fe4000-700766fe5000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700766fe5000-700767005000 r--s 00000000 00:13 6741                       /dev/__properties__/u:object_r:system_radio_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   2 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr me ms 
+700767005000-70076700e000 r--p 00000000 fe:00 1503                       /system/lib64/vndk-sp-Q/libbase.so
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  36 kB
+Pss:                   1 kB
+Shared_Clean:         36 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           36 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+70076700e000-700767017000 r-xp 00009000 fe:00 1503                       /system/lib64/vndk-sp-Q/libbase.so
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700767017000-700767018000 rw-p 00012000 fe:00 1503                       /system/lib64/vndk-sp-Q/libbase.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700767018000-700767019000 r--p 00013000 fe:00 1503                       /system/lib64/vndk-sp-Q/libbase.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700767019000-70076701a000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076701b000-70076701d000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70076701d000-70076701e000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+70076701e000-700767026000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700767026000-700767027000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700767027000-70076702a000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076702a000-70076702b000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70076702b000-70076702c000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+70076702c000-700767034000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700767034000-700767035000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700767035000-700767038000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700767038000-700767039000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700767039000-70076703a000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+70076703a000-700767042000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700767042000-700767043000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700767043000-70076704b000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076704b000-70076705a000 r--p 00000000 fe:00 1478                       /system/lib64/vndk-sp-Q/libutils.so
+Size:                 60 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  60 kB
+Pss:                   2 kB
+Shared_Clean:         60 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           60 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me 
+70076705a000-700767066000 r-xp 0000f000 fe:00 1478                       /system/lib64/vndk-sp-Q/libutils.so
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  48 kB
+Pss:                   2 kB
+Shared_Clean:         48 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           48 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd ex mr mw me 
+700767066000-700767067000 rw-p 0001b000 fe:00 1478                       /system/lib64/vndk-sp-Q/libutils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700767067000-700767068000 r--p 0001c000 fe:00 1478                       /system/lib64/vndk-sp-Q/libutils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700767068000-700767069000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076706a000-70076706c000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70076706c000-70076706d000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70076706d000-700767070000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700767070000-700767072000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700767072000-700767075000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700767075000-700767077000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700767077000-70076707a000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076707a000-70076707b000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70076707b000-70076707c000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+70076707c000-700767084000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700767084000-700767085000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700767085000-70076708d000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076708e000-700767094000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+700767094000-700767095000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700767095000-700767098000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700767098000-700767099000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700767099000-70076709b000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70076709b000-7007670d4000 r--p 00000000 fe:00 1489                       /system/lib64/vndk-sp-Q/libhidltransport.so
+Size:                228 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 148 kB
+Pss:                  10 kB
+Shared_Clean:        148 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          148 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               10 kB
+VmFlags: rd mr mw me 
+7007670d4000-70076712f000 r-xp 00039000 fe:00 1489                       /system/lib64/vndk-sp-Q/libhidltransport.so
+Size:                364 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 236 kB
+Pss:                  13 kB
+Shared_Clean:        236 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          236 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               13 kB
+VmFlags: rd ex mr mw me 
+70076712f000-700767130000 rw-p 00094000 fe:00 1489                       /system/lib64/vndk-sp-Q/libhidltransport.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700767130000-70076713a000 r--p 00095000 fe:00 1489                       /system/lib64/vndk-sp-Q/libhidltransport.so
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  40 kB
+Pss:                   2 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         40 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:            40 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me ac 
+70076713a000-70076713b000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70076713b000-70076713c000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70076713c000-70076713f000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076713f000-700767141000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700767141000-700767144000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700767144000-700767145000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700767145000-700767146000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700767146000-70076714e000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076714e000-70076714f000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70076714f000-700767152000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700767152000-700767153000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700767153000-700767154000 r--p 00000000 fe:00 1495                       /system/lib64/vndk-sp-Q/android.hardware.graphics.common@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+700767154000-700767155000 r-xp 00001000 fe:00 1495                       /system/lib64/vndk-sp-Q/android.hardware.graphics.common@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700767155000-700767156000 rw-p 00002000 fe:00 1495                       /system/lib64/vndk-sp-Q/android.hardware.graphics.common@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700767156000-700767157000 r--p 00003000 fe:00 1495                       /system/lib64/vndk-sp-Q/android.hardware.graphics.common@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700767157000-700767197000 rw-p 00000000 00:00 0                          [anon:dalvik-LinearAlloc]
+Name:           [anon:dalvik-LinearAlloc]
+Size:                256 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 152 kB
+Pss:                 152 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       152 kB
+Referenced:          152 kB
+Anonymous:           152 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              152 kB
+VmFlags: rd wr mr mw me ac 
+700767197000-7007671a6000 r--p 00000000 fe:00 1477                       /system/lib64/vndk-sp-Q/libhidlbase.so
+Size:                 60 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  60 kB
+Pss:                   3 kB
+Shared_Clean:         60 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           60 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me 
+7007671a6000-7007671b9000 r-xp 0000f000 fe:00 1477                       /system/lib64/vndk-sp-Q/libhidlbase.so
+Size:                 76 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  36 kB
+Pss:                   1 kB
+Shared_Clean:         36 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           36 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd ex mr mw me 
+7007671b9000-7007671ba000 rw-p 00022000 fe:00 1477                       /system/lib64/vndk-sp-Q/libhidlbase.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007671ba000-7007671bc000 r--p 00023000 fe:00 1477                       /system/lib64/vndk-sp-Q/libhidlbase.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007671bc000-7007671bd000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007671bd000-7007671bf000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007671bf000-7007671c0000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007671c0000-7007671c8000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007671c8000-7007671c9000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007671c9000-7007671cc000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007671cc000-7007671cd000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007671cd000-7007671ce000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007671ce000-7007671d6000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007671d6000-7007671d7000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007671d7000-7007671da000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007671da000-7007671db000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007671db000-700768d59000 r--s 00bc3000 fe:00 3346                       /system/framework/framework-res.apk
+Size:              28152 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:               19968 kB
+Pss:                1144 kB
+Shared_Clean:      19956 kB
+Shared_Dirty:          0 kB
+Private_Clean:        12 kB
+Private_Dirty:         0 kB
+Referenced:        19968 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             1144 kB
+VmFlags: rd mr me ms 
+700768d59000-700768e14000 r--s 0287a000 fe:00 3346                       /system/framework/framework-res.apk
+Size:                748 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 144 kB
+Pss:                  18 kB
+Shared_Clean:        144 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          144 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               18 kB
+VmFlags: rd mr me ms 
+700768e14000-700768e35000 r--p 00000000 fe:00 1638                       /system/lib64/libssl.so
+Size:                132 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 108 kB
+Pss:                  38 kB
+Shared_Clean:        108 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          108 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               38 kB
+VmFlags: rd mr mw me 
+700768e35000-700768e6c000 r-xp 00021000 fe:00 1638                       /system/lib64/libssl.so
+Size:                220 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 172 kB
+Pss:                  88 kB
+Shared_Clean:        168 kB
+Shared_Dirty:          0 kB
+Private_Clean:         4 kB
+Private_Dirty:         0 kB
+Referenced:          172 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               88 kB
+VmFlags: rd ex mr mw me 
+700768e6c000-700768e6d000 rw-p 00058000 fe:00 1638                       /system/lib64/libssl.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700768e6d000-700768e70000 r--p 00059000 fe:00 1638                       /system/lib64/libssl.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         12 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700768e70000-700768e74000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+700768e74000-700768e75000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700768e75000-700768e7d000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700768e7d000-700768e7e000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700768e7e000-700768e81000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700768e81000-700768e82000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700768e82000-700768ea2000 rw-p 00000000 00:00 0                          [anon:dalvik-LinearAlloc]
+Name:           [anon:dalvik-LinearAlloc]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 128 kB
+Pss:                 128 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       128 kB
+Referenced:          128 kB
+Anonymous:           128 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              128 kB
+VmFlags: rd wr mr mw me ac 
+700768ea2000-700768ebf000 r--p 00000000 fe:00 1513                       /system/lib64/libjavacrypto.so
+Size:                116 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 112 kB
+Pss:                  23 kB
+Shared_Clean:        112 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          112 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               23 kB
+VmFlags: rd mr mw me 
+700768ebf000-700768ee5000 r-xp 0001d000 fe:00 1513                       /system/lib64/libjavacrypto.so
+Size:                152 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 152 kB
+Pss:                  27 kB
+Shared_Clean:        152 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          152 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               27 kB
+VmFlags: rd ex mr mw me 
+700768ee5000-700768ee7000 rw-p 00043000 fe:00 1513                       /system/lib64/libjavacrypto.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700768ee7000-700768ee9000 r--p 00045000 fe:00 1513                       /system/lib64/libjavacrypto.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700768ee9000-700768eea000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700768eea000-700768eeb000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700768eeb000-700768ef3000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700768ef3000-700768ef4000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700768ef4000-700768efc000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700768efc000-700768f1c000 rw-p 00000000 00:00 0                          [anon:dalvik-LinearAlloc]
+Name:           [anon:dalvik-LinearAlloc]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 128 kB
+Pss:                 128 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       128 kB
+Referenced:          128 kB
+Anonymous:           128 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              128 kB
+VmFlags: rd wr mr mw me ac 
+700768f1c000-700768f22000 r--p 00000000 fe:00 1763                       /system/lib64/libsoundpool.so
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                   4 kB
+Shared_Clean:         24 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           24 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me 
+700768f22000-700768f27000 r-xp 00006000 fe:00 1763                       /system/lib64/libsoundpool.so
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                   6 kB
+Shared_Clean:         20 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           20 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd ex mr mw me 
+700768f27000-700768f28000 rw-p 0000b000 fe:00 1763                       /system/lib64/libsoundpool.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700768f28000-700768f29000 r--p 0000c000 fe:00 1763                       /system/lib64/libsoundpool.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700768f29000-700768f2a000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700768f2b000-700768f2c000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700768f2c000-700768f2f000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700768f2f000-700768f31000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700768f31000-700768f34000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700768f34000-700768f35000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700768f35000-700768f36000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700768f36000-700768f3e000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700768f3e000-700768f5e000 rw-p 00000000 00:00 0                          [anon:dalvik-LinearAlloc]
+Name:           [anon:dalvik-LinearAlloc]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 128 kB
+Pss:                 128 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       128 kB
+Referenced:          128 kB
+Anonymous:           128 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              128 kB
+VmFlags: rd wr mr mw me ac 
+700768f5e000-700768fd9000 r--s 00000000 07:08 15                         /apex/com.android.tzdata/etc/tz/tzdata
+Size:                492 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+700768fd9000-700769748000 r--s 00000000 fe:00 100                        /system/fonts/NotoColorEmoji.ttf
+Size:               7612 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+700769748000-70076aee5000 r--s 00000000 fe:00 130                        /system/fonts/NotoSerifCJK-Regular.ttc
+Size:              24180 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+70076aee5000-70076c0b5000 r--s 00000000 fe:00 239                        /system/fonts/NotoSansCJK-Regular.ttc
+Size:              18240 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+70076c0b5000-70076c163000 r--s 00000000 fe:00 161                        /system/fonts/NotoSansSymbols-Regular-Subsetted.ttf
+Size:                696 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+70076c163000-70076c1bf000 r--s 00000000 fe:00 257                        /system/fonts/NotoSansTibetan-Bold.ttf
+Size:                368 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+70076c1bf000-70076c222000 r--s 00000000 fe:00 270                        /system/fonts/NotoSansTibetan-Regular.ttf
+Size:                396 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+70076c222000-70076c29e000 r--s 00000000 fe:00 254                        /system/fonts/NotoSansEgyptianHieroglyphs-Regular.ttf
+Size:                496 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+70076c29e000-70076c319000 r--s 00000000 fe:00 196                        /system/fonts/NotoSansCuneiform-Regular.ttf
+Size:                492 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+70076c319000-70076c36a000 r--s 00000000 fe:00 233                        /system/fonts/RobotoCondensed-BoldItalic.ttf
+Size:                324 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+70076c36a000-70076c3b5000 r--s 00000000 fe:00 282                        /system/fonts/RobotoCondensed-Bold.ttf
+Size:                300 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+70076c3b5000-70076c406000 r--s 00000000 fe:00 288                        /system/fonts/RobotoCondensed-MediumItalic.ttf
+Size:                324 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+70076c406000-70076c451000 r--s 00000000 fe:00 184                        /system/fonts/RobotoCondensed-Medium.ttf
+Size:                300 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+70076c451000-70076c6d1000 rw-p 00000000 00:00 0                          [anon:libc_malloc]
+Name:           [anon:libc_malloc]
+Size:               2560 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                2452 kB
+Pss:                1171 kB
+Shared_Clean:          0 kB
+Shared_Dirty:       1356 kB
+Private_Clean:         0 kB
+Private_Dirty:      1096 kB
+Referenced:         1336 kB
+Anonymous:          2452 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             1171 kB
+VmFlags: rd wr mr mw me ac 
+70076c6d1000-70076de00000 r--s 00000000 fe:00 1096                       /system/usr/icu/icudt63l.dat
+Size:              23740 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 452 kB
+Pss:                  85 kB
+Shared_Clean:        452 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          452 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               85 kB
+VmFlags: rd mr me ms 
+70076de00000-70076e200000 rw-p 00000000 00:00 0                          [anon:libc_malloc]
+Name:           [anon:libc_malloc]
+Size:               4096 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                3988 kB
+Pss:                3677 kB
+Shared_Clean:          0 kB
+Shared_Dirty:        336 kB
+Private_Clean:         0 kB
+Private_Dirty:      3652 kB
+Referenced:         3632 kB
+Anonymous:          3988 kB
+AnonHugePages:      2048 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             3677 kB
+VmFlags: rd wr mr mw me ac 
+70076e201000-70076e203000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70076e203000-70076e204000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+70076e204000-70076e20c000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076e20c000-70076e25d000 r--s 00000000 fe:00 182                        /system/fonts/RobotoCondensed-Italic.ttf
+Size:                324 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+70076e25d000-70076e2a7000 r--s 00000000 fe:00 96                         /system/fonts/RobotoCondensed-Regular.ttf
+Size:                296 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+70076e2a7000-70076e2f9000 r--s 00000000 fe:00 285                        /system/fonts/RobotoCondensed-LightItalic.ttf
+Size:                328 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+70076e2f9000-70076e2fd000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+70076e2fd000-70076e2fe000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70076e2fe000-70076e301000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076e301000-70076e303000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70076e303000-70076e306000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076e306000-70076e307000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70076e307000-70076e308000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+70076e308000-70076e310000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076e310000-70076e311000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70076e311000-70076e314000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076e314000-70076e315000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70076e315000-70076e316000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+70076e316000-70076e31e000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076e31e000-70076e31f000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+70076e31f000-70076e327000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076e327000-70076e328000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70076e328000-70076e329000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70076e329000-70076e421000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+70076e421000-70076e422000 ---p 00000000 00:00 0                          [anon:thread stack guard]
+Name:           [anon:thread stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70076e422000-70076e423000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me nr 
+70076e423000-70076e51b000 rw-p 00000000 00:00 0 
+Size:                992 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me nr 
+70076e51b000-70076e51c000 ---p 00000000 00:00 0                          [anon:dalvik-Jit thread pool worker thread 0]
+Name:           [anon:dalvik-Jit thread pool worker thread 0]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+70076e51c000-70076e51d000 ---p 00000000 00:00 0                          [anon:dalvik-Jit thread pool worker thread 0]
+Name:           [anon:dalvik-Jit thread pool worker thread 0]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+70076e51d000-70076e61c000 rw-p 00000000 00:00 0                          [anon:dalvik-Jit thread pool worker thread 0]
+Name:           [anon:dalvik-Jit thread pool worker thread 0]
+Size:               1020 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                  28 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        28 kB
+Referenced:           28 kB
+Anonymous:            28 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               28 kB
+VmFlags: rd wr mr mw me ac 
+70076e61c000-70076e717000 rw-p 00000000 00:00 0                          [anon:dalvik-allocspace non moving space mark-bitmap 1]
+Name:           [anon:dalvik-allocspace non moving space mark-bitmap 1]
+Size:               1004 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076e717000-70076e812000 rw-p 00000000 00:00 0                          [anon:dalvik-allocspace non moving space live-bitmap 1]
+Name:           [anon:dalvik-allocspace non moving space live-bitmap 1]
+Size:               1004 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70076e812000-70076e8f1000 r--p 00000000 fe:00 1114                       /system/lib64/libart-compiler.so
+Size:                892 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 188 kB
+Pss:                  27 kB
+Shared_Clean:        188 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          188 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               27 kB
+VmFlags: rd mr mw me 
+70076e8f1000-70076eb46000 r-xp 000df000 fe:00 1114                       /system/lib64/libart-compiler.so
+Size:               2388 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                2092 kB
+Pss:                 201 kB
+Shared_Clean:       2092 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:         2092 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              201 kB
+VmFlags: rd ex mr mw me 
+70076eb46000-70076eb47000 rw-p 00334000 fe:00 1114                       /system/lib64/libart-compiler.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076eb47000-70076eb59000 r--p 00335000 fe:00 1114                       /system/lib64/libart-compiler.so
+Size:                 72 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  72 kB
+Pss:                   3 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         72 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:            72 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me ac 
+70076eb59000-70076eb5a000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076eb5b000-70076eb5d000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70076eb5d000-70076eb5e000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70076eb5e000-70076eb61000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076eb61000-70076eb62000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70076eb62000-70076eb82000 rw-p 00000000 00:00 0                          [anon:dalvik-LinearAlloc]
+Name:           [anon:dalvik-LinearAlloc]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 124 kB
+Pss:                 124 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       124 kB
+Referenced:          124 kB
+Anonymous:           124 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              124 kB
+VmFlags: rd wr mr mw me ac 
+70076eb82000-70076eb9c000 r--p 00000000 fe:00 1229                       /system/lib64/libopenjdk.so
+Size:                104 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 100 kB
+Pss:                   5 kB
+Shared_Clean:        100 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          100 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                5 kB
+VmFlags: rd mr mw me 
+70076eb9c000-70076ebb7000 r-xp 0001a000 fe:00 1229                       /system/lib64/libopenjdk.so
+Size:                108 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 108 kB
+Pss:                   8 kB
+Shared_Clean:        108 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          108 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd ex mr mw me 
+70076ebb7000-70076ebb9000 rw-p 00035000 fe:00 1229                       /system/lib64/libopenjdk.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076ebb9000-70076ebba000 r--p 00037000 fe:00 1229                       /system/lib64/libopenjdk.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+70076ebba000-70076ebbb000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70076ebbb000-70076ebbd000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70076ebbd000-70076ebbe000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70076ebbe000-70076ebc1000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076ebc1000-70076ebc2000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70076ebc2000-70076ebe2000 rw-p 00000000 00:00 0                          [anon:dalvik-LinearAlloc]
+Name:           [anon:dalvik-LinearAlloc]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 112 kB
+Pss:                 112 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       112 kB
+Referenced:          112 kB
+Anonymous:           112 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              112 kB
+VmFlags: rd wr mr mw me ac 
+70076ebe2000-70076ebe6000 r--p 00000000 fe:00 1645                       /system/lib64/libopenjdkjvm.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                   5 kB
+Shared_Clean:         16 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                5 kB
+VmFlags: rd mr mw me 
+70076ebe6000-70076ebea000 r-xp 00004000 fe:00 1645                       /system/lib64/libopenjdkjvm.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                   0 kB
+Shared_Clean:         16 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+70076ebea000-70076ebeb000 rw-p 00008000 fe:00 1645                       /system/lib64/libopenjdkjvm.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076ebeb000-70076ebec000 r--p 00009000 fe:00 1645                       /system/lib64/libopenjdkjvm.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+70076ebed000-70076ebef000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70076ebef000-70076ebf0000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+70076ebf0000-70076ebf8000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076ebf8000-70076ebf9000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+70076ebf9000-70076ec01000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076ec01000-70076ec02000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+70076ec02000-70076ec0a000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076ec0a000-70076ec2a000 rw-p 00000000 00:00 0                          [anon:dalvik-CompilerMetadata]
+Name:           [anon:dalvik-CompilerMetadata]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70076ec2a000-700770359000 r--s 00000000 fe:00 1096                       /system/usr/icu/icudt63l.dat
+Size:              23740 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                1164 kB
+Pss:                 165 kB
+Shared_Clean:       1164 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:         1164 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              165 kB
+VmFlags: rd mr me ms rr 
+700770359000-700770371000 r--p 00000000 fe:00 1615                       /system/lib64/libjavacore.so
+Size:                 96 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  92 kB
+Pss:                  10 kB
+Shared_Clean:         92 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           92 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               10 kB
+VmFlags: rd mr mw me 
+700770371000-700770396000 r-xp 00018000 fe:00 1615                       /system/lib64/libjavacore.so
+Size:                148 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 148 kB
+Pss:                   8 kB
+Shared_Clean:        148 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          148 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd ex mr mw me 
+700770396000-700770398000 rw-p 0003d000 fe:00 1615                       /system/lib64/libjavacore.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770398000-70077039a000 r--p 0003f000 fe:00 1615                       /system/lib64/libjavacore.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+70077039a000-70077039b000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70077039c000-70077039e000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70077039e000-70077039f000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70077039f000-7007703a2000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007703a2000-7007703a3000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007703a3000-7007703a4000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007703a4000-7007703ac000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007703ac000-7007703cc000 rw-p 00000000 00:00 0                          [anon:dalvik-CompilerMetadata]
+Name:           [anon:dalvik-CompilerMetadata]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007703cc000-7007703ce000 r--p 00000000 fe:00 1134                       /system/lib64/libwebviewchromium_plat_support.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+7007703ce000-7007703cf000 r-xp 00002000 fe:00 1134                       /system/lib64/libwebviewchromium_plat_support.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007703cf000-7007703d0000 rw-p 00003000 fe:00 1134                       /system/lib64/libwebviewchromium_plat_support.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007703d0000-7007703d1000 r--p 00004000 fe:00 1134                       /system/lib64/libwebviewchromium_plat_support.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007703d1000-7007703d2000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007703d2000-7007703d3000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007703d3000-7007703d6000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007703d6000-7007703d7000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007703d7000-7007703d8000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007703d8000-7007703e0000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007703e0000-700770400000 rw-p 00000000 00:00 0                          [anon:dalvik-CompilerMetadata]
+Name:           [anon:dalvik-CompilerMetadata]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770400000-700770423000 r--p 00000000 fe:00 1228                       /system/lib64/android.hardware.renderscript@1.0.so
+Size:                140 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  21 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               21 kB
+VmFlags: rd mr mw me 
+700770423000-70077047a000 r-xp 00023000 fe:00 1228                       /system/lib64/android.hardware.renderscript@1.0.so
+Size:                348 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+70077047a000-70077047b000 rw-p 0007a000 fe:00 1228                       /system/lib64/android.hardware.renderscript@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70077047b000-70077047f000 r--p 0007b000 fe:00 1228                       /system/lib64/android.hardware.renderscript@1.0.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         16 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700770480000-700770481000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770481000-700770484000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770484000-700770486000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770486000-700770489000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770489000-70077048a000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70077048a000-7007704aa000 rw-p 00000000 00:00 0                          [anon:dalvik-CompilerMetadata]
+Name:           [anon:dalvik-CompilerMetadata]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007704aa000-7007704b2000 r--p 00000000 fe:00 1184                       /system/lib64/libRS.so
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  32 kB
+Pss:                  10 kB
+Shared_Clean:         32 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           32 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               10 kB
+VmFlags: rd mr mw me 
+7007704b2000-7007704bc000 r-xp 00008000 fe:00 1184                       /system/lib64/libRS.so
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007704bc000-7007704bd000 rw-p 00012000 fe:00 1184                       /system/lib64/libRS.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007704bd000-7007704be000 r--p 00013000 fe:00 1184                       /system/lib64/libRS.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007704be000-7007704bf000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007704c0000-7007704c2000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007704c2000-7007704c3000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007704c3000-7007704c6000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007704c6000-7007704c7000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007704c7000-7007704c8000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007704c8000-7007704d0000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007704d0000-7007704d2000 r--p 00000000 fe:00 1768                       /system/lib64/libOpenSLES.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   2 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me 
+7007704d2000-7007704d3000 r-xp 00002000 fe:00 1768                       /system/lib64/libOpenSLES.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007704d3000-7007704d4000 rw-p 00003000 fe:00 1768                       /system/lib64/libOpenSLES.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007704d4000-7007704d5000 r--p 00004000 fe:00 1768                       /system/lib64/libOpenSLES.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007704d5000-7007704d6000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007704d6000-7007704d9000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007704d9000-7007704da000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007704da000-7007704db000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007704db000-7007704e3000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007704e3000-700770503000 rw-p 00000000 00:00 0                          [anon:dalvik-LinearAlloc]
+Name:           [anon:dalvik-LinearAlloc]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 124 kB
+Pss:                 124 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       124 kB
+Referenced:          124 kB
+Anonymous:           124 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              124 kB
+VmFlags: rd wr mr mw me ac 
+700770503000-700770505000 r--p 00000000 fe:00 1250                       /system/lib64/libOpenMAXAL.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   2 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me 
+700770505000-700770506000 r-xp 00002000 fe:00 1250                       /system/lib64/libOpenMAXAL.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700770506000-700770507000 rw-p 00003000 fe:00 1250                       /system/lib64/libOpenMAXAL.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770507000-700770508000 r--p 00004000 fe:00 1250                       /system/lib64/libOpenMAXAL.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700770508000-700770528000 rw-p 00000000 00:00 0                          [anon:dalvik-CompilerMetadata]
+Name:           [anon:dalvik-CompilerMetadata]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770528000-700770551000 r--p 00000000 fe:00 1943                       /system/priv-app/SettingsProvider/SettingsProvider.apk
+Size:                164 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  88 kB
+Pss:                  88 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        88 kB
+Private_Dirty:         0 kB
+Referenced:           88 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               88 kB
+VmFlags: rd mr mw me ac 
+700770551000-700770575000 r--p 00000000 fe:00 1470                       /system/lib64/libneuralnetworks.so
+Size:                144 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  21 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               21 kB
+VmFlags: rd mr mw me 
+700770575000-70077065a000 r-xp 00024000 fe:00 1470                       /system/lib64/libneuralnetworks.so
+Size:                916 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+70077065a000-70077065b000 rw-p 00109000 fe:00 1470                       /system/lib64/libneuralnetworks.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70077065b000-70077065e000 r--p 0010a000 fe:00 1470                       /system/lib64/libneuralnetworks.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         12 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+70077065e000-70077096f000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:               3140 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70077096f000-700770973000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+700770973000-700770974000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770974000-700770977000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770977000-700770978000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770978000-700770979000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770979000-700770981000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770981000-70077098b000 r--s 00039000 fe:00 1943                       /system/priv-app/SettingsProvider/SettingsProvider.apk
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  40 kB
+Pss:                  40 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:        40 kB
+Private_Dirty:         0 kB
+Referenced:           40 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               40 kB
+VmFlags: rd mr me ms 
+70077098b000-7007709ab000 r--p 00000000 fe:00 1724                       /system/lib64/android.hardware.neuralnetworks@1.2.so
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  21 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               21 kB
+VmFlags: rd mr mw me 
+7007709ab000-7007709d1000 r-xp 00020000 fe:00 1724                       /system/lib64/android.hardware.neuralnetworks@1.2.so
+Size:                152 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007709d1000-7007709d2000 rw-p 00046000 fe:00 1724                       /system/lib64/android.hardware.neuralnetworks@1.2.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007709d2000-7007709d8000 r--p 00047000 fe:00 1724                       /system/lib64/android.hardware.neuralnetworks@1.2.so
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         24 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            24 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me ac 
+7007709d8000-7007709da000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007709da000-7007709db000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007709db000-7007709de000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007709de000-7007709df000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007709df000-7007709e0000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007709e0000-7007709e8000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007709e8000-700770a08000 r--s 00000000 00:13 6735                       /dev/__properties__/u:object_r:serialno_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr me ms 
+700770a08000-700770a15000 r--p 00000000 fe:00 1214                       /system/lib64/android.hardware.neuralnetworks@1.1.so
+Size:                 52 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  52 kB
+Pss:                  17 kB
+Shared_Clean:         52 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           52 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               17 kB
+VmFlags: rd mr mw me 
+700770a15000-700770a23000 r-xp 0000d000 fe:00 1214                       /system/lib64/android.hardware.neuralnetworks@1.1.so
+Size:                 56 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700770a23000-700770a24000 rw-p 0001b000 fe:00 1214                       /system/lib64/android.hardware.neuralnetworks@1.1.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770a24000-700770a26000 r--p 0001c000 fe:00 1214                       /system/lib64/android.hardware.neuralnetworks@1.1.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700770a26000-700770a2a000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+700770a2a000-700770a2b000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770a2b000-700770a2e000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770a2e000-700770a2f000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770a2f000-700770a30000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770a30000-700770a38000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770a38000-700770a39000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770a39000-700770a3c000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770a3c000-700770a3d000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770a3d000-700770a3e000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770a3e000-700770a46000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770a46000-700770a66000 r--s 00000000 00:13 6726                       /dev/__properties__/u:object_r:overlay_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   2 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr me ms 
+700770a66000-700770a80000 r--p 00000000 fe:00 1136                       /system/lib64/android.hardware.neuralnetworks@1.0.so
+Size:                104 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  21 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               21 kB
+VmFlags: rd mr mw me 
+700770a80000-700770aa1000 r-xp 0001a000 fe:00 1136                       /system/lib64/android.hardware.neuralnetworks@1.0.so
+Size:                132 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700770aa1000-700770aa2000 rw-p 0003b000 fe:00 1136                       /system/lib64/android.hardware.neuralnetworks@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770aa2000-700770aa7000 r--p 0003c000 fe:00 1136                       /system/lib64/android.hardware.neuralnetworks@1.0.so
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         20 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            20 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me ac 
+700770aa7000-700770aa9000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700770aa9000-700770aaa000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770aaa000-700770ab2000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770ab2000-700770ab3000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770ab3000-700770abb000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770abb000-700770abc000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770abc000-700770ac4000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770ac4000-700770ac6000 r--p 00000000 fe:00 1183                       /system/lib64/libtextclassifier_hash.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+700770ac6000-700770aca000 r-xp 00002000 fe:00 1183                       /system/lib64/libtextclassifier_hash.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700770aca000-700770acb000 rw-p 00006000 fe:00 1183                       /system/lib64/libtextclassifier_hash.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770acb000-700770acc000 r--p 00007000 fe:00 1183                       /system/lib64/libtextclassifier_hash.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700770acd000-700770b1f000 r--s 00000000 fe:00 211                        /system/fonts/Roboto-BoldItalic.ttf
+Size:                328 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+700770b1f000-700770b20000 r--p 00000000 fe:00 1767                       /system/lib64/libjnigraphics.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+700770b20000-700770b21000 r-xp 00001000 fe:00 1767                       /system/lib64/libjnigraphics.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700770b21000-700770b22000 rw-p 00002000 fe:00 1767                       /system/lib64/libjnigraphics.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770b22000-700770b23000 r--p 00003000 fe:00 1767                       /system/lib64/libjnigraphics.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700770b24000-700770b26000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770b26000-700770b27000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770b27000-700770b2a000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770b2a000-700770b2b000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770b2b000-700770b4b000 rw-p 00000000 00:00 0                          [anon:dalvik-LinearAlloc]
+Name:           [anon:dalvik-LinearAlloc]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  44 kB
+Pss:                  44 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        44 kB
+Referenced:           44 kB
+Anonymous:            44 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               44 kB
+VmFlags: rd wr mr mw me ac 
+700770b4b000-700770b4c000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770b4c000-700770b54000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770b54000-700770b67000 r--p 00000000 fe:00 1691                       /system/lib64/libGLESv3.so
+Size:                 76 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  60 kB
+Pss:                  19 kB
+Shared_Clean:         60 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           60 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               19 kB
+VmFlags: rd mr mw me 
+700770b67000-700770b6e000 r-xp 00013000 fe:00 1691                       /system/lib64/libGLESv3.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700770b6e000-700770b6f000 rw-p 0001a000 fe:00 1691                       /system/lib64/libGLESv3.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770b6f000-700770b70000 r--p 0001b000 fe:00 1691                       /system/lib64/libGLESv3.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700770b71000-700770b72000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+700770b72000-700770b76000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700770b76000-700770b77000 rw-p 00000000 00:00 0                          [anon:linker_alloc_small_objects]
+Name:           [anon:linker_alloc_small_objects]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700770b77000-700770b79000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+700770b79000-700770b7b000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700770b7b000-700770b7c000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770b7c000-700770b7f000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770b7f000-700770b80000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770b80000-700770baa000 r--p 00000000 fe:00 1605                       /system/lib64/android.hardware.drm@1.0.so
+Size:                168 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  16 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd mr mw me 
+700770baa000-700770bf8000 r-xp 0002a000 fe:00 1605                       /system/lib64/android.hardware.drm@1.0.so
+Size:                312 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700770bf8000-700770bf9000 rw-p 00078000 fe:00 1605                       /system/lib64/android.hardware.drm@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770bf9000-700770c00000 r--p 00079000 fe:00 1605                       /system/lib64/android.hardware.drm@1.0.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         28 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            28 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me ac 
+700770c00000-700770c01000 rw-s 00000000 00:05 19682                      /dev/ashmem/GFXStats-2254 (deleted)
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   2 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd wr sh mr mw me ms 
+700770c01000-700770c02000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770c02000-700770c05000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770c05000-700770c06000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770c06000-700770c15000 r--p 00000000 fe:00 1658                       /system/lib64/libcamera2ndk.so
+Size:                 60 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  60 kB
+Pss:                  19 kB
+Shared_Clean:         60 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           60 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               19 kB
+VmFlags: rd mr mw me 
+700770c15000-700770c28000 r-xp 0000f000 fe:00 1658                       /system/lib64/libcamera2ndk.so
+Size:                 76 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700770c28000-700770c29000 rw-p 00022000 fe:00 1658                       /system/lib64/libcamera2ndk.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770c29000-700770c2b000 r--p 00023000 fe:00 1658                       /system/lib64/libcamera2ndk.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700770c2b000-700770c2c000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770c2c000-700770c2d000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+700770c2d000-700770c2f000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700770c2f000-700770c30000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770c30000-700770c33000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770c33000-700770c35000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770c35000-700770c38000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770c38000-700770c39000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770c39000-700770c3a000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770c3a000-700770c42000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770c42000-700770c43000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770c43000-700770c46000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770c46000-700770c47000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770c47000-700770c48000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770c48000-700770c50000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770c50000-700770c51000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770c51000-700770c54000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770c54000-700770c55000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770c55000-700770c67000 r--p 00000000 fe:00 1269                       /system/lib64/libmediadrmmetrics_lite.so
+Size:                 72 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  16 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd mr mw me 
+700770c67000-700770c79000 r-xp 00012000 fe:00 1269                       /system/lib64/libmediadrmmetrics_lite.so
+Size:                 72 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700770c79000-700770c7a000 rw-p 00024000 fe:00 1269                       /system/lib64/libmediadrmmetrics_lite.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770c7a000-700770c7c000 r--p 00025000 fe:00 1269                       /system/lib64/libmediadrmmetrics_lite.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700770c7c000-700770c7d000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770c7d000-700770c7f000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700770c7f000-700770c80000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770c80000-700770c88000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770c88000-700770c9c000 r--p 00000000 fe:00 1173                       /system/lib64/libexif.so
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  60 kB
+Pss:                  15 kB
+Shared_Clean:         60 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           60 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               15 kB
+VmFlags: rd mr mw me 
+700770c9c000-700770cac000 r-xp 00014000 fe:00 1173                       /system/lib64/libexif.so
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700770cac000-700770cad000 rw-p 00024000 fe:00 1173                       /system/lib64/libexif.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770cad000-700770cc0000 r--p 00025000 fe:00 1173                       /system/lib64/libexif.so
+Size:                 76 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  76 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         76 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            76 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+700770cc0000-700770cc2000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700770cc2000-700770cd8000 r--p 00000000 fe:00 1584                       /system/lib64/libmtp.so
+Size:                 88 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  16 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd mr mw me 
+700770cd8000-700770cf0000 r-xp 00016000 fe:00 1584                       /system/lib64/libmtp.so
+Size:                 96 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  88 kB
+Pss:                  58 kB
+Shared_Clean:         60 kB
+Shared_Dirty:          0 kB
+Private_Clean:        28 kB
+Private_Dirty:         0 kB
+Referenced:           88 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               58 kB
+VmFlags: rd ex mr mw me 
+700770cf0000-700770cf1000 rw-p 0002e000 fe:00 1584                       /system/lib64/libmtp.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770cf1000-700770cf4000 r--p 0002f000 fe:00 1584                       /system/lib64/libmtp.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         12 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700770cf4000-700770cf5000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770cf5000-700770cf9000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770cf9000-700770cfa000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770cfa000-700770cfd000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700770cfd000-700770cfe000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770cfe000-700770cff000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770cff000-700770d07000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770d07000-700770d08000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770d08000-700770d10000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770d10000-700770d11000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+700770d11000-700770d13000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700770d13000-700770d14000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770d14000-700770d17000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770d17000-700770d18000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770d18000-700770d38000 r--s 00000000 00:13 6730                       /dev/__properties__/u:object_r:pm_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   2 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr me ms 
+700770d38000-700770d39000 r--p 00000000 fe:00 1731                       /system/lib64/libasyncio.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+700770d39000-700770d3a000 r-xp 00001000 fe:00 1731                       /system/lib64/libasyncio.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700770d3a000-700770d3b000 rw-p 00002000 fe:00 1731                       /system/lib64/libasyncio.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770d3b000-700770d3c000 r--p 00003000 fe:00 1731                       /system/lib64/libasyncio.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700770d3d000-700770d3f000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700770d3f000-700770d40000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770d40000-700770d43000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770d43000-700770d44000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770d44000-700770d77000 r--p 00000000 fe:00 1573                       /system/lib64/libmedia_jni.so
+Size:                204 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  15 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               15 kB
+VmFlags: rd mr mw me 
+700770d77000-700770dae000 r-xp 00033000 fe:00 1573                       /system/lib64/libmedia_jni.so
+Size:                220 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700770dae000-700770daf000 rw-p 0006a000 fe:00 1573                       /system/lib64/libmedia_jni.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770daf000-700770db5000 r--p 0006b000 fe:00 1573                       /system/lib64/libmedia_jni.so
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         24 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            24 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me ac 
+700770db5000-700770db6000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770db6000-700770db7000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770db7000-700770dbf000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770dbf000-700770dc0000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770dc0000-700770dc3000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770dc3000-700770dc4000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770dc4000-700770dc5000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770dc5000-700770dcd000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770dcd000-700770dce000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770dce000-700770dd1000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770dd1000-700770dd2000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770dd2000-700770dd3000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770dd3000-700770ddb000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770ddb000-700770ddc000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770ddc000-700770de4000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770de4000-700770ded000 r--p 00000000 fe:00 1558                       /system/lib64/libmidi.so
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  36 kB
+Pss:                   9 kB
+Shared_Clean:         36 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           36 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                9 kB
+VmFlags: rd mr mw me 
+700770ded000-700770df7000 r-xp 00009000 fe:00 1558                       /system/lib64/libmidi.so
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700770df7000-700770df8000 rw-p 00013000 fe:00 1558                       /system/lib64/libmidi.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770df8000-700770dfa000 r--p 00014000 fe:00 1558                       /system/lib64/libmidi.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700770dfa000-700770dfb000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770dfb000-700770dfc000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770dfc000-700770dff000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770dff000-700770e00000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770e00000-700770e1a000 r--p 00000000 fe:00 1744                       /system/lib64/libmediadrm.so
+Size:                104 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  16 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd mr mw me 
+700770e1a000-700770e32000 r-xp 0001a000 fe:00 1744                       /system/lib64/libmediadrm.so
+Size:                 96 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700770e32000-700770e33000 rw-p 00032000 fe:00 1744                       /system/lib64/libmediadrm.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770e33000-700770e3a000 r--p 00033000 fe:00 1744                       /system/lib64/libmediadrm.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         28 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            28 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me ac 
+700770e3a000-700770e3b000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770e3b000-700770e3c000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770e3c000-700770e3f000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770e3f000-700770e40000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770e40000-700770e41000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770e41000-700770e49000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770e49000-700770e59000 r--p 00000000 fe:00 1239                       /system/lib64/libmediandk.so
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  14 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               14 kB
+VmFlags: rd mr mw me 
+700770e59000-700770e66000 r-xp 00010000 fe:00 1239                       /system/lib64/libmediandk.so
+Size:                 52 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  52 kB
+Pss:                  26 kB
+Shared_Clean:         52 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           52 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               26 kB
+VmFlags: rd ex mr mw me 
+700770e66000-700770e67000 rw-p 0001d000 fe:00 1239                       /system/lib64/libmediandk.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770e67000-700770e69000 r--p 0001e000 fe:00 1239                       /system/lib64/libmediandk.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700770e69000-700770e6a000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770e6a000-700770e6e000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+700770e6e000-700770e6f000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770e6f000-700770e77000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770e77000-700770e97000 r--s 00000000 00:13 6713                       /dev/__properties__/u:object_r:hwservicemanager_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+700770e97000-700770eb7000 r--p 00000000 fe:00 1647                       /system/lib64/android.hardware.drm@1.1.so
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  16 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd mr mw me 
+700770eb7000-700770ee3000 r-xp 00020000 fe:00 1647                       /system/lib64/android.hardware.drm@1.1.so
+Size:                176 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700770ee3000-700770ee4000 rw-p 0004c000 fe:00 1647                       /system/lib64/android.hardware.drm@1.1.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770ee4000-700770eea000 r--p 0004d000 fe:00 1647                       /system/lib64/android.hardware.drm@1.1.so
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         24 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            24 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me ac 
+700770eea000-700770eeb000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+700770eeb000-700770eed000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700770eed000-700770eee000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770eee000-700770ef1000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770ef1000-700770ef2000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770ef2000-700770ef3000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770ef3000-700770efb000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770efb000-700770f1b000 r--s 00000000 00:13 6745                       /dev/__properties__/u:object_r:vendor_default_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+700770f1b000-700770f22000 r--p 00000000 fe:00 1701                       /system/lib64/libbinder_ndk.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                   9 kB
+Shared_Clean:         28 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           28 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                9 kB
+VmFlags: rd mr mw me 
+700770f22000-700770f28000 r-xp 00007000 fe:00 1701                       /system/lib64/libbinder_ndk.so
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700770f28000-700770f29000 rw-p 0000d000 fe:00 1701                       /system/lib64/libbinder_ndk.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770f29000-700770f2a000 r--p 0000e000 fe:00 1701                       /system/lib64/libbinder_ndk.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700770f2a000-700770f2b000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770f2b000-700770f2c000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770f2c000-700770f2f000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770f2f000-700770f31000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770f31000-700770f34000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770f34000-700770f35000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770f35000-700770f36000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770f36000-700770f3e000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770f3e000-700770f3f000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770f3f000-700770f47000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770f47000-700770f62000 r--p 00000000 fe:00 1703                       /system/lib64/libaaudio.so
+Size:                108 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  21 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               21 kB
+VmFlags: rd mr mw me 
+700770f62000-700770f74000 r-xp 0001b000 fe:00 1703                       /system/lib64/libaaudio.so
+Size:                 72 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700770f74000-700770f75000 rw-p 0002d000 fe:00 1703                       /system/lib64/libaaudio.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770f75000-700770f7a000 r--p 0002e000 fe:00 1703                       /system/lib64/libaaudio.so
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         20 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            20 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me ac 
+700770f7a000-700770f7b000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770f7b000-700770f7c000 rw-s 00000000 00:05 11676                      /dev/ashmem/GFXStats-1674 (deleted)
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   2 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd wr sh mr mw me ms 
+700770f7c000-700770fa0000 r--s 00000000 fe:00 985                        /system/usr/hyphen-data/hyph-nn.hyb
+Size:                144 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+700770fa0000-700770faf000 r--p 00000000 fe:00 1602                       /system/lib64/libandroid.so
+Size:                 60 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  60 kB
+Pss:                  15 kB
+Shared_Clean:         60 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           60 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               15 kB
+VmFlags: rd mr mw me 
+700770faf000-700770fb8000 r-xp 0000f000 fe:00 1602                       /system/lib64/libandroid.so
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700770fb8000-700770fb9000 rw-p 00018000 fe:00 1602                       /system/lib64/libandroid.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770fb9000-700770fbb000 r--p 00019000 fe:00 1602                       /system/lib64/libandroid.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700770fbb000-700770fbc000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770fbc000-700770fc0000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700770fc0000-700770fc1000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700770fc1000-700770fc9000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770fc9000-700770fce000 r--p 00000000 fe:00 1736                       /system/lib64/libadbconnection.so
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                   1 kB
+Shared_Clean:         20 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           20 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+700770fce000-700770fd6000 r-xp 00005000 fe:00 1736                       /system/lib64/libadbconnection.so
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  32 kB
+Pss:                   1 kB
+Shared_Clean:         32 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           32 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd ex mr mw me 
+700770fd6000-700770fd7000 rw-p 0000d000 fe:00 1736                       /system/lib64/libadbconnection.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770fd7000-700770fd8000 r--p 0000e000 fe:00 1736                       /system/lib64/libadbconnection.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700770fd8000-700770fd9000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770fd9000-700770fda000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770fda000-700770fdd000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700770fdd000-700770fde000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700770fde000-70077102f000 r--s 00000000 fe:00 231                        /system/fonts/Roboto-BlackItalic.ttf
+Size:                324 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+70077102f000-7007711bf000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:               1600 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                  20 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:        20 kB
+Referenced:           24 kB
+Anonymous:            24 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               20 kB
+VmFlags: rd wr mr mw me ac 
+7007711bf000-7007712bf000 rw-p 00000000 00:00 0                          [anon:dalvik-non-moving-space inter region ref bitmap]
+Name:           [anon:dalvik-non-moving-space inter region ref bitmap]
+Size:               1024 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007712bf000-7007722bf000 rw-p 00000000 00:00 0                          [anon:dalvik-region-space inter region ref bitmap]
+Name:           [anon:dalvik-region-space inter region ref bitmap]
+Size:              16384 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007722bf000-7007724bf000 rw-p 00000000 00:00 0                          [anon:dalvik-rb copying gc mark stack]
+Name:           [anon:dalvik-rb copying gc mark stack]
+Size:               2048 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007724bf000-700772cbf000 rw-p 00000000 00:00 0                          [anon:dalvik-concurrent copying gc mark stack]
+Name:           [anon:dalvik-concurrent copying gc mark stack]
+Size:               8192 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700772cbf000-700772ebf000 rw-p 00000000 00:00 0                          [anon:dalvik-rb copying gc mark stack]
+Name:           [anon:dalvik-rb copying gc mark stack]
+Size:               2048 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700772ebf000-7007736bf000 rw-p 00000000 00:00 0                          [anon:dalvik-concurrent copying gc mark stack]
+Name:           [anon:dalvik-concurrent copying gc mark stack]
+Size:               8192 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007736bf000-700773ec0000 rw-p 00000000 00:00 0                          [anon:dalvik-live stack]
+Name:           [anon:dalvik-live stack]
+Size:               8196 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700773ec0000-7007746c1000 rw-p 00000000 00:00 0                          [anon:dalvik-allocation stack]
+Name:           [anon:dalvik-allocation stack]
+Size:               8196 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007746c1000-700774ac2000 rw-p 00000000 00:00 0                          [anon:dalvik-card table]
+Name:           [anon:dalvik-card table]
+Size:               4100 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  56 kB
+Pss:                  56 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        56 kB
+Referenced:           56 kB
+Anonymous:            56 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               56 kB
+VmFlags: rd wr mr mw me ac 
+700774ac2000-700775ac2000 rw-p 00000000 00:00 0                          [anon:dalvik-region space live bitmap]
+Name:           [anon:dalvik-region space live bitmap]
+Size:              16384 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  84 kB
+Pss:                  84 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        84 kB
+Referenced:           84 kB
+Anonymous:            84 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               84 kB
+VmFlags: rd wr mr mw me ac 
+700775ac2000-700775bc2000 rw-p 00000000 00:00 0                          [anon:dalvik-allocspace zygote / non moving space mark-bitmap 0]
+Name:           [anon:dalvik-allocspace zygote / non moving space mark-bitmap 0]
+Size:               1024 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700775bc2000-700775cc2000 rw-p 00000000 00:00 0                          [anon:dalvik-allocspace zygote / non moving space live-bitmap 0]
+Name:           [anon:dalvik-allocspace zygote / non moving space live-bitmap 0]
+Size:               1024 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         24 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           24 kB
+Anonymous:            24 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd wr mr mw me ac 
+700775cc2000-700775cc4000 r--p 00000000 fe:00 1730                       /system/lib64/libsigchain.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+700775cc4000-700775cc6000 r-xp 00002000 fe:00 1730                       /system/lib64/libsigchain.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700775cc6000-700775cc7000 rw-p 00004000 fe:00 1730                       /system/lib64/libsigchain.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700775cc7000-700775cc8000 r--p 00005000 fe:00 1730                       /system/lib64/libsigchain.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700775cc8000-700775ccc000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+700775ccc000-700775ccd000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700775ccd000-700775cd5000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700775cd5000-700775d26000 r--s 00000000 fe:00 219                        /system/fonts/Roboto-MediumItalic.ttf
+Size:                324 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+700775d26000-700775d27000 r--p 00000000 fe:00 1509                       /system/lib64/libstatssocket.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+700775d27000-700775d28000 r-xp 00001000 fe:00 1509                       /system/lib64/libstatssocket.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700775d28000-700775d29000 rw-p 00002000 fe:00 1509                       /system/lib64/libstatssocket.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700775d29000-700775d2a000 r--p 00003000 fe:00 1509                       /system/lib64/libstatssocket.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700775d2a000-700775d2b000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700775d2b000-700775d2f000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700775d2f000-700775d7a000 r--s 00000000 fe:00 275                        /system/fonts/Roboto-Bold.ttf
+Size:                300 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+700775d7a000-700775ece000 r--p 00000000 fe:00 1156                       /system/lib64/libart.so
+Size:               1360 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 384 kB
+Pss:                  39 kB
+Shared_Clean:        384 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          384 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               39 kB
+VmFlags: rd mr mw me 
+700775ece000-7007763e6000 r-xp 00154000 fe:00 1156                       /system/lib64/libart.so
+Size:               5216 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                4276 kB
+Pss:                 288 kB
+Shared_Clean:       4272 kB
+Shared_Dirty:          0 kB
+Private_Clean:         4 kB
+Private_Dirty:         0 kB
+Referenced:         4276 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              288 kB
+VmFlags: rd ex mr mw me 
+7007763e6000-7007763e9000 rw-p 0066c000 fe:00 1156                       /system/lib64/libart.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            8 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007763e9000-7007763f9000 r--p 0066f000 fe:00 1156                       /system/lib64/libart.so
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                   3 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         64 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           56 kB
+Anonymous:            64 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me ac 
+7007763f9000-7007763fc000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   6 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd wr mr mw me ac 
+7007763fc000-700776400000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+700776400000-700776800000 rw-p 00000000 00:00 0                          [anon:libc_malloc]
+Name:           [anon:libc_malloc]
+Size:               4096 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                4080 kB
+Pss:                3230 kB
+Shared_Clean:          0 kB
+Shared_Dirty:        900 kB
+Private_Clean:         0 kB
+Private_Dirty:      3180 kB
+Referenced:         3992 kB
+Anonymous:          4080 kB
+AnonHugePages:      2048 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             3230 kB
+VmFlags: rd wr mr mw me ac 
+700776800000-700776804000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+700776804000-700776805000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+700776805000-700776807000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700776807000-700776808000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+700776808000-700776810000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700776810000-700776861000 r--s 00000000 fe:00 195                        /system/fonts/Roboto-Italic.ttf
+Size:                324 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+700776861000-700776862000 r--p 00000000 fe:00 1199                       /system/lib64/libmetricslogger.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+700776862000-700776863000 r-xp 00001000 fe:00 1199                       /system/lib64/libmetricslogger.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700776863000-700776864000 rw-p 00002000 fe:00 1199                       /system/lib64/libmetricslogger.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700776864000-700776865000 r--p 00003000 fe:00 1199                       /system/lib64/libmetricslogger.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700776865000-700776866000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700776866000-700776867000 r--s 00009000 fe:00 2036                       /system/priv-app/FusedLocation/FusedLocation.apk
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         4 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr me ms 
+700776867000-700776868000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700776868000-70077686b000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+70077686b000-70077686c000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70077686c000-700776890000 r--s 00000000 fe:00 1019                       /system/usr/hyphen-data/hyph-nb.hyb
+Size:                144 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+700776890000-700776892000 r--p 00000000 fe:00 1606                       /system/lib64/libtombstoned_client.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   2 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me 
+700776892000-700776895000 r-xp 00002000 fe:00 1606                       /system/lib64/libtombstoned_client.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+700776895000-700776896000 rw-p 00005000 fe:00 1606                       /system/lib64/libtombstoned_client.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700776896000-700776897000 r--p 00006000 fe:00 1606                       /system/lib64/libtombstoned_client.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+700776897000-700776899000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+700776899000-70077689a000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70077689a000-70077689d000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+70077689d000-70077689f000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+70077689f000-7007768a2000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007768a2000-7007768a3000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007768a3000-7007768c1000 r--s 00000000 fe:00 971                        /system/usr/hyphen-data/hyph-de-ch-1901.hyb
+Size:                120 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007768c1000-7007768d2000 r--p 00000000 fe:00 1651                       /system/lib64/libprofile.so
+Size:                 68 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                   3 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me 
+7007768d2000-7007768f3000 r-xp 00011000 fe:00 1651                       /system/lib64/libprofile.so
+Size:                132 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007768f3000-7007768f4000 rw-p 00032000 fe:00 1651                       /system/lib64/libprofile.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007768f4000-7007768f5000 r--p 00033000 fe:00 1651                       /system/lib64/libprofile.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007768f5000-7007768f6000 rw-p 00000000 00:00 0                          [anon:linker_alloc_small_objects]
+Name:           [anon:linker_alloc_small_objects]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007768f6000-7007768f8000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007768f8000-7007768f9000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007768f9000-700776901000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700776901000-700776902000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700776902000-700776905000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+700776905000-700776906000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+700776906000-700776909000 r--p 00000000 00:00 0                          [anon:cfi shadow]
+Name:           [anon:cfi shadow]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me nr 
+700776909000-70077690a000 r--p 00000000 00:00 0                          [anon:cfi shadow]
+Name:           [anon:cfi shadow]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+70077690a000-7007a2575000 r--p 00000000 00:00 0                          [anon:cfi shadow]
+Name:           [anon:cfi shadow]
+Size:             717228 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me nr 
+7007a2575000-7007a2576000 r--p 00000000 00:00 0                          [anon:cfi shadow]
+Name:           [anon:cfi shadow]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007a2576000-7007ae940000 r--p 00000000 00:00 0                          [anon:cfi shadow]
+Name:           [anon:cfi shadow]
+Size:             200488 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me nr 
+7007ae940000-7007ae941000 r--p 00000000 00:00 0                          [anon:cfi shadow]
+Name:           [anon:cfi shadow]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007ae941000-7007ae942000 r--p 00000000 00:00 0                          [anon:cfi shadow]
+Name:           [anon:cfi shadow]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007ae942000-7007ae945000 r--p 00000000 00:00 0                          [anon:cfi shadow]
+Name:           [anon:cfi shadow]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me nr 
+7007ae945000-7007ae946000 r--p 00000000 00:00 0                          [anon:cfi shadow]
+Name:           [anon:cfi shadow]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007ae946000-7007b68f5000 r--p 00000000 00:00 0                          [anon:cfi shadow]
+Name:           [anon:cfi shadow]
+Size:             130748 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me nr 
+7007b68f5000-7007b68f6000 r--p 00000000 00:00 0                          [anon:cfi shadow]
+Name:           [anon:cfi shadow]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007b68f6000-7007f6906000 r--p 00000000 00:00 0                          [anon:cfi shadow]
+Name:           [anon:cfi shadow]
+Size:            1048640 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me nr 
+7007f6906000-7007f690d000 r--p 00000000 fe:00 1707                       /system/lib64/libmediautils.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                   4 kB
+Shared_Clean:         28 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           28 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me 
+7007f690d000-7007f6912000 r-xp 00007000 fe:00 1707                       /system/lib64/libmediautils.so
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f6912000-7007f6913000 rw-p 0000c000 fe:00 1707                       /system/lib64/libmediautils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6913000-7007f6915000 r--p 0000d000 fe:00 1707                       /system/lib64/libmediautils.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f6915000-7007f6916000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6916000-7007f6917000 rw-p 00000000 00:00 0                          [anon:linker_alloc_small_objects]
+Name:           [anon:linker_alloc_small_objects]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f6917000-7007f6919000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f6919000-7007f691a000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007f691a000-7007f6922000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6922000-7007f6940000 r--s 00000000 fe:00 976                        /system/usr/hyphen-data/hyph-de-1996.hyb
+Size:                120 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6940000-7007f694d000 r--p 00000000 fe:00 1526                       /system/lib64/libmedia_helper.so
+Size:                 52 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  52 kB
+Pss:                   8 kB
+Shared_Clean:         52 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           52 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me 
+7007f694d000-7007f6955000 r-xp 0000d000 fe:00 1526                       /system/lib64/libmedia_helper.so
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f6955000-7007f6956000 rw-p 00015000 fe:00 1526                       /system/lib64/libmedia_helper.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6956000-7007f6958000 r--p 00016000 fe:00 1526                       /system/lib64/libmedia_helper.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f6958000-7007f6959000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6959000-7007f695a000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007f695a000-7007f695c000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f695c000-7007f695d000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007f695d000-7007f6960000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6960000-7007f6961000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007f6961000-7007f6963000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f6963000-7007f6964000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007f6964000-7007f696c000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f696c000-7007f696d000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007f696d000-7007f6970000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+7007f6970000-7007f6971000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007f6971000-7007f698f000 r--s 00000000 fe:00 991                        /system/usr/hyphen-data/hyph-de-1901.hyb
+Size:                120 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f698f000-7007f699d000 r--p 00000000 fe:00 1227                       /system/lib64/libminikin.so
+Size:                 56 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  52 kB
+Pss:                   8 kB
+Shared_Clean:         52 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           52 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me 
+7007f699d000-7007f69b5000 r-xp 0000e000 fe:00 1227                       /system/lib64/libminikin.so
+Size:                 96 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  96 kB
+Pss:                  24 kB
+Shared_Clean:         96 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           96 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               24 kB
+VmFlags: rd ex mr mw me 
+7007f69b5000-7007f69b6000 rw-p 00026000 fe:00 1227                       /system/lib64/libminikin.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f69b6000-7007f69b7000 r--p 00027000 fe:00 1227                       /system/lib64/libminikin.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f69b7000-7007f69b8000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f69b8000-7007f69ba000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f69ba000-7007f69c1000 rw-p 00000000 fe:00 944                        /system/etc/event-log-tags
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                   2 kB
+Shared_Clean:         28 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           28 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd wr mr mw me ac 
+7007f69c1000-7007f69c2000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007f69c2000-7007f69ca000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f69ca000-7007f69d4000 r--p 00000000 fe:00 1135                       /system/lib64/android.hidl.memory.token@1.0.so
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  40 kB
+Pss:                   7 kB
+Shared_Clean:         40 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           40 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                7 kB
+VmFlags: rd mr mw me 
+7007f69d4000-7007f69dd000 r-xp 0000a000 fe:00 1135                       /system/lib64/android.hidl.memory.token@1.0.so
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f69dd000-7007f69de000 rw-p 00013000 fe:00 1135                       /system/lib64/android.hidl.memory.token@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f69de000-7007f69e0000 r--p 00014000 fe:00 1135                       /system/lib64/android.hidl.memory.token@1.0.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f69e0000-7007f69e1000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007f69e1000-7007f69e4000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f69e4000-7007f69e6000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007f69e6000-7007f69e9000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f69e9000-7007f69ea000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007f69ea000-7007f69eb000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007f69eb000-7007f69f3000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f69f3000-7007f69f4000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007f69f4000-7007f69fc000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f69fc000-7007f6a1c000 r--s 00000000 00:13 6716                       /dev/__properties__/u:object_r:log_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6a1c000-7007f6a1d000 r--p 00000000 fe:00 1746                       /system/lib64/android.hardware.configstore-utils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007f6a1d000-7007f6a1e000 r-xp 00001000 fe:00 1746                       /system/lib64/android.hardware.configstore-utils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd ex mr mw me 
+7007f6a1e000-7007f6a1f000 rw-p 00002000 fe:00 1746                       /system/lib64/android.hardware.configstore-utils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6a1f000-7007f6a20000 r--p 00003000 fe:00 1746                       /system/lib64/android.hardware.configstore-utils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f6a20000-7007f6a21000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007f6a21000-7007f6a24000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6a24000-7007f6a25000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007f6a25000-7007f6a45000 rw-p 00000000 00:00 0                          [anon:dalvik-CompilerMetadata]
+Name:           [anon:dalvik-CompilerMetadata]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6a45000-7007f6a56000 r--p 00000000 fe:00 1599                       /system/lib64/libvulkan.so
+Size:                 68 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  12 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd mr mw me 
+7007f6a56000-7007f6a69000 r-xp 00011000 fe:00 1599                       /system/lib64/libvulkan.so
+Size:                 76 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f6a69000-7007f6a6a000 rw-p 00024000 fe:00 1599                       /system/lib64/libvulkan.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6a6a000-7007f6a6c000 r--p 00025000 fe:00 1599                       /system/lib64/libvulkan.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f6a6c000-7007f6a6d000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6a6d000-7007f6a8d000 rw-p 00000000 00:00 0                          [anon:dalvik-CompilerMetadata]
+Name:           [anon:dalvik-CompilerMetadata]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6a8d000-7007f6ab4000 r--p 00000000 fe:00 1727                       /system/lib64/libandroidicu.so
+Size:                156 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  16 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd mr mw me 
+7007f6ab4000-7007f6abc000 r-xp 00027000 fe:00 1727                       /system/lib64/libandroidicu.so
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f6abc000-7007f6abd000 rw-p 0002f000 fe:00 1727                       /system/lib64/libandroidicu.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6abd000-7007f6abf000 r--p 00030000 fe:00 1727                       /system/lib64/libandroidicu.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f6abf000-7007f6ac3000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f6ac3000-7007f6ac4000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007f6ac4000-7007f6acc000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6acc000-7007f6acd000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007f6acd000-7007f6ad0000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f6ad0000-7007f6ad1000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007f6ad1000-7007f6ad2000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007f6ad2000-7007f6ada000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6ada000-7007f6ae9000 r--p 00000000 fe:00 1717                       /system/lib64/android.hardware.configstore@1.1.so
+Size:                 60 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  60 kB
+Pss:                   5 kB
+Shared_Clean:         60 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           60 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                5 kB
+VmFlags: rd mr mw me 
+7007f6ae9000-7007f6af7000 r-xp 0000f000 fe:00 1717                       /system/lib64/android.hardware.configstore@1.1.so
+Size:                 56 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f6af7000-7007f6af8000 rw-p 0001d000 fe:00 1717                       /system/lib64/android.hardware.configstore@1.1.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6af8000-7007f6afb000 r--p 0001e000 fe:00 1717                       /system/lib64/android.hardware.configstore@1.1.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         12 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f6afb000-7007f6afc000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007f6afc000-7007f6afe000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f6afe000-7007f6aff000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007f6aff000-7007f6b02000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6b02000-7007f6b04000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007f6b04000-7007f6b07000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6b07000-7007f6b08000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007f6b08000-7007f6b09000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007f6b09000-7007f6b11000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6b11000-7007f6b62000 r--p 00000000 fe:00 1580                       /system/lib64/libbinder.so
+Size:                324 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 252 kB
+Pss:                   9 kB
+Shared_Clean:        252 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          252 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                9 kB
+VmFlags: rd mr mw me 
+7007f6b62000-7007f6ba1000 r-xp 00051000 fe:00 1580                       /system/lib64/libbinder.so
+Size:                252 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 248 kB
+Pss:                   6 kB
+Shared_Clean:        248 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          248 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd ex mr mw me 
+7007f6ba1000-7007f6ba2000 rw-p 00090000 fe:00 1580                       /system/lib64/libbinder.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f6ba2000-7007f6bb1000 r--p 00091000 fe:00 1580                       /system/lib64/libbinder.so
+Size:                 60 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  60 kB
+Pss:                   3 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         60 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           40 kB
+Anonymous:            60 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me ac 
+7007f6bb1000-7007f6bb2000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f6bb2000-7007f6bb3000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007f6bb3000-7007f6bb6000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6bb6000-7007f6bb7000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007f6bb7000-7007f6bb8000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007f6bb8000-7007f6bc0000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6bc0000-7007f6bc1000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007f6bc1000-7007f6bc9000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6bc9000-7007f6bdf000 r--p 00000000 fe:00 1694                       /system/lib64/libinput.so
+Size:                 88 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  88 kB
+Pss:                  20 kB
+Shared_Clean:         88 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           88 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               20 kB
+VmFlags: rd mr mw me 
+7007f6bdf000-7007f6bf3000 r-xp 00016000 fe:00 1694                       /system/lib64/libinput.so
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  72 kB
+Pss:                  18 kB
+Shared_Clean:         72 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           72 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               18 kB
+VmFlags: rd ex mr mw me 
+7007f6bf3000-7007f6bf4000 rw-p 0002a000 fe:00 1694                       /system/lib64/libinput.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6bf4000-7007f6bfb000 r--p 0002b000 fe:00 1694                       /system/lib64/libinput.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         28 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           24 kB
+Anonymous:            28 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me ac 
+7007f6bfb000-7007f6bfc000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6bfc000-7007f6bfd000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007f6bfd000-7007f6c05000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6c05000-7007f6c0e000 r--s 00000000 fe:00 972                        /system/usr/hyphen-data/hyph-ga.hyb
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6c0e000-7007f6c1a000 r--p 00000000 fe:00 1613                       /system/lib64/android.hardware.graphics.mapper@2.0.so
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  48 kB
+Pss:                   4 kB
+Shared_Clean:         48 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           48 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me 
+7007f6c1a000-7007f6c26000 r-xp 0000c000 fe:00 1613                       /system/lib64/android.hardware.graphics.mapper@2.0.so
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  48 kB
+Pss:                   6 kB
+Shared_Clean:         48 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           48 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd ex mr mw me 
+7007f6c26000-7007f6c27000 rw-p 00018000 fe:00 1613                       /system/lib64/android.hardware.graphics.mapper@2.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6c27000-7007f6c29000 r--p 00019000 fe:00 1613                       /system/lib64/android.hardware.graphics.mapper@2.0.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f6c29000-7007f6c2b000 r--p 0007b000 fe:00 3288                       /system/framework/oat/x86_64/services.art
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         8 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me 
+7007f6c2b000-7007f6c3a000 r--s 00000000 fe:00 984                        /system/usr/hyphen-data/hyph-en-us.hyb
+Size:                 60 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  60 kB
+Pss:                  30 kB
+Shared_Clean:         60 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           60 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               30 kB
+VmFlags: rd mr me ms 
+7007f6c3a000-7007f6c46000 r--s 00000000 fe:00 1020                       /system/usr/hyphen-data/hyph-en-gb.hyb
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6c46000-7007f6c5b000 r--p 00000000 fe:00 1210                       /system/lib64/libstagefright_foundation.so
+Size:                 84 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  10 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               10 kB
+VmFlags: rd mr mw me 
+7007f6c5b000-7007f6c7a000 r-xp 00015000 fe:00 1210                       /system/lib64/libstagefright_foundation.so
+Size:                124 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 124 kB
+Pss:                  27 kB
+Shared_Clean:        124 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          124 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               27 kB
+VmFlags: rd ex mr mw me 
+7007f6c7a000-7007f6c7b000 rw-p 00034000 fe:00 1210                       /system/lib64/libstagefright_foundation.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6c7b000-7007f6c7d000 r--p 00035000 fe:00 1210                       /system/lib64/libstagefright_foundation.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f6c7d000-7007f6c7e000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f6c7e000-7007f6c7f000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007f6c7f000-7007f6c81000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f6c81000-7007f6ca1000 rw-p 00000000 00:00 0                          [anon:dalvik-CompilerMetadata]
+Name:           [anon:dalvik-CompilerMetadata]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6ca1000-7007f6ca3000 r--p 00000000 fe:00 1711                       /system/lib64/libhardware_legacy.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   1 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+7007f6ca3000-7007f6ca5000 r-xp 00002000 fe:00 1711                       /system/lib64/libhardware_legacy.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         8 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd ex mr mw me 
+7007f6ca5000-7007f6ca6000 rw-p 00004000 fe:00 1711                       /system/lib64/libhardware_legacy.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f6ca6000-7007f6ca7000 r--p 00005000 fe:00 1711                       /system/lib64/libhardware_legacy.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f6ca7000-7007f6ca8000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f6ca8000-7007f6ca9000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007f6ca9000-7007f6cac000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f6cac000-7007f6cad000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007f6cad000-7007f6cba000 r--s 00000000 fe:00 1022                       /system/usr/hyphen-data/hyph-cu.hyb
+Size:                 52 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6cba000-7007f6cda000 rw-p 00000000 00:00 0                          [anon:dalvik-LinearAlloc]
+Name:           [anon:dalvik-LinearAlloc]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 128 kB
+Pss:                 128 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:       128 kB
+Referenced:          128 kB
+Anonymous:           128 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              128 kB
+VmFlags: rd wr mr mw me ac 
+7007f6cda000-7007f6cdf000 r--p 00000000 fe:00 1678                       /system/lib64/libappfuse.so
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                   5 kB
+Shared_Clean:         20 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           20 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                5 kB
+VmFlags: rd mr mw me 
+7007f6cdf000-7007f6ce6000 r-xp 00005000 fe:00 1678                       /system/lib64/libappfuse.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f6ce6000-7007f6ce7000 rw-p 0000c000 fe:00 1678                       /system/lib64/libappfuse.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6ce7000-7007f6ce8000 r--p 0000d000 fe:00 1678                       /system/lib64/libappfuse.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f6ce8000-7007f6cea000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f6cea000-7007f6cf3000 r--s 00000000 fe:00 1000                       /system/usr/hyphen-data/hyph-cy.hyb
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6cf3000-7007f6d0c000 r--s 00000000 fe:00 162                        /system/fonts/NotoSansBhaiksuki-Regular.otf
+Size:                100 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6d0c000-7007f6d18000 r--p 00000000 fe:00 1607                       /system/lib64/android.hardware.cas.native@1.0.so
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  48 kB
+Pss:                   9 kB
+Shared_Clean:         48 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           48 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                9 kB
+VmFlags: rd mr mw me 
+7007f6d18000-7007f6d23000 r-xp 0000c000 fe:00 1607                       /system/lib64/android.hardware.cas.native@1.0.so
+Size:                 44 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f6d23000-7007f6d24000 rw-p 00017000 fe:00 1607                       /system/lib64/android.hardware.cas.native@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6d24000-7007f6d26000 r--p 00018000 fe:00 1607                       /system/lib64/android.hardware.cas.native@1.0.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f6d26000-7007f6d2c000 r--s 00000000 fe:00 1025                       /system/usr/hyphen-data/hyph-et.hyb
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6d2c000-7007f6d3d000 r--s 00000000 fe:00 274                        /system/fonts/NotoSansNewa-Regular.otf
+Size:                 68 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6d3d000-7007f6d5e000 r--s 00000000 fe:00 142                        /system/fonts/NotoSansAnatolianHieroglyphs-Regular.otf
+Size:                132 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6d5e000-7007f6d60000 r--p 00000000 fe:00 1650                       /system/lib64/libstagefright_omx_utils.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   1 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+7007f6d60000-7007f6d62000 r-xp 00002000 fe:00 1650                       /system/lib64/libstagefright_omx_utils.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   4 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd ex mr mw me 
+7007f6d62000-7007f6d63000 rw-p 00004000 fe:00 1650                       /system/lib64/libstagefright_omx_utils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6d63000-7007f6d64000 r--p 00005000 fe:00 1650                       /system/lib64/libstagefright_omx_utils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f6d64000-7007f6d65000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f6d65000-7007f6d67000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f6d67000-7007f6db2000 r--s 00000000 fe:00 144                        /system/fonts/Roboto-Black.ttf
+Size:                300 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6db2000-7007f6db5000 r--p 00000000 fe:00 1142                       /system/lib64/libhidlmemory.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   2 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me 
+7007f6db5000-7007f6db7000 r-xp 00003000 fe:00 1142                       /system/lib64/libhidlmemory.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   2 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd ex mr mw me 
+7007f6db7000-7007f6db8000 rw-p 00005000 fe:00 1142                       /system/lib64/libhidlmemory.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6db8000-7007f6db9000 r--p 00006000 fe:00 1142                       /system/lib64/libhidlmemory.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f6db9000-7007f6dba000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f6dba000-7007f6dca000 r--s 00000000 fe:00 119                        /system/fonts/NotoSansMarchen-Regular.otf
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6dca000-7007f6dcc000 r--p 00000000 fe:00 1469                       /system/lib64/libprocessgroup.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007f6dcc000-7007f6dcf000 r-xp 00002000 fe:00 1469                       /system/lib64/libprocessgroup.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   0 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f6dcf000-7007f6dd0000 rw-p 00005000 fe:00 1469                       /system/lib64/libprocessgroup.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6dd0000-7007f6dd1000 r--p 00006000 fe:00 1469                       /system/lib64/libprocessgroup.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f6dd1000-7007f6dd2000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6dd2000-7007f6dd3000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007f6dd3000-7007f6dd7000 r--s 00000000 fe:00 1008                       /system/usr/hyphen-data/hyph-es.hyb
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6dd7000-7007f6dde000 r--s 00000000 fe:00 132                        /system/fonts/NotoSansSharada-Regular.otf
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6dde000-7007f6de7000 r--s 00000000 fe:00 157                        /system/fonts/NotoSansLinearA-Regular.otf
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6de7000-7007f6e03000 r--s 00000000 fe:00 139                        /system/fonts/NotoSansMongolian-Regular.ttf
+Size:                112 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6e03000-7007f6e15000 r--p 00000000 fe:00 1511                       /system/lib64/libpdx_default_transport.so
+Size:                 72 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                   9 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                9 kB
+VmFlags: rd mr mw me 
+7007f6e15000-7007f6e29000 r-xp 00012000 fe:00 1511                       /system/lib64/libpdx_default_transport.so
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f6e29000-7007f6e2a000 rw-p 00026000 fe:00 1511                       /system/lib64/libpdx_default_transport.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6e2a000-7007f6e2b000 r--p 00027000 fe:00 1511                       /system/lib64/libpdx_default_transport.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f6e2b000-7007f6e2c000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6e2c000-7007f6e2d000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007f6e2d000-7007f6e2f000 r--s 00000000 fe:00 964                        /system/usr/hyphen-data/hyph-sl.hyb
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6e2f000-7007f6e4e000 r--s 00000000 fe:00 289                        /system/fonts/NotoSansYi-Regular.ttf
+Size:                124 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6e4e000-7007f6e60000 r--p 00000000 fe:00 1545                       /system/lib64/libEGL.so
+Size:                 72 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  72 kB
+Pss:                   8 kB
+Shared_Clean:         72 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           72 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me 
+7007f6e60000-7007f6e73000 r-xp 00012000 fe:00 1545                       /system/lib64/libEGL.so
+Size:                 76 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  76 kB
+Pss:                   3 kB
+Shared_Clean:         76 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           76 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd ex mr mw me 
+7007f6e73000-7007f6e74000 rw-p 00025000 fe:00 1545                       /system/lib64/libEGL.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f6e74000-7007f6e78000 r--p 00026000 fe:00 1545                       /system/lib64/libEGL.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         16 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f6e78000-7007f6e80000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  32 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         16 kB
+Private_Clean:         0 kB
+Private_Dirty:        16 kB
+Referenced:           20 kB
+Anonymous:            32 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd wr mr mw me ac 
+7007f6e80000-7007f6e82000 r--s 00000000 fe:00 983                        /system/usr/hyphen-data/hyph-mn-cyrl.hyb
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6e82000-7007f6e91000 r--p 00000000 fe:00 1200                       /system/lib64/libutils.so
+Size:                 60 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  60 kB
+Pss:                   1 kB
+Shared_Clean:         60 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           60 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+7007f6e91000-7007f6e9d000 r-xp 0000f000 fe:00 1200                       /system/lib64/libutils.so
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  48 kB
+Pss:                   1 kB
+Shared_Clean:         48 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           48 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd ex mr mw me 
+7007f6e9d000-7007f6e9e000 rw-p 0001b000 fe:00 1200                       /system/lib64/libutils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6e9e000-7007f6e9f000 r--p 0001c000 fe:00 1200                       /system/lib64/libutils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f6e9f000-7007f6ea0000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f6ea1000-7007f6eb1000 r--s 00000000 fe:00 220                        /system/fonts/NotoSansVai-Regular.ttf
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6eb1000-7007f6eca000 r--s 00000000 fe:00 154                        /system/fonts/NotoSansLepcha-Regular.ttf
+Size:                100 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6eca000-7007f6ecb000 r--p 00000000 fe:00 1636                       /system/lib64/android.hardware.graphics.common@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007f6ecb000-7007f6ecc000 r-xp 00001000 fe:00 1636                       /system/lib64/android.hardware.graphics.common@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f6ecc000-7007f6ecd000 rw-p 00002000 fe:00 1636                       /system/lib64/android.hardware.graphics.common@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6ecd000-7007f6ece000 r--p 00003000 fe:00 1636                       /system/lib64/android.hardware.graphics.common@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f6ece000-7007f6ed0000 r--s 00000000 fe:00 986                        /system/usr/hyphen-data/hyph-fr.hyb
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6ed0000-7007f6edc000 r--s 00000000 fe:00 156                        /system/fonts/NotoSansTaiTham-Regular.ttf
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6edc000-7007f6efd000 r--s 00000000 fe:00 147                        /system/fonts/NotoSansBamum-Regular.ttf
+Size:                132 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6efd000-7007f6f17000 r--s 00000000 fe:00 105                        /system/fonts/NotoSansMyanmarUI-Bold.ttf
+Size:                104 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6f17000-7007f6f54000 r--p 00000000 fe:00 1563                       /system/lib64/libjpeg.so
+Size:                244 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  60 kB
+Pss:                  15 kB
+Shared_Clean:         60 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           60 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               15 kB
+VmFlags: rd mr mw me 
+7007f6f54000-7007f6fa6000 r-xp 0003d000 fe:00 1563                       /system/lib64/libjpeg.so
+Size:                328 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f6fa6000-7007f6fa7000 rw-p 0008f000 fe:00 1563                       /system/lib64/libjpeg.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6fa7000-7007f6fa8000 r--p 00090000 fe:00 1563                       /system/lib64/libjpeg.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f6fa8000-7007f6fa9000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6fa9000-7007f6faa000 r-xp 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd ex mr mw me ac 
+7007f6faa000-7007f6ff5000 r--s 00000000 fe:00 121                        /system/fonts/Roboto-Medium.ttf
+Size:                300 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 148 kB
+Pss:                  47 kB
+Shared_Clean:        148 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          148 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               47 kB
+VmFlags: rd mr me ms 
+7007f6ff5000-7007f6ff6000 r--p 00000000 fe:00 1623                       /system/lib64/libvndksupport.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007f6ff6000-7007f6ff7000 r-xp 00001000 fe:00 1623                       /system/lib64/libvndksupport.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f6ff7000-7007f6ff8000 rw-p 00002000 fe:00 1623                       /system/lib64/libvndksupport.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6ff8000-7007f6ff9000 r--p 00003000 fe:00 1623                       /system/lib64/libvndksupport.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f6ff9000-7007f6ffa000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f6ffa000-7007f6ffb000 rw-p 00000000 00:00 0                          [anon:linker_alloc_small_objects]
+Name:           [anon:linker_alloc_small_objects]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f6ffb000-7007f6ffd000 r--s 00000000 fe:00 1014                       /system/usr/hyphen-data/hyph-da.hyb
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f6ffd000-7007f7001000 r--s 00000000 fe:00 199                        /system/fonts/NotoSansPahawhHmong-Regular.otf
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7001000-7007f700e000 r--s 00000000 fe:00 159                        /system/fonts/NotoSansSyriacWestern-Regular.ttf
+Size:                 52 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f700e000-7007f7028000 r--s 00000000 fe:00 80                         /system/fonts/NotoSansMyanmarUI-Regular.ttf
+Size:                104 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7028000-7007f702b000 r--p 00000000 fe:00 1747                       /system/lib64/libnetdbpf.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   2 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me 
+7007f702b000-7007f7031000 r-xp 00003000 fe:00 1747                       /system/lib64/libnetdbpf.so
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f7031000-7007f7032000 rw-p 00009000 fe:00 1747                       /system/lib64/libnetdbpf.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7032000-7007f7033000 r--p 0000a000 fe:00 1747                       /system/lib64/libnetdbpf.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f7033000-7007f7034000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7034000-7007f7037000 r--s 00000000 fe:00 214                        /system/fonts/NotoSansPauCinHau-Regular.otf
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7037000-7007f7044000 r--s 00000000 fe:00 194                        /system/fonts/NotoSansSyriacEastern-Regular.ttf
+Size:                 52 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7044000-7007f704b000 r--p 00000000 fe:00 1620                       /system/lib64/libmediametrics.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                   3 kB
+Shared_Clean:         28 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           28 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me 
+7007f704b000-7007f7051000 r-xp 00007000 fe:00 1620                       /system/lib64/libmediametrics.so
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                   4 kB
+Shared_Clean:         24 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           24 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd ex mr mw me 
+7007f7051000-7007f7052000 rw-p 0000d000 fe:00 1620                       /system/lib64/libmediametrics.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7052000-7007f7054000 r--p 0000e000 fe:00 1620                       /system/lib64/libmediametrics.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f7054000-7007f7055000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f7055000-7007f7058000 r--s 00000000 fe:00 141                        /system/fonts/NotoSansPalmyrene-Regular.otf
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7058000-7007f707a000 r--s 00000000 fe:00 226                        /system/fonts/NotoSerifMyanmar-Bold.otf
+Size:                136 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f707a000-7007f709c000 r--s 00000000 fe:00 153                        /system/fonts/NotoSerifMyanmar-Regular.otf
+Size:                136 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f709c000-7007f70da000 r--p 00000000 fe:00 1512                       /system/lib64/libaudioclient.so
+Size:                248 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 128 kB
+Pss:                  22 kB
+Shared_Clean:        128 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          128 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               22 kB
+VmFlags: rd mr mw me 
+7007f70da000-7007f7110000 r-xp 0003e000 fe:00 1512                       /system/lib64/libaudioclient.so
+Size:                216 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 188 kB
+Pss:                  41 kB
+Shared_Clean:        188 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          188 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               41 kB
+VmFlags: rd ex mr mw me 
+7007f7110000-7007f7111000 rw-p 00074000 fe:00 1512                       /system/lib64/libaudioclient.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7111000-7007f711f000 r--p 00075000 fe:00 1512                       /system/lib64/libaudioclient.so
+Size:                 56 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  56 kB
+Pss:                   3 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         56 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           32 kB
+Anonymous:            56 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me ac 
+7007f711f000-7007f7120000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f7120000-7007f7126000 r--s 00000000 fe:00 108                        /system/fonts/NotoSansMiao-Regular.otf
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7126000-7007f7132000 r--s 00000000 fe:00 206                        /system/fonts/NotoSansSyriacEstrangela-Regular.ttf
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7132000-7007f714d000 r--s 00000000 fe:00 227                        /system/fonts/NotoSansMyanmar-Bold.ttf
+Size:                108 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f714d000-7007f7172000 r--p 00000000 fe:00 1732                       /system/lib64/libmedia_omx.so
+Size:                148 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 120 kB
+Pss:                  24 kB
+Shared_Clean:        120 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          120 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               24 kB
+VmFlags: rd mr mw me 
+7007f7172000-7007f7194000 r-xp 00025000 fe:00 1732                       /system/lib64/libmedia_omx.so
+Size:                136 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 136 kB
+Pss:                  31 kB
+Shared_Clean:        136 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          136 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               31 kB
+VmFlags: rd ex mr mw me 
+7007f7194000-7007f7195000 rw-p 00047000 fe:00 1732                       /system/lib64/libmedia_omx.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7195000-7007f71a0000 r--p 00048000 fe:00 1732                       /system/lib64/libmedia_omx.so
+Size:                 44 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  44 kB
+Pss:                   2 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         44 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           36 kB
+Anonymous:            44 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me ac 
+7007f71a0000-7007f71a1000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f71a1000-7007f71a3000 r--s 00000000 fe:00 997                        /system/usr/hyphen-data/hyph-be.hyb
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f71a3000-7007f71be000 r--s 00000000 fe:00 187                        /system/fonts/NotoSansMyanmar-Regular.ttf
+Size:                108 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f71be000-7007f71e2000 r--s 00000000 fe:00 180                        /system/fonts/NotoSansKhmer-VF.ttf
+Size:                144 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f71e2000-7007f71ea000 r--p 00000000 fe:00 1655                       /system/lib64/libselinux.so
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  32 kB
+Pss:                   0 kB
+Shared_Clean:         32 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           32 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007f71ea000-7007f71fa000 r-xp 00008000 fe:00 1655                       /system/lib64/libselinux.so
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                   2 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd ex mr mw me 
+7007f71fa000-7007f71fb000 rw-p 00018000 fe:00 1655                       /system/lib64/libselinux.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f71fb000-7007f71fc000 r--p 00019000 fe:00 1655                       /system/lib64/libselinux.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f71fc000-7007f71fe000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+7007f71fe000-7007f7203000 r--s 00000000 fe:00 261                        /system/fonts/NotoSansMeroitic-Regular.otf
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7203000-7007f7212000 r--s 00000000 fe:00 190                        /system/fonts/NotoSansLinearB-Regular.ttf
+Size:                 60 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7212000-7007f7219000 r--p 00000000 fe:00 1697                       /system/lib64/libpiex.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                   6 kB
+Shared_Clean:         24 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           24 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd mr mw me 
+7007f7219000-7007f722c000 r-xp 00007000 fe:00 1697                       /system/lib64/libpiex.so
+Size:                 76 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f722c000-7007f722d000 rw-p 0001a000 fe:00 1697                       /system/lib64/libpiex.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f722d000-7007f722e000 r--p 0001b000 fe:00 1697                       /system/lib64/libpiex.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f722e000-7007f722f000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007f722f000-7007f7238000 r--s 00000000 fe:00 259                        /system/fonts/NotoSansSymbols-Regular-Subsetted2.ttf
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7238000-7007f724d000 r--s 00000000 fe:00 72                         /system/fonts/NotoSansSinhalaUI-Bold.otf
+Size:                 84 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f724d000-7007f7262000 r--s 00000000 fe:00 241                        /system/fonts/NotoSansSinhalaUI-Regular.otf
+Size:                 84 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7262000-7007f726a000 r--p 00000000 fe:00 1240                       /system/lib64/libaudioutils.so
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  32 kB
+Pss:                   5 kB
+Shared_Clean:         32 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           32 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                5 kB
+VmFlags: rd mr mw me 
+7007f726a000-7007f7274000 r-xp 00008000 fe:00 1240                       /system/lib64/libaudioutils.so
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f7274000-7007f7275000 rw-p 00012000 fe:00 1240                       /system/lib64/libaudioutils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7275000-7007f7276000 r--p 00013000 fe:00 1240                       /system/lib64/libaudioutils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f7276000-7007f7277000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007f7277000-7007f7286000 r--s 00000000 fe:00 186                        /system/fonts/NotoSansKaithi-Regular.ttf
+Size:                 60 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7286000-7007f7304000 r--p 00000000 fe:00 1125                       /system/lib64/libcrypto.so
+Size:                504 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 296 kB
+Pss:                  18 kB
+Shared_Clean:        296 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          296 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               18 kB
+VmFlags: rd mr mw me 
+7007f7304000-7007f73e2000 r-xp 0007e000 fe:00 1125                       /system/lib64/libcrypto.so
+Size:                888 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 752 kB
+Pss:                 256 kB
+Shared_Clean:        632 kB
+Shared_Dirty:          0 kB
+Private_Clean:       120 kB
+Private_Dirty:         0 kB
+Referenced:          752 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              256 kB
+VmFlags: rd ex mr mw me 
+7007f73e2000-7007f73e3000 rw-p 0015c000 fe:00 1125                       /system/lib64/libcrypto.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f73e3000-7007f73f4000 r--p 0015d000 fe:00 1125                       /system/lib64/libcrypto.so
+Size:                 68 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  68 kB
+Pss:                   3 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         68 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           60 kB
+Anonymous:            68 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me ac 
+7007f73f4000-7007f73f6000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+7007f73f6000-7007f73fd000 r--s 00000000 fe:00 267                        /system/fonts/NotoSansPhagsPa-Regular.ttf
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f73fd000-7007f7423000 r--s 00000000 fe:00 129                        /system/fonts/NotoSansSinhala-Bold.ttf
+Size:                152 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7423000-7007f7426000 r--p 00000000 fe:00 1569                       /system/lib64/libnativewindow.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   1 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+7007f7426000-7007f7428000 r-xp 00003000 fe:00 1569                       /system/lib64/libnativewindow.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f7428000-7007f7429000 rw-p 00005000 fe:00 1569                       /system/lib64/libnativewindow.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7429000-7007f742a000 r--p 00006000 fe:00 1569                       /system/lib64/libnativewindow.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f742a000-7007f7434000 r--s 00000000 fe:00 178                        /system/fonts/NotoSansJavanese-Regular.ttf
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7434000-7007f7449000 r--s 00000000 fe:00 198                        /system/fonts/NotoSerifSinhala-Bold.otf
+Size:                 84 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7449000-7007f7471000 r--s 00000000 fe:00 115                        /system/fonts/NotoSansSinhala-Regular.ttf
+Size:                160 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7471000-7007f7473000 r--p 00000000 fe:00 1740                       /system/lib64/android.hidl.token@1.0-utils.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007f7473000-7007f7474000 r-xp 00002000 fe:00 1740                       /system/lib64/android.hidl.token@1.0-utils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f7474000-7007f7475000 rw-p 00003000 fe:00 1740                       /system/lib64/android.hidl.token@1.0-utils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7475000-7007f7476000 r--p 00004000 fe:00 1740                       /system/lib64/android.hidl.token@1.0-utils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f7476000-7007f7477000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7477000-7007f7486000 r--s 00000000 fe:00 76                         /system/fonts/NotoSansCherokee-Regular.ttf
+Size:                 60 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7486000-7007f7499000 r--s 00000000 fe:00 131                        /system/fonts/NotoSerifSinhala-Regular.otf
+Size:                 76 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7499000-7007f74ad000 r--s 00000000 fe:00 284                        /system/fonts/NotoSansOriyaUI-Bold.ttf
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f74ad000-7007f74c9000 r--p 00000000 fe:00 1562                       /system/lib64/android.hardware.cas@1.0.so
+Size:                112 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  12 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd mr mw me 
+7007f74c9000-7007f74f5000 r-xp 0001c000 fe:00 1562                       /system/lib64/android.hardware.cas@1.0.so
+Size:                176 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f74f5000-7007f74f6000 rw-p 00048000 fe:00 1562                       /system/lib64/android.hardware.cas@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f74f6000-7007f74fb000 r--p 00049000 fe:00 1562                       /system/lib64/android.hardware.cas@1.0.so
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         20 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            20 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me ac 
+7007f74fb000-7007f7500000 r--s 00000000 fe:00 86                         /system/fonts/NotoSansManichaean-Regular.otf
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7500000-7007f7514000 r--s 00000000 fe:00 244                        /system/fonts/NotoSansOriyaUI-Regular.ttf
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7514000-7007f7523000 r--p 00000000 fe:00 1198                       /system/lib64/libhidlbase.so
+Size:                 60 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  60 kB
+Pss:                   2 kB
+Shared_Clean:         60 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           60 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me 
+7007f7523000-7007f7536000 r-xp 0000f000 fe:00 1198                       /system/lib64/libhidlbase.so
+Size:                 76 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  76 kB
+Pss:                   3 kB
+Shared_Clean:         76 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           76 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd ex mr mw me 
+7007f7536000-7007f7537000 rw-p 00022000 fe:00 1198                       /system/lib64/libhidlbase.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7537000-7007f7539000 r--p 00023000 fe:00 1198                       /system/lib64/libhidlbase.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f7539000-7007f753a000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f753a000-7007f753b000 rw-s 00000000 00:05 11403                      /dev/ashmem/GFXStats-1521 (deleted)
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   2 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd wr sh mr mw me ms 
+7007f753b000-7007f7547000 r--s 00000000 fe:00 283                        /system/fonts/NotoSansCanadianAboriginal-Regular.ttf
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7547000-7007f7558000 r--p 00000000 fe:00 1748                       /system/lib64/libpng.so
+Size:                 68 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  10 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               10 kB
+VmFlags: rd mr mw me 
+7007f7558000-7007f757e000 r-xp 00011000 fe:00 1748                       /system/lib64/libpng.so
+Size:                152 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 152 kB
+Pss:                  38 kB
+Shared_Clean:        152 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          152 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               38 kB
+VmFlags: rd ex mr mw me 
+7007f757e000-7007f757f000 rw-p 00037000 fe:00 1748                       /system/lib64/libpng.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f757f000-7007f7580000 r--p 00038000 fe:00 1748                       /system/lib64/libpng.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f7580000-7007f75a0000 r--p 00000000 fe:00 1539                       /system/lib64/libpcre2.so
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 128 kB
+Pss:                  20 kB
+Shared_Clean:        128 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          128 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               20 kB
+VmFlags: rd mr mw me 
+7007f75a0000-7007f75cf000 r-xp 00020000 fe:00 1539                       /system/lib64/libpcre2.so
+Size:                188 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 184 kB
+Pss:                  37 kB
+Shared_Clean:        184 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          184 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               37 kB
+VmFlags: rd ex mr mw me 
+7007f75cf000-7007f75d0000 rw-p 0004f000 fe:00 1539                       /system/lib64/libpcre2.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f75d0000-7007f75d1000 r--p 00050000 fe:00 1539                       /system/lib64/libpcre2.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f75d1000-7007f75d4000 r--s 00000000 fe:00 281                        /system/fonts/NotoSansOldPermic-Regular.otf
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f75d4000-7007f75d8000 r--s 00000000 fe:00 268                        /system/fonts/NotoSansTifinagh-Regular.ttf
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f75d8000-7007f75ed000 r--s 00000000 fe:00 60                         /system/fonts/NotoSansOriya-Bold.ttf
+Size:                 84 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f75ed000-7007f7602000 r--s 00000000 fe:00 120                        /system/fonts/NotoSansOriya-Regular.ttf
+Size:                 84 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7602000-7007f760a000 r--p 00000000 fe:00 1141                       /system/lib64/libnetdutils.so
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  32 kB
+Pss:                   6 kB
+Shared_Clean:         32 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           32 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd mr mw me 
+7007f760a000-7007f7614000 r-xp 00008000 fe:00 1141                       /system/lib64/libnetdutils.so
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f7614000-7007f7615000 rw-p 00012000 fe:00 1141                       /system/lib64/libnetdutils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7615000-7007f7616000 r--p 00013000 fe:00 1141                       /system/lib64/libnetdutils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f7616000-7007f7617000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7617000-7007f7619000 r--s 00000000 fe:00 277                        /system/fonts/NotoSansSoraSompeng-Regular.otf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7619000-7007f761d000 r--s 00000000 fe:00 225                        /system/fonts/NotoSansTaiViet-Regular.ttf
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f761d000-7007f7668000 r--s 00000000 fe:00 83                         /system/fonts/Roboto-Regular.ttf
+Size:                300 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 208 kB
+Pss:                  55 kB
+Shared_Clean:        208 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          208 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               55 kB
+VmFlags: rd mr me ms 
+7007f7668000-7007f7909000 r--p 00000000 fe:00 1705                       /system/lib64/libpdfium.so
+Size:               2692 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 128 kB
+Pss:                  37 kB
+Shared_Clean:        128 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          128 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               37 kB
+VmFlags: rd mr mw me 
+7007f7909000-7007f7b59000 r-xp 002a1000 fe:00 1705                       /system/lib64/libpdfium.so
+Size:               2368 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f7b59000-7007f7b5d000 rw-p 004f1000 fe:00 1705                       /system/lib64/libpdfium.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         16 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7b5d000-7007f7b72000 r--p 004f5000 fe:00 1705                       /system/lib64/libpdfium.so
+Size:                 84 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  84 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         84 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            84 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007f7b72000-7007f7b7a000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7b7a000-7007f7b7c000 r--s 00000000 fe:00 230                        /system/fonts/NotoSansOldNorthArabian-Regular.otf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7b7c000-7007f7b85000 r--s 00000000 fe:00 137                        /system/fonts/NotoSansChakma-Regular.ttf
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7b85000-7007f7b9c000 r--s 00000000 fe:00 210                        /system/fonts/NotoSerifKannada-Bold.ttf
+Size:                 92 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7b9c000-7007f7bb3000 r--s 00000000 fe:00 160                        /system/fonts/NotoSerifKannada-Regular.ttf
+Size:                 92 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7bb3000-7007f7bcd000 r--p 00000000 fe:00 1595                       /system/lib64/libwilhelm.so
+Size:                104 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  10 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               10 kB
+VmFlags: rd mr mw me 
+7007f7bcd000-7007f7bed000 r-xp 0001a000 fe:00 1595                       /system/lib64/libwilhelm.so
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f7bed000-7007f7bee000 rw-p 0003a000 fe:00 1595                       /system/lib64/libwilhelm.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7bee000-7007f7bf3000 r--p 0003b000 fe:00 1595                       /system/lib64/libwilhelm.so
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         20 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            20 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me ac 
+7007f7bf3000-7007f7bf4000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7bf4000-7007f7bf6000 r--s 00000000 fe:00 213                        /system/fonts/NotoSansNabataean-Regular.otf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7bf6000-7007f7c0a000 r--s 00000000 fe:00 169                        /system/fonts/NotoSansKannadaUI-Bold.ttf
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7c0a000-7007f7c96000 r--p 00000000 fe:00 1659                       /system/lib64/libstagefright.so
+Size:                560 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 256 kB
+Pss:                  66 kB
+Shared_Clean:        256 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          256 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               66 kB
+VmFlags: rd mr mw me 
+7007f7c96000-7007f7dad000 r-xp 0008c000 fe:00 1659                       /system/lib64/libstagefright.so
+Size:               1116 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 788 kB
+Pss:                 289 kB
+Shared_Clean:        788 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          788 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              289 kB
+VmFlags: rd ex mr mw me 
+7007f7dad000-7007f7dae000 rw-p 001a3000 fe:00 1659                       /system/lib64/libstagefright.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7dae000-7007f7dbe000 r--p 001a4000 fe:00 1659                       /system/lib64/libstagefright.so
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                   3 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         64 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           52 kB
+Anonymous:            64 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me ac 
+7007f7dbe000-7007f7dbf000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f7dbf000-7007f7ddb000 r--s 00000000 fe:00 291                        /system/fonts/NotoSansTeluguUI-Bold.ttf
+Size:                112 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7ddb000-7007f7de5000 r--p 00000000 fe:00 1770                       /system/lib64/android.hidl.allocator@1.0.so
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  40 kB
+Pss:                   6 kB
+Shared_Clean:         40 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           40 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd mr mw me 
+7007f7de5000-7007f7def000 r-xp 0000a000 fe:00 1770                       /system/lib64/android.hidl.allocator@1.0.so
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  40 kB
+Pss:                   7 kB
+Shared_Clean:         40 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           40 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                7 kB
+VmFlags: rd ex mr mw me 
+7007f7def000-7007f7df0000 rw-p 00014000 fe:00 1770                       /system/lib64/android.hidl.allocator@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7df0000-7007f7df2000 r--p 00015000 fe:00 1770                       /system/lib64/android.hidl.allocator@1.0.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f7df2000-7007f7df3000 rw-s 00000000 00:05 11403                      /dev/ashmem/GFXStats-1521 (deleted)
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   2 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd wr sh mr mw me ms 
+7007f7df3000-7007f7e07000 r--s 00000000 fe:00 236                        /system/fonts/NotoSansKannadaUI-Regular.ttf
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7e07000-7007f7e10000 r--p 00000000 fe:00 1203                       /system/lib64/liblzma.so
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  36 kB
+Pss:                   3 kB
+Shared_Clean:         36 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           36 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me 
+7007f7e10000-7007f7e2f000 r-xp 00009000 fe:00 1203                       /system/lib64/liblzma.so
+Size:                124 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f7e2f000-7007f7e30000 rw-p 00028000 fe:00 1203                       /system/lib64/liblzma.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7e30000-7007f7e31000 r--p 00029000 fe:00 1203                       /system/lib64/liblzma.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f7e31000-7007f7e38000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7e38000-7007f7e3d000 r--s 00000000 fe:00 269                        /system/fonts/NotoSansSaurashtra-Regular.ttf
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7e3d000-7007f7e45000 r--s 00000000 fe:00 204                        /system/fonts/NotoSansBalinese-Regular.ttf
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7e45000-7007f7e50000 r--p 00000000 fe:00 1544                       /system/lib64/android.hidl.token@1.0.so
+Size:                 44 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  44 kB
+Pss:                   5 kB
+Shared_Clean:         44 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           44 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                5 kB
+VmFlags: rd mr mw me 
+7007f7e50000-7007f7e5b000 r-xp 0000b000 fe:00 1544                       /system/lib64/android.hidl.token@1.0.so
+Size:                 44 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f7e5b000-7007f7e5c000 rw-p 00016000 fe:00 1544                       /system/lib64/android.hidl.token@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7e5c000-7007f7e5e000 r--p 00017000 fe:00 1544                       /system/lib64/android.hidl.token@1.0.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f7e5e000-7007f7e60000 r--s 00000000 fe:00 85                         /system/fonts/NotoSansMultani-Regular.otf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7e60000-7007f7e65000 r--s 00000000 fe:00 168                        /system/fonts/NotoSansNKo-Regular.ttf
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7e65000-7007f7eb7000 r--s 00000000 fe:00 165                        /system/fonts/Roboto-LightItalic.ttf
+Size:                328 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7eb7000-7007f7ed5000 r--p 00000000 fe:00 1126                       /system/lib64/libprotobuf-cpp-lite.so
+Size:                120 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  96 kB
+Pss:                   8 kB
+Shared_Clean:         96 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           96 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me 
+7007f7ed5000-7007f7ef4000 r-xp 0001e000 fe:00 1126                       /system/lib64/libprotobuf-cpp-lite.so
+Size:                124 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 124 kB
+Pss:                  11 kB
+Shared_Clean:        124 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          124 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               11 kB
+VmFlags: rd ex mr mw me 
+7007f7ef4000-7007f7ef5000 rw-p 0003d000 fe:00 1126                       /system/lib64/libprotobuf-cpp-lite.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7ef5000-7007f7ef7000 r--p 0003e000 fe:00 1126                       /system/lib64/libprotobuf-cpp-lite.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f7ef7000-7007f7ef8000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7ef8000-7007f7ef9000 r--p 00000000 00:00 0                          [anon:atexit handlers]
+Name:           [anon:atexit handlers]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007f7ef9000-7007f7f0d000 r--s 00000000 fe:00 111                        /system/fonts/NotoSansKannada-Bold.ttf
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7f0d000-7007f7f19000 r--p 00000000 fe:00 1208                       /system/lib64/libbufferhubqueue.so
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  48 kB
+Pss:                   6 kB
+Shared_Clean:         48 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           48 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd mr mw me 
+7007f7f19000-7007f7f2a000 r-xp 0000c000 fe:00 1208                       /system/lib64/libbufferhubqueue.so
+Size:                 68 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f7f2a000-7007f7f2b000 rw-p 0001d000 fe:00 1208                       /system/lib64/libbufferhubqueue.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7f2b000-7007f7f2c000 r--p 0001e000 fe:00 1208                       /system/lib64/libbufferhubqueue.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f7f2c000-7007f7f2d000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7f2d000-7007f7f2e000 rw-s 00000000 00:05 9940                       /dev/ashmem/0f02fd80-4c97-473b-981d-e08369625636 (deleted)
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr sh mr mw ms 
+7007f7f2e000-7007f7f30000 r--s 00000000 fe:00 112                        /system/fonts/NotoSansMro-Regular.otf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7f30000-7007f7f71000 r--s 00000000 fe:00 62                         /system/fonts/NotoSerif-BoldItalic.ttf
+Size:                260 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7f71000-7007f7f72000 r--p 00000000 fe:00 1177                       /system/lib64/libsync.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007f7f72000-7007f7f73000 r-xp 00001000 fe:00 1177                       /system/lib64/libsync.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f7f73000-7007f7f74000 rw-p 00002000 fe:00 1177                       /system/lib64/libsync.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7f74000-7007f7f75000 r--p 00003000 fe:00 1177                       /system/lib64/libsync.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f7f75000-7007f7f76000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f7f76000-7007f7f7a000 r--s 00000000 fe:00 99                         /system/fonts/NotoSansSylotiNagri-Regular.ttf
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7f7a000-7007f7f87000 r--s 00000000 fe:00 125                        /system/fonts/NotoSansAdlam-Regular.ttf
+Size:                 52 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f7f87000-7007f7fd1000 r--p 00000000 fe:00 1597                       /system/lib64/libc.so
+Size:                296 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 228 kB
+Pss:                   3 kB
+Shared_Clean:        228 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          228 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me 
+7007f7fd1000-7007f8089000 r-xp 0004a000 fe:00 1597                       /system/lib64/libc.so
+Size:                736 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 728 kB
+Pss:                  12 kB
+Shared_Clean:        728 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          728 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd ex mr mw me 
+7007f8089000-7007f808c000 rw-p 00102000 fe:00 1597                       /system/lib64/libc.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+7007f808c000-7007f8093000 r--p 00105000 fe:00 1597                       /system/lib64/libc.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         28 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           24 kB
+Anonymous:            28 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me ac 
+7007f8093000-7007f80e5000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                328 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  48 kB
+Pss:                  40 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:        40 kB
+Referenced:           48 kB
+Anonymous:            48 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               40 kB
+VmFlags: rd wr mr mw me ac 
+7007f80e5000-7007f80e6000 r--p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f80e6000-7007f80eb000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                  20 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        20 kB
+Referenced:           20 kB
+Anonymous:            20 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               20 kB
+VmFlags: rd wr mr mw me ac 
+7007f80eb000-7007f80ec000 rw-p 00000000 00:00 0                          [anon:linker_alloc_small_objects]
+Name:           [anon:linker_alloc_small_objects]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f80ec000-7007f80ee000 r--s 00000000 fe:00 209                        /system/fonts/NotoSansHatran-Regular.otf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f80ee000-7007f810a000 r--s 00000000 fe:00 181                        /system/fonts/NotoSansTeluguUI-Regular.ttf
+Size:                112 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f810a000-7007f8113000 r--p 00000000 fe:00 1515                       /system/lib64/libsoundtrigger.so
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  36 kB
+Pss:                   9 kB
+Shared_Clean:         36 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           36 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                9 kB
+VmFlags: rd mr mw me 
+7007f8113000-7007f8118000 r-xp 00009000 fe:00 1515                       /system/lib64/libsoundtrigger.so
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                  10 kB
+Shared_Clean:         20 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           20 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               10 kB
+VmFlags: rd ex mr mw me 
+7007f8118000-7007f8119000 rw-p 0000e000 fe:00 1515                       /system/lib64/libsoundtrigger.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f8119000-7007f811d000 r--p 0000f000 fe:00 1515                       /system/lib64/libsoundtrigger.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         16 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f811d000-7007f811e000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f811e000-7007f8122000 r--s 00000000 fe:00 107                        /system/fonts/NotoSansOldPersian-Regular.ttf
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f8122000-7007f812a000 r--s 00000000 fe:00 116                        /system/fonts/NotoSansLaoUI-Bold.ttf
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f812a000-7007f814f000 r--s 00000000 fe:00 252                        /system/fonts/NotoSerifTelugu-Bold.ttf
+Size:                148 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f814f000-7007f815d000 r--p 00000000 fe:00 1190                       /system/lib64/libexpat.so
+Size:                 56 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  56 kB
+Pss:                   9 kB
+Shared_Clean:         56 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           56 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                9 kB
+VmFlags: rd mr mw me 
+7007f815d000-7007f8178000 r-xp 0000e000 fe:00 1190                       /system/lib64/libexpat.so
+Size:                108 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f8178000-7007f8179000 rw-p 00029000 fe:00 1190                       /system/lib64/libexpat.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f8179000-7007f817b000 r--p 0002a000 fe:00 1190                       /system/lib64/libexpat.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f817b000-7007f817c000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007f817c000-7007f8182000 r--s 00000000 fe:00 188                        /system/fonts/NotoSansKharoshthi-Regular.ttf
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f8182000-7007f81c5000 r--p 00000000 fe:00 1204                       /system/lib64/libmedia.so
+Size:                268 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 128 kB
+Pss:                  21 kB
+Shared_Clean:        128 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          128 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               21 kB
+VmFlags: rd mr mw me 
+7007f81c5000-7007f821a000 r-xp 00043000 fe:00 1204                       /system/lib64/libmedia.so
+Size:                340 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 272 kB
+Pss:                  65 kB
+Shared_Clean:        272 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          272 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               65 kB
+VmFlags: rd ex mr mw me 
+7007f821a000-7007f821b000 rw-p 00098000 fe:00 1204                       /system/lib64/libmedia.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f821b000-7007f8230000 r--p 00099000 fe:00 1204                       /system/lib64/libmedia.so
+Size:                 84 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  84 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         84 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           48 kB
+Anonymous:            84 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007f8230000-7007f8231000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f8231000-7007f8232000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007f8232000-7007f8257000 r--s 00000000 fe:00 203                        /system/fonts/NotoSerifTelugu-Regular.ttf
+Size:                148 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f8257000-7007f8264000 r--p 00000000 fe:00 1700                       /system/lib64/android.hardware.graphics.mapper@2.1.so
+Size:                 52 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  52 kB
+Pss:                   5 kB
+Shared_Clean:         52 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           52 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                5 kB
+VmFlags: rd mr mw me 
+7007f8264000-7007f8271000 r-xp 0000d000 fe:00 1700                       /system/lib64/android.hardware.graphics.mapper@2.1.so
+Size:                 52 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  52 kB
+Pss:                   6 kB
+Shared_Clean:         52 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           52 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd ex mr mw me 
+7007f8271000-7007f8272000 rw-p 0001a000 fe:00 1700                       /system/lib64/android.hardware.graphics.mapper@2.1.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f8272000-7007f8274000 r--p 0001b000 fe:00 1700                       /system/lib64/android.hardware.graphics.mapper@2.1.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f8274000-7007f827a000 r--s 00000000 fe:00 106                        /system/fonts/NotoSansCoptic-Regular.ttf
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f827a000-7007f8282000 r--s 00000000 fe:00 59                         /system/fonts/NotoSansLaoUI-Regular.ttf
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f8282000-7007f8296000 r--p 00000000 fe:00 1216                       /system/lib64/android.system.suspend@1.0.so
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                   9 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                9 kB
+VmFlags: rd mr mw me 
+7007f8296000-7007f82ae000 r-xp 00014000 fe:00 1216                       /system/lib64/android.system.suspend@1.0.so
+Size:                 96 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  96 kB
+Pss:                  19 kB
+Shared_Clean:         96 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           96 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               19 kB
+VmFlags: rd ex mr mw me 
+7007f82ae000-7007f82af000 rw-p 0002c000 fe:00 1216                       /system/lib64/android.system.suspend@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f82af000-7007f82b3000 r--p 0002d000 fe:00 1216                       /system/lib64/android.system.suspend@1.0.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         16 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f82b3000-7007f82b4000 r--s 00000000 00:13 6990                       /dev/event-log-tags
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f82b4000-7007f82ba000 r--s 00000000 fe:00 242                        /system/fonts/NotoSansBrahmi-Regular.ttf
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f82ba000-7007f82c4000 r--s 00000000 fe:00 92                         /system/fonts/NotoSansKhmerUI-Regular.ttf
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f82c4000-7007f82c5000 r--p 00000000 fe:00 1217                       /system/lib64/libhidlallocatorutils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007f82c5000-7007f82c6000 r-xp 00001000 fe:00 1217                       /system/lib64/libhidlallocatorutils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f82c6000-7007f82c7000 rw-p 00002000 fe:00 1217                       /system/lib64/libhidlallocatorutils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f82c7000-7007f82c8000 r--p 00003000 fe:00 1217                       /system/lib64/libhidlallocatorutils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f82c8000-7007f82ce000 r--s 00000000 fe:00 101                        /system/fonts/NotoSansCham-Bold.ttf
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f82ce000-7007f82da000 r--s 00000000 fe:00 228                        /system/fonts/NotoSerifKhmer-Bold.otf
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f82da000-7007f832b000 r--s 00000000 fe:00 89                         /system/fonts/Roboto-ThinItalic.ttf
+Size:                324 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f832b000-7007f835c000 r--p 00000000 fe:00 1152                       /system/lib64/libvintf.so
+Size:                196 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 124 kB
+Pss:                  32 kB
+Shared_Clean:        124 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          124 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               32 kB
+VmFlags: rd mr mw me 
+7007f835c000-7007f83b7000 r-xp 00031000 fe:00 1152                       /system/lib64/libvintf.so
+Size:                364 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 360 kB
+Pss:                 138 kB
+Shared_Clean:        352 kB
+Shared_Dirty:          0 kB
+Private_Clean:         8 kB
+Private_Dirty:         0 kB
+Referenced:          360 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              138 kB
+VmFlags: rd ex mr mw me 
+7007f83b7000-7007f83b8000 rw-p 0008c000 fe:00 1152                       /system/lib64/libvintf.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f83b8000-7007f83bd000 r--p 0008d000 fe:00 1152                       /system/lib64/libvintf.so
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         20 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           20 kB
+Anonymous:            20 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me ac 
+7007f83bd000-7007f83bf000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+7007f83bf000-7007f83c2000 r--s 00000000 fe:00 287                        /system/fonts/NotoSansTaiLe-Regular.ttf
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f83c2000-7007f85b0000 r--p 00000000 fe:00 1537                       /system/lib64/libhwui.so
+Size:               1976 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 632 kB
+Pss:                 196 kB
+Shared_Clean:        632 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          632 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              196 kB
+VmFlags: rd mr mw me 
+7007f85b0000-7007f8b9b000 r-xp 001ee000 fe:00 1537                       /system/lib64/libhwui.so
+Size:               6060 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                4132 kB
+Pss:                1274 kB
+Shared_Clean:       4132 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:         4132 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             1274 kB
+VmFlags: rd ex mr mw me 
+7007f8b9b000-7007f8b9c000 rw-p 007d9000 fe:00 1537                       /system/lib64/libhwui.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f8b9c000-7007f8bd5000 r--p 007da000 fe:00 1537                       /system/lib64/libhwui.so
+Size:                228 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 228 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:        228 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          160 kB
+Anonymous:           228 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd mr mw me ac 
+7007f8bd5000-7007f8bda000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        16 kB
+Referenced:           16 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd wr mr mw me ac 
+7007f8bda000-7007f8bde000 r--s 00000000 fe:00 126                        /system/fonts/NotoSansNewTaiLue-Regular.ttf
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f8bde000-7007f8bf3000 r--s 00000000 fe:00 90                         /system/fonts/NotoSansKannada-Regular.ttf
+Size:                 84 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f8bf3000-7007f8c0e000 r--s 00000000 fe:00 118                        /system/fonts/NotoSansTelugu-Bold.ttf
+Size:                108 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f8c0e000-7007f8cfe000 r--p 00000000 fe:00 1538                       /system/lib64/libicui18n.so
+Size:                960 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 192 kB
+Pss:                  21 kB
+Shared_Clean:        192 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          192 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               21 kB
+VmFlags: rd mr mw me 
+7007f8cfe000-7007f8e8a000 r-xp 000f0000 fe:00 1538                       /system/lib64/libicui18n.so
+Size:               1584 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                1276 kB
+Pss:                 318 kB
+Shared_Clean:       1276 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:         1276 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              318 kB
+VmFlags: rd ex mr mw me 
+7007f8e8a000-7007f8e8b000 rw-p 0027c000 fe:00 1538                       /system/lib64/libicui18n.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f8e8b000-7007f8e9e000 r--p 0027d000 fe:00 1538                       /system/lib64/libicui18n.so
+Size:                 76 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  76 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         76 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:            76 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007f8e9e000-7007f8e9f000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f8e9f000-7007f8ea7000 r--s 00000000 fe:00 95                         /system/fonts/NotoSerifLao-Bold.ttf
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f8ea7000-7007f8ec3000 r--s 00000000 fe:00 221                        /system/fonts/NotoSansTelugu-Regular.ttf
+Size:                112 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f8ec3000-7007f8edd000 r--s 00000000 fe:00 64                         /system/fonts/NotoSansBengaliUI-Bold.ttf
+Size:                104 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f8edd000-7007f8edf000 r--p 00000000 fe:00 1743                       /system/lib64/libstagefright_codecbase.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   1 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+7007f8edf000-7007f8ee2000 r-xp 00002000 fe:00 1743                       /system/lib64/libstagefright_codecbase.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   6 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd ex mr mw me 
+7007f8ee2000-7007f8ee3000 rw-p 00005000 fe:00 1743                       /system/lib64/libstagefright_codecbase.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f8ee3000-7007f8ee4000 r--p 00006000 fe:00 1743                       /system/lib64/libstagefright_codecbase.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f8ee4000-7007f8ee7000 r--s 00000000 fe:00 176                        /system/fonts/NotoSansSundanese-Regular.ttf
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f8ee7000-7007f8f04000 r--s 00000000 fe:00 197                        /system/fonts/NotoSerifBengali-Bold.ttf
+Size:                116 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f8f04000-7007f8f05000 r--p 00000000 fe:00 1151                       /system/lib64/libion.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007f8f05000-7007f8f06000 r-xp 00001000 fe:00 1151                       /system/lib64/libion.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f8f06000-7007f8f07000 rw-p 00002000 fe:00 1151                       /system/lib64/libion.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f8f07000-7007f8f08000 r--p 00003000 fe:00 1151                       /system/lib64/libion.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f8f08000-7007f8f09000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f8f09000-7007f8f0a000 r--s 00000000 fe:00 996                        /system/usr/hyphen-data/hyph-und-ethi.hyb
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f8f0a000-7007f8f55000 r--s 00000000 fe:00 237                        /system/fonts/Roboto-Light.ttf
+Size:                300 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f8f55000-7007f9019000 r--p 00000000 fe:00 1195                       /system/lib64/libandroid_runtime.so
+Size:                784 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 448 kB
+Pss:                  78 kB
+Shared_Clean:        448 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          448 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               78 kB
+VmFlags: rd mr mw me 
+7007f9019000-7007f910c000 r-xp 000c4000 fe:00 1195                       /system/lib64/libandroid_runtime.so
+Size:                972 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 772 kB
+Pss:                  68 kB
+Shared_Clean:        772 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          772 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               68 kB
+VmFlags: rd ex mr mw me 
+7007f910c000-7007f910d000 rw-p 001b7000 fe:00 1195                       /system/lib64/libandroid_runtime.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f910d000-7007f912a000 r--p 001b8000 fe:00 1195                       /system/lib64/libandroid_runtime.so
+Size:                116 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 116 kB
+Pss:                   6 kB
+Shared_Clean:          0 kB
+Shared_Dirty:        116 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           72 kB
+Anonymous:           116 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd mr mw me ac 
+7007f912a000-7007f912d000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me ac 
+7007f912d000-7007f9148000 r--s 00000000 fe:00 67                         /system/fonts/NotoSansBengaliUI-Regular.ttf
+Size:                108 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9148000-7007f9158000 r--p 00000000 fe:00 1761                       /system/lib64/android.hardware.configstore@1.0.so
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                   6 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd mr mw me 
+7007f9158000-7007f916c000 r-xp 00010000 fe:00 1761                       /system/lib64/android.hardware.configstore@1.0.so
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  80 kB
+Pss:                  10 kB
+Shared_Clean:         80 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           80 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               10 kB
+VmFlags: rd ex mr mw me 
+7007f916c000-7007f916d000 rw-p 00024000 fe:00 1761                       /system/lib64/android.hardware.configstore@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f916d000-7007f9170000 r--p 00025000 fe:00 1761                       /system/lib64/android.hardware.configstore@1.0.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         12 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f9170000-7007f9172000 r--s 00000000 fe:00 79                         /system/fonts/NotoSansBassaVah-Regular.otf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9172000-7007f918f000 r--s 00000000 fe:00 246                        /system/fonts/NotoSerifBengali-Regular.ttf
+Size:                116 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f918f000-7007f91b2000 r--p 00000000 fe:00 1581                       /system/lib64/libsqlite.so
+Size:                140 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                   7 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                7 kB
+VmFlags: rd mr mw me 
+7007f91b2000-7007f92b8000 r-xp 00023000 fe:00 1581                       /system/lib64/libsqlite.so
+Size:               1048 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 916 kB
+Pss:                 111 kB
+Shared_Clean:        916 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          916 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              111 kB
+VmFlags: rd ex mr mw me 
+7007f92b8000-7007f92ba000 rw-p 00129000 fe:00 1581                       /system/lib64/libsqlite.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f92ba000-7007f92bd000 r--p 0012b000 fe:00 1581                       /system/lib64/libsqlite.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         12 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f92bd000-7007f92be000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f92be000-7007f92c1000 r--s 00000000 fe:00 65                         /system/fonts/NotoSansSamaritan-Regular.ttf
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f92c1000-7007f92cb000 r--s 00000000 fe:00 174                        /system/fonts/NotoSerifKhmer-Regular.otf
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f92cb000-7007f92ed000 r--p 00000000 fe:00 1127                       /system/lib64/libm.so
+Size:                136 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 136 kB
+Pss:                   7 kB
+Shared_Clean:        136 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          136 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                7 kB
+VmFlags: rd mr mw me 
+7007f92ed000-7007f9319000 r-xp 00022000 fe:00 1127                       /system/lib64/libm.so
+Size:                176 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 100 kB
+Pss:                   4 kB
+Shared_Clean:        100 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          100 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd ex mr mw me 
+7007f9319000-7007f931a000 rw-p 0004e000 fe:00 1127                       /system/lib64/libm.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f931a000-7007f931b000 r--p 0004f000 fe:00 1127                       /system/lib64/libm.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f931b000-7007f931c000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f931c000-7007f9366000 r--s 00000000 fe:00 175                        /system/fonts/RobotoCondensed-Light.ttf
+Size:                296 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9366000-7007f936e000 r--p 00000000 fe:00 1260                       /system/lib64/libcutils.so
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  32 kB
+Pss:                   0 kB
+Shared_Clean:         32 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           32 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007f936e000-7007f9377000 r-xp 00008000 fe:00 1260                       /system/lib64/libcutils.so
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  36 kB
+Pss:                   0 kB
+Shared_Clean:         36 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           36 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f9377000-7007f9378000 rw-p 00011000 fe:00 1260                       /system/lib64/libcutils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f9378000-7007f937a000 r--p 00012000 fe:00 1260                       /system/lib64/libcutils.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f937a000-7007f937b000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f937b000-7007f937c000 r--s 00000000 fe:00 1018                       /system/usr/hyphen-data/hyph-tk.hyb
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f937c000-7007f9384000 r--s 00000000 fe:00 117                        /system/fonts/NotoSerifLao-Regular.ttf
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9384000-7007f93b3000 r--p 00000000 fe:00 1598                       /system/lib64/libft2.so
+Size:                188 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 128 kB
+Pss:                  26 kB
+Shared_Clean:        128 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          128 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               26 kB
+VmFlags: rd mr mw me 
+7007f93b3000-7007f9422000 r-xp 0002f000 fe:00 1598                       /system/lib64/libft2.so
+Size:                444 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 376 kB
+Pss:                  94 kB
+Shared_Clean:        376 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          376 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               94 kB
+VmFlags: rd ex mr mw me 
+7007f9422000-7007f9423000 rw-p 0009e000 fe:00 1598                       /system/lib64/libft2.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9423000-7007f9428000 r--p 0009f000 fe:00 1598                       /system/lib64/libft2.so
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         20 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           20 kB
+Anonymous:            20 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me ac 
+7007f9428000-7007f942c000 r--s 00000000 fe:00 217                        /system/fonts/NotoSansMeeteiMayek-Regular.ttf
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f942c000-7007f9445000 r--s 00000000 fe:00 127                        /system/fonts/NotoSansBengali-Bold.ttf
+Size:                100 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9445000-7007f9452000 r--p 00000000 fe:00 1520                       /system/lib64/libhidl-gen-utils.so
+Size:                 52 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  52 kB
+Pss:                   5 kB
+Shared_Clean:         52 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           52 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                5 kB
+VmFlags: rd mr mw me 
+7007f9452000-7007f9467000 r-xp 0000d000 fe:00 1520                       /system/lib64/libhidl-gen-utils.so
+Size:                 84 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  84 kB
+Pss:                  11 kB
+Shared_Clean:         84 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           84 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               11 kB
+VmFlags: rd ex mr mw me 
+7007f9467000-7007f9468000 rw-p 00022000 fe:00 1520                       /system/lib64/libhidl-gen-utils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9468000-7007f9469000 r--p 00023000 fe:00 1520                       /system/lib64/libhidl-gen-utils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f9469000-7007f946a000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f946a000-7007f946c000 r--s 00000000 fe:00 272                        /system/fonts/NotoSansUgaritic-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f946c000-7007f9485000 r--s 00000000 fe:00 183                        /system/fonts/NotoSansBengali-Regular.ttf
+Size:                100 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9485000-7007f9493000 r--s 00000000 fe:00 94                         /system/fonts/NotoSansMalayalamUI-Bold.ttf
+Size:                 56 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9493000-7007f9495000 r--p 00000000 fe:00 1189                       /system/lib64/libnativebridge.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007f9495000-7007f9497000 r-xp 00002000 fe:00 1189                       /system/lib64/libnativebridge.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f9497000-7007f9498000 rw-p 00004000 fe:00 1189                       /system/lib64/libnativebridge.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9498000-7007f9499000 r--p 00005000 fe:00 1189                       /system/lib64/libnativebridge.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f9499000-7007f949a000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f949a000-7007f949e000 r--s 00000000 fe:00 145                        /system/fonts/NotoSansMandaic-Regular.ttf
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f949e000-7007f94ac000 r--s 00000000 fe:00 133                        /system/fonts/NotoSansMalayalamUI-Regular.ttf
+Size:                 56 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f94ac000-7007f94b9000 r--s 00000000 fe:00 232                        /system/fonts/NotoSerifMalayalam-Bold.ttf
+Size:                 52 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f94b9000-7007f94c7000 r--s 00000000 fe:00 273                        /system/fonts/NotoSansMalayalam-Bold.ttf
+Size:                 56 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f94c7000-7007f94f3000 r--p 00000000 fe:00 1702                       /system/lib64/libharfbuzz_ng.so
+Size:                176 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 156 kB
+Pss:                  33 kB
+Shared_Clean:        156 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          156 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               33 kB
+VmFlags: rd mr mw me 
+7007f94f3000-7007f9570000 r-xp 0002c000 fe:00 1702                       /system/lib64/libharfbuzz_ng.so
+Size:                500 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 488 kB
+Pss:                 122 kB
+Shared_Clean:        488 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          488 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              122 kB
+VmFlags: rd ex mr mw me 
+7007f9570000-7007f9571000 rw-p 000a9000 fe:00 1702                       /system/lib64/libharfbuzz_ng.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9571000-7007f9572000 r--p 000aa000 fe:00 1702                       /system/lib64/libharfbuzz_ng.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f9572000-7007f9575000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9575000-7007f957e000 r--s 00000000 fe:00 212                        /system/fonts/NotoSansKhmerUI-Bold.ttf
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f957e000-7007f958c000 r--s 00000000 fe:00 150                        /system/fonts/NotoSansMalayalam-Regular.ttf
+Size:                 56 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f958c000-7007f958e000 r--p 00000000 fe:00 1640                       /system/lib64/libmemtrack.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+7007f958e000-7007f958f000 r-xp 00002000 fe:00 1640                       /system/lib64/libmemtrack.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         4 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd ex mr mw me 
+7007f958f000-7007f9590000 rw-p 00003000 fe:00 1640                       /system/lib64/libmemtrack.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9590000-7007f9591000 r--p 00004000 fe:00 1640                       /system/lib64/libmemtrack.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f9591000-7007f9592000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f9592000-7007f959f000 r--s 00000000 fe:00 109                        /system/fonts/NotoSerifMalayalam-Regular.ttf
+Size:                 52 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f959f000-7007f95a8000 r--s 00000000 fe:00 163                        /system/fonts/NotoSansTamilUI-Bold.ttf
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f95a8000-7007f95f4000 r--s 00000000 fe:00 251                        /system/fonts/Roboto-Thin.ttf
+Size:                304 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f95f4000-7007f95f6000 r--p 00000000 fe:00 1657                       /system/lib64/libdebuggerd_client.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   1 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+7007f95f6000-7007f95f8000 r-xp 00002000 fe:00 1657                       /system/lib64/libdebuggerd_client.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f95f8000-7007f95f9000 rw-p 00004000 fe:00 1657                       /system/lib64/libdebuggerd_client.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f95f9000-7007f95fa000 r--p 00005000 fe:00 1657                       /system/lib64/libdebuggerd_client.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f95fa000-7007f95fe000 r--s 00000000 fe:00 201                        /system/fonts/NotoSansGlagolitic-Regular.ttf
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f95fe000-7007f961a000 r--s 00000000 fe:00 69                         /system/fonts/NotoSansGujaratiUI-Bold.ttf
+Size:                112 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f961a000-7007f961b000 r--p 00000000 fe:00 1652                       /system/lib64/libdl.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007f961b000-7007f961c000 r-xp 00001000 fe:00 1652                       /system/lib64/libdl.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f961c000-7007f961d000 r--p 00002000 fe:00 1652                       /system/lib64/libdl.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f961d000-7007f961e000 r--p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f961e000-7007f9624000 r--s 00000000 fe:00 173                        /system/fonts/NotoSansCham-Regular.ttf
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9624000-7007f9642000 r--s 00000000 fe:00 271                        /system/fonts/NotoSansGujarati-Regular.ttf
+Size:                120 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9642000-7007f9648000 r--p 00000000 fe:00 1769                       /system/lib64/libbpf_android.so
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                   4 kB
+Shared_Clean:         24 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           24 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me 
+7007f9648000-7007f9652000 r-xp 00006000 fe:00 1769                       /system/lib64/libbpf_android.so
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   2 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd ex mr mw me 
+7007f9652000-7007f9653000 rw-p 00010000 fe:00 1769                       /system/lib64/libbpf_android.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9653000-7007f9654000 r--p 00011000 fe:00 1769                       /system/lib64/libbpf_android.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f9654000-7007f9655000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9655000-7007f9657000 r--s 00000000 fe:00 177                        /system/fonts/NotoSansTagbanwa-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9657000-7007f9660000 r--s 00000000 fe:00 75                         /system/fonts/NotoSansTamilUI-Regular.ttf
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9660000-7007f969d000 r--s 00000000 fe:00 140                        /system/fonts/NotoSerif-Italic.ttf
+Size:                244 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f969d000-7007f969f000 r--p 00000000 fe:00 1718                       /system/lib64/libstdc++.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007f969f000-7007f96a1000 r-xp 00002000 fe:00 1718                       /system/lib64/libstdc++.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f96a1000-7007f96a2000 rw-p 00004000 fe:00 1718                       /system/lib64/libstdc++.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f96a2000-7007f96a3000 r--p 00005000 fe:00 1718                       /system/lib64/libstdc++.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f96a3000-7007f96c0000 r--s 00000000 fe:00 256                        /system/fonts/NotoSansGujaratiUI-Regular.ttf
+Size:                116 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f96c0000-7007f96de000 r--s 00000000 fe:00 93                         /system/fonts/NotoSansDevanagariUI-Bold.ttf
+Size:                120 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f96de000-7007f9717000 r--p 00000000 fe:00 1547                       /system/lib64/libhidltransport.so
+Size:                228 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 200 kB
+Pss:                   9 kB
+Shared_Clean:        200 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          200 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                9 kB
+VmFlags: rd mr mw me 
+7007f9717000-7007f9772000 r-xp 00039000 fe:00 1547                       /system/lib64/libhidltransport.so
+Size:                364 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 364 kB
+Pss:                  16 kB
+Shared_Clean:        364 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          364 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               16 kB
+VmFlags: rd ex mr mw me 
+7007f9772000-7007f9773000 rw-p 00094000 fe:00 1547                       /system/lib64/libhidltransport.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9773000-7007f977d000 r--p 00095000 fe:00 1547                       /system/lib64/libhidltransport.so
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  40 kB
+Pss:                   2 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         40 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           36 kB
+Anonymous:            40 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me ac 
+7007f977d000-7007f977e000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f977e000-7007f9786000 r--s 00000000 fe:00 185                        /system/fonts/NotoSansLao-Bold.ttf
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9786000-7007f978f000 r--s 00000000 fe:00 245                        /system/fonts/NotoSerifTamil-Bold.ttf
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f978f000-7007f9798000 r--s 00000000 fe:00 191                        /system/fonts/NotoSerifTamil-Regular.ttf
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9798000-7007f97b7000 r--s 00000000 fe:00 248                        /system/fonts/NotoSansDevanagariUI-Regular.ttf
+Size:                124 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f97b7000-7007f986a000 r--p 00000000 fe:00 1267                       /system/lib64/libicuuc.so
+Size:                716 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 440 kB
+Pss:                  45 kB
+Shared_Clean:        440 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          440 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               45 kB
+VmFlags: rd mr mw me 
+7007f986a000-7007f9966000 r-xp 000b3000 fe:00 1267                       /system/lib64/libicuuc.so
+Size:               1008 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 992 kB
+Pss:                  73 kB
+Shared_Clean:        992 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          992 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               73 kB
+VmFlags: rd ex mr mw me 
+7007f9966000-7007f9967000 rw-p 001af000 fe:00 1267                       /system/lib64/libicuuc.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9967000-7007f997b000 r--p 001b0000 fe:00 1267                       /system/lib64/libicuuc.so
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  80 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         80 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           36 kB
+Anonymous:            80 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007f997b000-7007f997d000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+7007f997d000-7007f997e000 r--s 00000000 fe:00 977                        /system/usr/hyphen-data/hyph-te.hyb
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f997e000-7007f9987000 r--s 00000000 fe:00 70                         /system/fonts/NotoSansTamil-Bold.ttf
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9987000-7007f99a8000 r--s 00000000 fe:00 124                        /system/fonts/NotoSansDevanagari-Bold.ttf
+Size:                132 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f99a8000-7007f99ac000 r--p 00000000 fe:00 1542                       /system/lib64/libstagefright_xmlparser.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                   3 kB
+Shared_Clean:         16 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me 
+7007f99ac000-7007f99b4000 r-xp 00004000 fe:00 1542                       /system/lib64/libstagefright_xmlparser.so
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f99b4000-7007f99b5000 rw-p 0000c000 fe:00 1542                       /system/lib64/libstagefright_xmlparser.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f99b5000-7007f99b6000 r--p 0000d000 fe:00 1542                       /system/lib64/libstagefright_xmlparser.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f99b6000-7007f99b8000 r--s 00000000 fe:00 234                        /system/fonts/NotoSansTagalog-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f99b8000-7007f99da000 r--s 00000000 fe:00 235                        /system/fonts/NotoSansDevanagari-Regular.ttf
+Size:                136 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f99da000-7007f99e1000 r--p 00000000 fe:00 1555                       /system/lib64/libstatslog.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                   5 kB
+Shared_Clean:         28 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           28 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                5 kB
+VmFlags: rd mr mw me 
+7007f99e1000-7007f99ef000 r-xp 00007000 fe:00 1555                       /system/lib64/libstatslog.so
+Size:                 56 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  56 kB
+Pss:                  10 kB
+Shared_Clean:         56 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           56 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               10 kB
+VmFlags: rd ex mr mw me 
+7007f99ef000-7007f99f0000 rw-p 00015000 fe:00 1555                       /system/lib64/libstatslog.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f99f0000-7007f99f1000 r--p 00016000 fe:00 1555                       /system/lib64/libstatslog.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f99f1000-7007f99f2000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f99f2000-7007f99fa000 r--s 00000000 fe:00 73                         /system/fonts/NotoSansLao-Regular.ttf
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f99fa000-7007f9a03000 r--s 00000000 fe:00 205                        /system/fonts/NotoSansTamil-Regular.ttf
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9a03000-7007f9a13000 r--p 00000000 fe:00 1224                       /system/lib64/android.hidl.memory@1.0.so
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  10 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               10 kB
+VmFlags: rd mr mw me 
+7007f9a13000-7007f9a27000 r-xp 00010000 fe:00 1224                       /system/lib64/android.hidl.memory@1.0.so
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  80 kB
+Pss:                  20 kB
+Shared_Clean:         80 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           80 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               20 kB
+VmFlags: rd ex mr mw me 
+7007f9a27000-7007f9a28000 rw-p 00024000 fe:00 1224                       /system/lib64/android.hidl.memory@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9a28000-7007f9a2b000 r--p 00025000 fe:00 1224                       /system/lib64/android.hidl.memory@1.0.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         12 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f9a2b000-7007f9a48000 r--s 00000000 fe:00 171                        /system/fonts/NotoSansGujarati-Bold.ttf
+Size:                116 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9a48000-7007f9a51000 r--p 00000000 fe:00 1723                       /system/lib64/libbase.so
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  36 kB
+Pss:                   0 kB
+Shared_Clean:         36 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           36 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007f9a51000-7007f9a5a000 r-xp 00009000 fe:00 1723                       /system/lib64/libbase.so
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  36 kB
+Pss:                   0 kB
+Shared_Clean:         36 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           36 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f9a5a000-7007f9a5b000 rw-p 00012000 fe:00 1723                       /system/lib64/libbase.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9a5b000-7007f9a5c000 r--p 00013000 fe:00 1723                       /system/lib64/libbase.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f9a5c000-7007f9a5d000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f9a5d000-7007f9a5f000 r--s 00000000 fe:00 66                         /system/fonts/NotoSansShavian-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9a5f000-7007f9a73000 r--s 00000000 fe:00 264                        /system/fonts/NotoSerifDevanagari-Bold.ttf
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9a73000-7007f9a93000 r--s 00000000 fe:00 266                        /system/fonts/NotoSerifEthiopic-Bold.otf
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9a93000-7007f9a96000 r--p 00000000 fe:00 1519                       /system/lib64/libnativeloader.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   0 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007f9a96000-7007f9a9a000 r-xp 00003000 fe:00 1519                       /system/lib64/libnativeloader.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                   0 kB
+Shared_Clean:         16 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f9a9a000-7007f9a9b000 rw-p 00007000 fe:00 1519                       /system/lib64/libnativeloader.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9a9b000-7007f9a9c000 r--p 00008000 fe:00 1519                       /system/lib64/libnativeloader.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f9a9c000-7007f9a9d000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f9a9d000-7007f9aa0000 r--s 00000000 fe:00 136                        /system/fonts/NotoSansRunic-Regular.ttf
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9aa0000-7007f9aa8000 r--s 00000000 fe:00 143                        /system/fonts/NotoSansGurmukhiUI-Bold.ttf
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9aa8000-7007f9abc000 r--s 00000000 fe:00 260                        /system/fonts/NotoSerifDevanagari-Regular.ttf
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9abc000-7007f9ad8000 r--s 00000000 fe:00 215                        /system/fonts/NotoSerifEthiopic-Regular.otf
+Size:                112 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9ad8000-7007f9b30000 r--p 00000000 fe:00 1656                       /system/lib64/libc++.so
+Size:                352 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 224 kB
+Pss:                   3 kB
+Shared_Clean:        224 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          224 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me 
+7007f9b30000-7007f9ba4000 r-xp 00058000 fe:00 1656                       /system/lib64/libc++.so
+Size:                464 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 388 kB
+Pss:                   6 kB
+Shared_Clean:        388 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          388 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd ex mr mw me 
+7007f9ba4000-7007f9ba5000 rw-p 000cc000 fe:00 1656                       /system/lib64/libc++.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9ba5000-7007f9bad000 r--p 000cd000 fe:00 1656                       /system/lib64/libc++.so
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  32 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         32 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           20 kB
+Anonymous:            32 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me ac 
+7007f9bad000-7007f9bb1000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           16 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me ac 
+7007f9bb1000-7007f9bb3000 r--s 00000000 fe:00 146                        /system/fonts/NotoSansRejang-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9bb3000-7007f9bf0000 r--s 00000000 fe:00 113                        /system/fonts/NotoSerif-Bold.ttf
+Size:                244 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9bf0000-7007f9bf1000 r--p 00000000 fe:00 1614                       /system/lib64/libhardware.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007f9bf1000-7007f9bf2000 r-xp 00001000 fe:00 1614                       /system/lib64/libhardware.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f9bf2000-7007f9bf3000 rw-p 00002000 fe:00 1614                       /system/lib64/libhardware.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9bf3000-7007f9bf4000 r--p 00003000 fe:00 1614                       /system/lib64/libhardware.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f9bf4000-7007f9bfc000 r--s 00000000 fe:00 97                         /system/fonts/NotoSansGurmukhiUI-Regular.ttf
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9bfc000-7007f9c04000 r--s 00000000 fe:00 250                        /system/fonts/NotoSerifGurmukhi-Bold.otf
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9c04000-7007f9c26000 r--p 00000000 fe:00 1714                       /system/lib64/libartbase.so
+Size:                136 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 108 kB
+Pss:                   7 kB
+Shared_Clean:        108 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          108 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                7 kB
+VmFlags: rd mr mw me 
+7007f9c26000-7007f9c72000 r-xp 00022000 fe:00 1714                       /system/lib64/libartbase.so
+Size:                304 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 164 kB
+Pss:                   7 kB
+Shared_Clean:        164 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          164 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                7 kB
+VmFlags: rd ex mr mw me 
+7007f9c72000-7007f9c73000 rw-p 0006e000 fe:00 1714                       /system/lib64/libartbase.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9c73000-7007f9c75000 r--p 0006f000 fe:00 1714                       /system/lib64/libartbase.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f9c75000-7007f9c7c000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007f9c7c000-7007f9c84000 r--s 00000000 fe:00 78                         /system/fonts/NotoSansGurmukhi-Bold.ttf
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9c84000-7007f9ca8000 r--s 00000000 fe:00 218                        /system/fonts/NotoSansEthiopic-Bold.ttf
+Size:                144 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9ca8000-7007f9caa000 r--p 00000000 fe:00 1258                       /system/lib64/libdexfile_external.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007f9caa000-7007f9cad000 r-xp 00002000 fe:00 1258                       /system/lib64/libdexfile_external.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f9cad000-7007f9cae000 rw-p 00005000 fe:00 1258                       /system/lib64/libdexfile_external.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9cae000-7007f9caf000 r--p 00006000 fe:00 1258                       /system/lib64/libdexfile_external.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f9caf000-7007f9cb0000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9cb0000-7007f9cb7000 r--s 00000000 fe:00 179                        /system/fonts/NotoSerifGurmukhi-Regular.otf
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9cb7000-7007f9cda000 r--s 00000000 fe:00 77                         /system/fonts/NotoSansEthiopic-Regular.ttf
+Size:                140 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9cda000-7007f9ce3000 r--p 00000000 fe:00 1571                       /system/lib64/libtinyxml2.so
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  36 kB
+Pss:                   7 kB
+Shared_Clean:         36 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           36 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                7 kB
+VmFlags: rd mr mw me 
+7007f9ce3000-7007f9cef000 r-xp 00009000 fe:00 1571                       /system/lib64/libtinyxml2.so
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  48 kB
+Pss:                  24 kB
+Shared_Clean:         48 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           48 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               24 kB
+VmFlags: rd ex mr mw me 
+7007f9cef000-7007f9cf0000 rw-p 00015000 fe:00 1571                       /system/lib64/libtinyxml2.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9cf0000-7007f9cf1000 r--p 00016000 fe:00 1571                       /system/lib64/libtinyxml2.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f9cf1000-7007f9cf2000 r--s 00000000 fe:00 974                        /system/usr/hyphen-data/hyph-ta.hyb
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9cf2000-7007f9cf6000 r--s 00000000 fe:00 247                        /system/fonts/NotoSansBatak-Regular.ttf
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9cf6000-7007f9d14000 r--s 00000000 fe:00 253                        /system/fonts/NotoNaskhArabicUI-Bold.ttf
+Size:                120 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9d14000-7007f9d16000 r--p 00000000 fe:00 1256                       /system/lib64/libusbhost.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   2 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me 
+7007f9d16000-7007f9d18000 r-xp 00002000 fe:00 1256                       /system/lib64/libusbhost.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f9d18000-7007f9d19000 rw-p 00004000 fe:00 1256                       /system/lib64/libusbhost.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9d19000-7007f9d1a000 r--p 00005000 fe:00 1256                       /system/lib64/libusbhost.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f9d1a000-7007f9d1e000 r--s 00000000 fe:00 240                        /system/fonts/NotoSansAhom-Regular.otf
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9d1e000-7007f9d26000 r--s 00000000 fe:00 224                        /system/fonts/NotoSansGurmukhi-Regular.ttf
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9d26000-7007f9d63000 r--s 00000000 fe:00 91                         /system/fonts/NotoSerif-Regular.ttf
+Size:                244 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9d63000-7007f9dae000 r--p 00000000 fe:00 1681                       /system/lib64/libdng_sdk.so
+Size:                300 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 116 kB
+Pss:                  33 kB
+Shared_Clean:        116 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          116 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               33 kB
+VmFlags: rd mr mw me 
+7007f9dae000-7007f9e37000 r-xp 0004b000 fe:00 1681                       /system/lib64/libdng_sdk.so
+Size:                548 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f9e37000-7007f9e38000 rw-p 000d4000 fe:00 1681                       /system/lib64/libdng_sdk.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9e38000-7007f9e3e000 r--p 000d5000 fe:00 1681                       /system/lib64/libdng_sdk.so
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         24 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            24 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me ac 
+7007f9e3e000-7007f9e3f000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9e3f000-7007f9e40000 r--s 00000000 fe:00 1016                       /system/usr/hyphen-data/hyph-pt.hyb
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9e40000-7007f9e50000 r--s 00000000 fe:00 152                        /system/fonts/NotoSerifGujarati-Bold.ttf
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9e50000-7007f9e82000 r--p 00000000 fe:00 1221                       /system/lib64/libcamera_client.so
+Size:                200 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  10 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               10 kB
+VmFlags: rd mr mw me 
+7007f9e82000-7007f9ea4000 r-xp 00032000 fe:00 1221                       /system/lib64/libcamera_client.so
+Size:                136 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f9ea4000-7007f9ea5000 rw-p 00054000 fe:00 1221                       /system/lib64/libcamera_client.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9ea5000-7007f9eb1000 r--p 00055000 fe:00 1221                       /system/lib64/libcamera_client.so
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  48 kB
+Pss:                   2 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         48 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            48 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me ac 
+7007f9eb1000-7007f9eb2000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9eb2000-7007f9eb3000 r--s 00000000 fe:00 1002                       /system/usr/hyphen-data/hyph-pa.hyb
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9eb3000-7007f9ec3000 r--s 00000000 fe:00 238                        /system/fonts/NotoSerifGujarati-Regular.ttf
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9ec3000-7007f9ed6000 r--p 00000000 fe:00 1528                       /system/lib64/libGLESv2.so
+Size:                 76 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                   7 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                7 kB
+VmFlags: rd mr mw me 
+7007f9ed6000-7007f9edd000 r-xp 00013000 fe:00 1528                       /system/lib64/libGLESv2.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                   7 kB
+Shared_Clean:         28 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           28 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                7 kB
+VmFlags: rd ex mr mw me 
+7007f9edd000-7007f9ede000 rw-p 0001a000 fe:00 1528                       /system/lib64/libGLESv2.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9ede000-7007f9edf000 r--p 0001b000 fe:00 1528                       /system/lib64/libGLESv2.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f9edf000-7007f9ee0000 r--s 00000000 fe:00 975                        /system/usr/hyphen-data/hyph-or.hyb
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9ee0000-7007f9efe000 r--s 00000000 fe:00 63                         /system/fonts/NotoNaskhArabicUI-Regular.ttf
+Size:                120 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9efe000-7007f9f1b000 r--s 00000000 fe:00 202                        /system/fonts/NotoNaskhArabic-Bold.ttf
+Size:                116 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9f1b000-7007f9f20000 r--p 00000000 fe:00 1104                       /system/lib64/libziparchive.so
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                   0 kB
+Shared_Clean:         20 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           20 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007f9f20000-7007f9f26000 r-xp 00005000 fe:00 1104                       /system/lib64/libziparchive.so
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                   1 kB
+Shared_Clean:         24 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           24 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd ex mr mw me 
+7007f9f26000-7007f9f27000 rw-p 0000b000 fe:00 1104                       /system/lib64/libziparchive.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9f27000-7007f9f28000 r--p 0000c000 fe:00 1104                       /system/lib64/libziparchive.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f9f28000-7007f9f2c000 r--s 00000000 fe:00 280                        /system/fonts/NotoSansThaana-Bold.ttf
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9f2c000-7007f9f32000 r--s 00000000 fe:00 74                         /system/fonts/NotoSerifGeorgian-Bold.ttf
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9f32000-7007f9f4f000 r--s 00000000 fe:00 122                        /system/fonts/NotoNaskhArabic-Regular.ttf
+Size:                116 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9f4f000-7007f9f50000 r--p 00000000 fe:00 1709                       /system/lib64/libETC1.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+7007f9f50000-7007f9f52000 r-xp 00001000 fe:00 1709                       /system/lib64/libETC1.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f9f52000-7007f9f53000 rw-p 00003000 fe:00 1709                       /system/lib64/libETC1.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9f53000-7007f9f54000 r--p 00004000 fe:00 1709                       /system/lib64/libETC1.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f9f54000-7007f9f5a000 r--s 00000000 fe:00 262                        /system/fonts/NotoSerifGeorgian-Regular.ttf
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9f5a000-7007f9f5f000 r--s 00000000 fe:00 170                        /system/fonts/NotoSansGeorgian-Bold.ttf
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9f5f000-7007f9f64000 r--s 00000000 fe:00 290                        /system/fonts/NotoSansGeorgian-Regular.ttf
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9f64000-7007f9f69000 r--s 00000000 fe:00 68                         /system/fonts/NotoSansThaiUI-Bold.ttf
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9f69000-7007f9f6f000 r--s 00000000 fe:00 134                        /system/fonts/NotoSansThaiUI-Regular.ttf
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9f6f000-7007f9f8c000 r--s 00000000 fe:00 222                        /system/fonts/DancingScript-Bold.ttf
+Size:                116 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9f8c000-7007f9f8d000 r--p 00000000 fe:00 1663                       /system/lib64/libstagefright_http_support.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+7007f9f8d000-7007f9f8f000 r-xp 00001000 fe:00 1663                       /system/lib64/libstagefright_http_support.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007f9f8f000-7007f9f90000 rw-p 00003000 fe:00 1663                       /system/lib64/libstagefright_http_support.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007f9f90000-7007f9f91000 r--p 00004000 fe:00 1663                       /system/lib64/libstagefright_http_support.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007f9f91000-7007f9f92000 r--s 00000000 fe:00 1031                       /system/usr/hyphen-data/hyph-mr.hyb
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9f92000-7007f9f94000 r--s 00000000 fe:00 123                        /system/fonts/NotoSansPhoenician-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9f94000-7007f9fb1000 r--s 00000000 fe:00 103                        /system/fonts/DancingScript-Regular.ttf
+Size:                116 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9fb1000-7007f9fcc000 r--s 00000000 fe:00 135                        /system/fonts/DroidSansMono.ttf
+Size:                108 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007f9fcc000-7007f9fe7000 r--p 00000000 fe:00 1230                       /system/lib64/libdexfile.so
+Size:                108 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  80 kB
+Pss:                   4 kB
+Shared_Clean:         80 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           80 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me 
+7007f9fe7000-7007fa014000 r-xp 0001b000 fe:00 1230                       /system/lib64/libdexfile.so
+Size:                180 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 176 kB
+Pss:                   9 kB
+Shared_Clean:        176 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          176 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                9 kB
+VmFlags: rd ex mr mw me 
+7007fa014000-7007fa015000 rw-p 00048000 fe:00 1230                       /system/lib64/libdexfile.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa015000-7007fa017000 r--p 00049000 fe:00 1230                       /system/lib64/libdexfile.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa017000-7007fa01b000 r--s 00000000 fe:00 263                        /system/fonts/NotoSansThaana-Regular.ttf
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa01b000-7007fa03b000 r--s 00000000 00:13 6728                       /dev/__properties__/u:object_r:persist_debug_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr me ms 
+7007fa03b000-7007fa075000 r--s 00000000 07:08 18                         /apex/com.android.tzdata/etc/icu/icu_tzdata.dat
+Size:                232 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 204 kB
+Pss:                  24 kB
+Shared_Clean:        204 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          204 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               24 kB
+VmFlags: rd mr me ms 
+7007fa075000-7007fa077000 r--p 00000000 fe:00 1575                       /system/lib64/libspeexresampler.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007fa077000-7007fa07a000 r-xp 00002000 fe:00 1575                       /system/lib64/libspeexresampler.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007fa07a000-7007fa07b000 rw-p 00005000 fe:00 1575                       /system/lib64/libspeexresampler.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa07b000-7007fa07c000 r--p 00006000 fe:00 1575                       /system/lib64/libspeexresampler.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa07c000-7007fa081000 r--s 00000000 fe:00 249                        /system/fonts/NotoSerifThai-Bold.ttf
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa081000-7007fa09f000 r--s 001db000 fe:00 3224                       /system/framework/ext.jar
+Size:                120 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa09f000-7007fa0a7000 r--p 00000000 fe:00 1624                       /system/lib64/libz.so
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  32 kB
+Pss:                   1 kB
+Shared_Clean:         32 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           32 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+7007fa0a7000-7007fa0b7000 r-xp 00008000 fe:00 1624                       /system/lib64/libz.so
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                   5 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                5 kB
+VmFlags: rd ex mr mw me 
+7007fa0b7000-7007fa0b8000 rw-p 00018000 fe:00 1624                       /system/lib64/libz.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa0b8000-7007fa0b9000 r--p 00019000 fe:00 1624                       /system/lib64/libz.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa0b9000-7007fa0ba000 r--s 00000000 fe:00 1015                       /system/usr/hyphen-data/hyph-ml.hyb
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa0ba000-7007fa0bf000 r--s 00000000 fe:00 278                        /system/fonts/NotoSerifThai-Regular.ttf
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa0bf000-7007fa0c4000 r--s 00000000 fe:00 243                        /system/fonts/NotoSansThai-Bold.ttf
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa0c4000-7007fa0ca000 r--s 00000000 fe:00 128                        /system/fonts/NotoSansThai-Regular.ttf
+Size:                 24 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa0ca000-7007fa0cf000 r--s 00000000 fe:00 279                        /system/fonts/NotoSerifHebrew-Bold.ttf
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa0cf000-7007fa0ef000 r--s 00000000 00:13 6707                       /dev/__properties__/u:object_r:exported2_default_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa0ef000-7007fa0f0000 r--p 00000000 fe:00 1574                       /system/lib64/libpackagelistparser.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007fa0f0000-7007fa0f1000 r-xp 00001000 fe:00 1574                       /system/lib64/libpackagelistparser.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007fa0f1000-7007fa0f2000 rw-p 00002000 fe:00 1574                       /system/lib64/libpackagelistparser.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa0f2000-7007fa0f3000 r--p 00003000 fe:00 1574                       /system/lib64/libpackagelistparser.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa0f3000-7007fa0f6000 r--s 00000000 fe:00 207                        /system/fonts/NotoSansOsage-Regular.ttf
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa0f6000-7007fa100000 r--s 00000000 fe:00 164                        /system/fonts/CarroisGothicSC-Regular.ttf
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa100000-7007fa101000 r--p 00000000 fe:00 1120                       /system/lib64/android.hardware.media@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007fa101000-7007fa102000 r-xp 00001000 fe:00 1120                       /system/lib64/android.hardware.media@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007fa102000-7007fa103000 rw-p 00002000 fe:00 1120                       /system/lib64/android.hardware.media@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa103000-7007fa104000 r--p 00003000 fe:00 1120                       /system/lib64/android.hardware.media@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa104000-7007fa108000 r--s 00000000 fe:00 167                        /system/fonts/NotoSerifArmenian-Bold.ttf
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa108000-7007fa10d000 r--s 00000000 fe:00 223                        /system/fonts/NotoSerifHebrew-Regular.ttf
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa10d000-7007fa147000 r--s 00000000 07:08 18                         /apex/com.android.tzdata/etc/icu/icu_tzdata.dat
+Size:                232 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  44 kB
+Pss:                   2 kB
+Shared_Clean:         44 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           44 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr me ms rr 
+7007fa147000-7007fa18a000 r--p 00000000 fe:00 1222                       /system/lib64/libsonivox.so
+Size:                268 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  10 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               10 kB
+VmFlags: rd mr mw me 
+7007fa18a000-7007fa19f000 r-xp 00043000 fe:00 1222                       /system/lib64/libsonivox.so
+Size:                 84 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007fa19f000-7007fa1a0000 rw-p 00058000 fe:00 1222                       /system/lib64/libsonivox.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa1a0000-7007fa1a1000 r--p 00059000 fe:00 1222                       /system/lib64/libsonivox.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa1a1000-7007fa1a9000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa1a9000-7007fa1ad000 r--s 00000000 fe:00 148                        /system/fonts/NotoSerifArmenian-Regular.ttf
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa1ad000-7007fa1b2000 r--s 00000000 fe:00 138                        /system/fonts/NotoSansHebrew-Bold.ttf
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa1b2000-7007fa1d2000 r--s 00000000 00:13 6732                       /dev/__properties__/u:object_r:radio_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa1d2000-7007fa1d3000 r--p 00000000 fe:00 1629                       /system/lib64/libdexfile_support.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007fa1d3000-7007fa1d4000 r-xp 00001000 fe:00 1629                       /system/lib64/libdexfile_support.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007fa1d4000-7007fa1d5000 rw-p 00002000 fe:00 1629                       /system/lib64/libdexfile_support.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa1d5000-7007fa1d6000 r--p 00003000 fe:00 1629                       /system/lib64/libdexfile_support.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa1d6000-7007fa1da000 r--s 00000000 fe:00 104                        /system/fonts/NotoSansArmenian-Bold.ttf
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa1da000-7007fa1df000 r--s 00000000 fe:00 158                        /system/fonts/NotoSansHebrew-Regular.ttf
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa1df000-7007fa1ee000 r--s 00000000 fe:00 192                        /system/fonts/ComingSoon.ttf
+Size:                 60 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa1ee000-7007fa20e000 rw-p 00000000 00:00 0                          [anon:dalvik-LinearAlloc]
+Name:           [anon:dalvik-LinearAlloc]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  80 kB
+Pss:                  76 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:        76 kB
+Referenced:           76 kB
+Anonymous:            80 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               76 kB
+VmFlags: rd wr mr mw me ac 
+7007fa20e000-7007fa21e000 r--p 00000000 fe:00 1535                       /system/lib64/libmemunreachable.so
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  56 kB
+Pss:                   9 kB
+Shared_Clean:         56 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           56 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                9 kB
+VmFlags: rd mr mw me 
+7007fa21e000-7007fa239000 r-xp 00010000 fe:00 1535                       /system/lib64/libmemunreachable.so
+Size:                108 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 100 kB
+Pss:                  21 kB
+Shared_Clean:        100 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          100 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               21 kB
+VmFlags: rd ex mr mw me 
+7007fa239000-7007fa23a000 rw-p 0002b000 fe:00 1535                       /system/lib64/libmemunreachable.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa23a000-7007fa23c000 r--p 0002c000 fe:00 1535                       /system/lib64/libmemunreachable.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa23c000-7007fa23d000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa23d000-7007fa24e000 r--s 00000000 fe:00 200                        /system/fonts/CutiveMono.ttf
+Size:                 68 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa24e000-7007fa255000 r--p 00000000 fe:00 1174                       /system/lib64/libmediaextractor.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                   4 kB
+Shared_Clean:         28 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           28 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me 
+7007fa255000-7007fa25c000 r-xp 00007000 fe:00 1174                       /system/lib64/libmediaextractor.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                   9 kB
+Shared_Clean:         28 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           28 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                9 kB
+VmFlags: rd ex mr mw me 
+7007fa25c000-7007fa25d000 rw-p 0000e000 fe:00 1174                       /system/lib64/libmediaextractor.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa25d000-7007fa25e000 r--p 0000f000 fe:00 1174                       /system/lib64/libmediaextractor.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa25e000-7007fa25f000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa25f000-7007fa263000 r--s 00000000 fe:00 255                        /system/fonts/NotoSansArmenian-Regular.ttf
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa263000-7007fa264000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007fa264000-7007fa267000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa267000-7007fa268000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007fa269000-7007fa26b000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa26b000-7007fa26c000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007fa26c000-7007fa26f000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa26f000-7007fa270000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007fa270000-7007fa271000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007fa271000-7007fa279000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa279000-7007fa27a000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007fa27a000-7007fa27d000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa27d000-7007fa27e000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007fa27e000-7007fa27f000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007fa27f000-7007fa287000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa287000-7007fa292000 r--p 00000000 fe:00 1524                       /system/lib64/android.hardware.graphics.allocator@2.0.so
+Size:                 44 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  44 kB
+Pss:                   4 kB
+Shared_Clean:         44 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           44 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me 
+7007fa292000-7007fa29c000 r-xp 0000b000 fe:00 1524                       /system/lib64/android.hardware.graphics.allocator@2.0.so
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007fa29c000-7007fa29d000 rw-p 00015000 fe:00 1524                       /system/lib64/android.hardware.graphics.allocator@2.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa29d000-7007fa29f000 r--p 00016000 fe:00 1524                       /system/lib64/android.hardware.graphics.allocator@2.0.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa29f000-7007fa2a0000 r--s 00000000 fe:00 995                        /system/usr/hyphen-data/hyph-la.hyb
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa2a0000-7007fa2a1000 rw-p 00000000 00:00 0                          [anon:linker_alloc_small_objects]
+Name:           [anon:linker_alloc_small_objects]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fa2a1000-7007fa2a3000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fa2a4000-7007fa2a6000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa2a6000-7007fa2a7000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007fa2a7000-7007fa2aa000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa2aa000-7007fa2ab000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007fa2ab000-7007fa2ac000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007fa2ac000-7007fa2b4000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa2b4000-7007fa2b5000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007fa2b5000-7007fa2b8000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa2b8000-7007fa2b9000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007fa2b9000-7007fa2ba000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007fa2ba000-7007fa2c2000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa2c2000-7007fa2ee000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                176 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa2ee000-7007fa2f2000 r--p 00000000 fe:00 1525                       /system/lib64/libheif.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                   4 kB
+Shared_Clean:         16 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me 
+7007fa2f2000-7007fa2f4000 r-xp 00004000 fe:00 1525                       /system/lib64/libheif.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007fa2f4000-7007fa2f5000 rw-p 00006000 fe:00 1525                       /system/lib64/libheif.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa2f5000-7007fa2f6000 r--p 00007000 fe:00 1525                       /system/lib64/libheif.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa2f6000-7007fa2f7000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fa2f7000-7007fa317000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa317000-7007fa32a000 r--p 00000000 fe:00 1186                       /system/lib64/libhwbinder.so
+Size:                 76 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                   2 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me 
+7007fa32a000-7007fa33b000 r-xp 00013000 fe:00 1186                       /system/lib64/libhwbinder.so
+Size:                 68 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  68 kB
+Pss:                   2 kB
+Shared_Clean:         68 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           68 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd ex mr mw me 
+7007fa33b000-7007fa33c000 rw-p 00024000 fe:00 1186                       /system/lib64/libhwbinder.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa33c000-7007fa33e000 r--p 00025000 fe:00 1186                       /system/lib64/libhwbinder.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa33e000-7007fa33f000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fa33f000-7007fa340000 r--s 00000000 fe:00 968                        /system/usr/hyphen-data/hyph-kn.hyb
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa340000-7007fa348000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa348000-7007fa36b000 r--p 00000000 fe:00 1698                       /system/lib64/libunwindstack.so
+Size:                140 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                   6 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd mr mw me 
+7007fa36b000-7007fa39c000 r-xp 00023000 fe:00 1698                       /system/lib64/libunwindstack.so
+Size:                196 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007fa39c000-7007fa39d000 rw-p 00054000 fe:00 1698                       /system/lib64/libunwindstack.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa39d000-7007fa3a4000 r--p 00055000 fe:00 1698                       /system/lib64/libunwindstack.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         28 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            28 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me ac 
+7007fa3a4000-7007fa3a5000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa3a5000-7007fa3a6000 r--s 00000000 fe:00 999                        /system/usr/hyphen-data/hyph-hy.hyb
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa3a6000-7007fa3e2000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                240 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa3e2000-7007fa3e4000 r--p 00000000 fe:00 1236                       /system/lib64/libaudiomanager.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   1 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+7007fa3e4000-7007fa3e5000 r-xp 00002000 fe:00 1236                       /system/lib64/libaudiomanager.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007fa3e5000-7007fa3e6000 rw-p 00003000 fe:00 1236                       /system/lib64/libaudiomanager.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa3e6000-7007fa3e7000 r--p 00004000 fe:00 1236                       /system/lib64/libaudiomanager.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa3e7000-7007fa3e8000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa3e8000-7007fa420000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                224 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa420000-7007fa42d000 r--p 00000000 fe:00 1241                       /system/lib64/libsensor.so
+Size:                 52 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  52 kB
+Pss:                  10 kB
+Shared_Clean:         52 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           52 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               10 kB
+VmFlags: rd mr mw me 
+7007fa42d000-7007fa434000 r-xp 0000d000 fe:00 1241                       /system/lib64/libsensor.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                   9 kB
+Shared_Clean:         28 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           28 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                9 kB
+VmFlags: rd ex mr mw me 
+7007fa434000-7007fa435000 rw-p 00014000 fe:00 1241                       /system/lib64/libsensor.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa435000-7007fa438000 r--p 00015000 fe:00 1241                       /system/lib64/libsensor.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         12 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa438000-7007fa439000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fa439000-7007fa43b000 r--s 00000000 fe:00 82                         /system/fonts/NotoSansOsmanya-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa43b000-7007fa477000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                240 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa477000-7007fa478000 r--p 00000000 fe:00 1182                       /system/lib64/libgraphicsenv.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007fa478000-7007fa479000 r-xp 00001000 fe:00 1182                       /system/lib64/libgraphicsenv.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007fa479000-7007fa47a000 rw-p 00002000 fe:00 1182                       /system/lib64/libgraphicsenv.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa47a000-7007fa47b000 r--p 00003000 fe:00 1182                       /system/lib64/libgraphicsenv.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa47b000-7007fa47c000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   2 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd wr mr mw me ac 
+7007fa47c000-7007fa47d000 r--s 00000000 fe:00 993                        /system/usr/hyphen-data/hyph-hr.hyb
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa47d000-7007fa485000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa485000-7007fa48c000 r--p 00000000 fe:00 1132                       /system/lib64/liblog.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                   0 kB
+Shared_Clean:         28 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           28 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007fa48c000-7007fa49d000 r-xp 00007000 fe:00 1132                       /system/lib64/liblog.so
+Size:                 68 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  68 kB
+Pss:                   1 kB
+Shared_Clean:         68 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           68 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd ex mr mw me 
+7007fa49d000-7007fa49e000 rw-p 00018000 fe:00 1132                       /system/lib64/liblog.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fa49e000-7007fa49f000 r--p 00019000 fe:00 1132                       /system/lib64/liblog.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa49f000-7007fa4a0000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fa4a0000-7007fa4d8000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                224 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa4d8000-7007fa4ec000 r--p 00000000 fe:00 1108                       /system/lib64/libdrmframework.so
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  11 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               11 kB
+VmFlags: rd mr mw me 
+7007fa4ec000-7007fa4f8000 r-xp 00014000 fe:00 1108                       /system/lib64/libdrmframework.so
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  48 kB
+Pss:                  12 kB
+Shared_Clean:         48 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           48 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd ex mr mw me 
+7007fa4f8000-7007fa4f9000 rw-p 00020000 fe:00 1108                       /system/lib64/libdrmframework.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa4f9000-7007fa4fe000 r--p 00021000 fe:00 1108                       /system/lib64/libdrmframework.so
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         20 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:            20 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me ac 
+7007fa4fe000-7007fa4ff000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fa4ff000-7007fa527000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                160 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa527000-7007fa540000 r--p 00000000 fe:00 1577                       /system/lib64/android.hardware.graphics.bufferqueue@1.0.so
+Size:                100 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                   9 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                9 kB
+VmFlags: rd mr mw me 
+7007fa540000-7007fa568000 r-xp 00019000 fe:00 1577                       /system/lib64/android.hardware.graphics.bufferqueue@1.0.so
+Size:                160 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007fa568000-7007fa569000 rw-p 00041000 fe:00 1577                       /system/lib64/android.hardware.graphics.bufferqueue@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa569000-7007fa56d000 r--p 00042000 fe:00 1577                       /system/lib64/android.hardware.graphics.bufferqueue@1.0.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         16 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa56d000-7007fa56f000 r--s 00000000 fe:00 58                         /system/fonts/NotoSansOldTurkic-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa56f000-7007fa597000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                160 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa597000-7007fa59b000 r--p 00000000 fe:00 1619                       /system/lib64/libnativehelper.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                   3 kB
+Shared_Clean:         16 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me 
+7007fa59b000-7007fa59e000 r-xp 00004000 fe:00 1619                       /system/lib64/libnativehelper.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   0 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007fa59e000-7007fa59f000 rw-p 00007000 fe:00 1619                       /system/lib64/libnativehelper.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa59f000-7007fa5a0000 r--p 00008000 fe:00 1619                       /system/lib64/libnativehelper.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa5a0000-7007fa5a1000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fa5a1000-7007fa5a3000 r--s 00000000 fe:00 208                        /system/fonts/NotoSansOldSouthArabian-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa5a3000-7007fa5c3000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa5c3000-7007fa5cc000 r--p 00000000 fe:00 1181                       /system/lib64/libimg_utils.so
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  36 kB
+Pss:                   9 kB
+Shared_Clean:         36 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           36 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                9 kB
+VmFlags: rd mr mw me 
+7007fa5cc000-7007fa5d4000 r-xp 00009000 fe:00 1181                       /system/lib64/libimg_utils.so
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007fa5d4000-7007fa5d5000 rw-p 00011000 fe:00 1181                       /system/lib64/libimg_utils.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa5d5000-7007fa5d7000 r--p 00012000 fe:00 1181                       /system/lib64/libimg_utils.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa5d7000-7007fa5d8000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa5d8000-7007fa5da000 r--s 00000000 fe:00 193                        /system/fonts/NotoSansOldItalic-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa5da000-7007fa62e000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                336 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa62e000-7007fa630000 r--p 00000000 fe:00 1251                       /system/lib64/libnetd_client.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007fa630000-7007fa632000 r-xp 00002000 fe:00 1251                       /system/lib64/libnetd_client.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007fa632000-7007fa633000 rw-p 00004000 fe:00 1251                       /system/lib64/libnetd_client.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa633000-7007fa634000 r--p 00005000 fe:00 1251                       /system/lib64/libnetd_client.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa634000-7007fa635000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa635000-7007fa636000 r--s 00000000 fe:00 1005                       /system/usr/hyphen-data/hyph-hi.hyb
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa636000-7007fa64a000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa64a000-7007fa64b000 r--p 00000000 fe:00 1628                       /system/lib64/android.hardware.graphics.common@1.1.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007fa64b000-7007fa64c000 r-xp 00001000 fe:00 1628                       /system/lib64/android.hardware.graphics.common@1.1.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007fa64c000-7007fa64d000 rw-p 00002000 fe:00 1628                       /system/lib64/android.hardware.graphics.common@1.1.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa64d000-7007fa64e000 r--p 00003000 fe:00 1628                       /system/lib64/android.hardware.graphics.common@1.1.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa64e000-7007fa696000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                288 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa696000-7007fa6e4000 r--p 00000000 fe:00 1194                       /system/lib64/libgui.so
+Size:                312 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 232 kB
+Pss:                  39 kB
+Shared_Clean:        232 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          232 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               39 kB
+VmFlags: rd mr mw me 
+7007fa6e4000-7007fa72c000 r-xp 0004e000 fe:00 1194                       /system/lib64/libgui.so
+Size:                288 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 288 kB
+Pss:                  49 kB
+Shared_Clean:        288 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          288 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               49 kB
+VmFlags: rd ex mr mw me 
+7007fa72c000-7007fa72d000 rw-p 00096000 fe:00 1194                       /system/lib64/libgui.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa72d000-7007fa73e000 r--p 00097000 fe:00 1194                       /system/lib64/libgui.so
+Size:                 68 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  68 kB
+Pss:                   3 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         68 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           48 kB
+Anonymous:            68 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me ac 
+7007fa73e000-7007fa73f000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fa73f000-7007fa740000 r--s 00000000 fe:00 1004                       /system/usr/hyphen-data/hyph-gu.hyb
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa740000-7007fa742000 r--s 00000000 fe:00 166                        /system/fonts/NotoSansOlChiki-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa742000-7007fa74e000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa74e000-7007fa755000 r--p 00000000 fe:00 1726                       /system/lib64/libGLESv1_CM.so
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  28 kB
+Pss:                   4 kB
+Shared_Clean:         28 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           28 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me 
+7007fa755000-7007fa758000 r-xp 00007000 fe:00 1726                       /system/lib64/libGLESv1_CM.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   3 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd ex mr mw me 
+7007fa758000-7007fa759000 rw-p 0000a000 fe:00 1726                       /system/lib64/libGLESv1_CM.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa759000-7007fa75a000 r--p 0000b000 fe:00 1726                       /system/lib64/libGLESv1_CM.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa75a000-7007fa75b000 r--s 00000000 fe:00 1011                       /system/usr/hyphen-data/hyph-eu.hyb
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa75b000-7007fa75d000 r--s 00000000 fe:00 71                         /system/fonts/NotoSansOgham-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa75d000-7007fa785000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                160 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa785000-7007fa78a000 r--p 00000000 fe:00 1207                       /system/lib64/libcamera_metadata.so
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                   2 kB
+Shared_Clean:         20 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           20 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me 
+7007fa78a000-7007fa78d000 r-xp 00005000 fe:00 1207                       /system/lib64/libcamera_metadata.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007fa78d000-7007fa78f000 rw-p 00008000 fe:00 1207                       /system/lib64/libcamera_metadata.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa78f000-7007fa790000 r--p 0000a000 fe:00 1207                       /system/lib64/libcamera_metadata.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa790000-7007fa791000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa791000-7007fa793000 r--s 00000000 fe:00 88                         /system/fonts/NotoSansLydian-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa793000-7007fa7db000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                288 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa7db000-7007fa806000 r--p 00000000 fe:00 1248                       /system/lib64/libandroidfw.so
+Size:                172 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 128 kB
+Pss:                  19 kB
+Shared_Clean:        128 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          128 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               19 kB
+VmFlags: rd mr mw me 
+7007fa806000-7007fa839000 r-xp 0002b000 fe:00 1248                       /system/lib64/libandroidfw.so
+Size:                204 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 200 kB
+Pss:                  11 kB
+Shared_Clean:        200 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          200 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               11 kB
+VmFlags: rd ex mr mw me 
+7007fa839000-7007fa83a000 rw-p 0005e000 fe:00 1248                       /system/lib64/libandroidfw.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa83a000-7007fa83c000 r--p 0005f000 fe:00 1248                       /system/lib64/libandroidfw.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa83c000-7007fa83d000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fa83d000-7007fa83e000 r--s 00000000 fe:00 1027                       /system/usr/hyphen-data/hyph-bn.hyb
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa83e000-7007fa840000 r--s 00000000 fe:00 87                         /system/fonts/NotoSansLycian-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa840000-7007fa85c000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                112 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa85c000-7007fa88b000 r--p 00000000 fe:00 1556                       /system/lib64/android.hardware.media.omx@1.0.so
+Size:                188 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 124 kB
+Pss:                  23 kB
+Shared_Clean:        124 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          124 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               23 kB
+VmFlags: rd mr mw me 
+7007fa88b000-7007fa8dd000 r-xp 0002f000 fe:00 1556                       /system/lib64/android.hardware.media.omx@1.0.so
+Size:                328 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 292 kB
+Pss:                  60 kB
+Shared_Clean:        292 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          292 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               60 kB
+VmFlags: rd ex mr mw me 
+7007fa8dd000-7007fa8de000 rw-p 00081000 fe:00 1556                       /system/lib64/android.hardware.media.omx@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa8de000-7007fa8e6000 r--p 00082000 fe:00 1556                       /system/lib64/android.hardware.media.omx@1.0.so
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  32 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         32 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           20 kB
+Anonymous:            32 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me ac 
+7007fa8e6000-7007fa8e9000 r--s 00000000 fe:00 216                        /system/fonts/NotoSansLimbu-Regular.ttf
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa8e9000-7007fa919000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                192 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa919000-7007fa92b000 r--p 00000000 fe:00 1466                       /system/lib64/libui.so
+Size:                 72 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                   6 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd mr mw me 
+7007fa92b000-7007fa93d000 r-xp 00012000 fe:00 1466                       /system/lib64/libui.so
+Size:                 72 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  52 kB
+Pss:                   6 kB
+Shared_Clean:         52 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           52 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                6 kB
+VmFlags: rd ex mr mw me 
+7007fa93d000-7007fa93e000 rw-p 00024000 fe:00 1466                       /system/lib64/libui.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa93e000-7007fa93f000 r--p 00025000 fe:00 1466                       /system/lib64/libui.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa93f000-7007fa940000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fa940000-7007fa94a000 r--p 00000000 fe:00 1667                       /system/lib64/android.hardware.memtrack@1.0.so
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  40 kB
+Pss:                  10 kB
+Shared_Clean:         40 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           40 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               10 kB
+VmFlags: rd mr mw me 
+7007fa94a000-7007fa953000 r-xp 0000a000 fe:00 1667                       /system/lib64/android.hardware.memtrack@1.0.so
+Size:                 36 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  36 kB
+Pss:                  18 kB
+Shared_Clean:         36 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           36 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               18 kB
+VmFlags: rd ex mr mw me 
+7007fa953000-7007fa954000 rw-p 00013000 fe:00 1667                       /system/lib64/android.hardware.memtrack@1.0.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa954000-7007fa956000 r--p 00014000 fe:00 1667                       /system/lib64/android.hardware.memtrack@1.0.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa956000-7007fa958000 r--s 00000000 fe:00 81                         /system/fonts/NotoSansLisu-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa958000-7007fa9a0000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                288 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa9a0000-7007fa9aa000 r--p 00000000 fe:00 1654                       /system/lib64/libbacktrace.so
+Size:                 40 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  40 kB
+Pss:                   3 kB
+Shared_Clean:         40 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           40 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me 
+7007fa9aa000-7007fa9ba000 r-xp 0000a000 fe:00 1654                       /system/lib64/libbacktrace.so
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007fa9ba000-7007fa9bb000 rw-p 0001a000 fe:00 1654                       /system/lib64/libbacktrace.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa9bb000-7007fa9bc000 r--p 0001b000 fe:00 1654                       /system/lib64/libbacktrace.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa9bc000-7007fa9bd000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa9bd000-7007fa9d9000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                112 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa9d9000-7007fa9dd000 r--p 00000000 fe:00 1637                       /system/lib64/libbpf.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                   3 kB
+Shared_Clean:         16 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me 
+7007fa9dd000-7007fa9e1000 r-xp 00004000 fe:00 1637                       /system/lib64/libbpf.so
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007fa9e1000-7007fa9e2000 rw-p 00008000 fe:00 1637                       /system/lib64/libbpf.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fa9e2000-7007fa9e3000 r--p 00009000 fe:00 1637                       /system/lib64/libbpf.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fa9e3000-7007fa9e4000 r--s 00000000 fe:00 966                        /system/usr/hyphen-data/hyph-bg.hyb
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa9e4000-7007fa9e6000 r--s 00000000 fe:00 151                        /system/fonts/NotoSansKayahLi-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fa9e6000-7007faa16000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                192 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faa16000-7007faa18000 r--p 00000000 fe:00 1465                       /system/lib64/libbinderthreadstate.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007faa18000-7007faa1a000 r-xp 00002000 fe:00 1465                       /system/lib64/libbinderthreadstate.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007faa1a000-7007faa1b000 rw-p 00004000 fe:00 1465                       /system/lib64/libbinderthreadstate.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faa1b000-7007faa1c000 r--p 00005000 fe:00 1465                       /system/lib64/libbinderthreadstate.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007faa1c000-7007faa1d000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007faa1d000-7007faa59000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                240 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faa59000-7007faa5c000 r--p 00000000 fe:00 1155                       /system/lib64/libutilscallstack.so
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   1 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+7007faa5c000-7007faa5e000 r-xp 00003000 fe:00 1155                       /system/lib64/libutilscallstack.so
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007faa5e000-7007faa5f000 rw-p 00005000 fe:00 1155                       /system/lib64/libutilscallstack.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faa5f000-7007faa60000 r--p 00006000 fe:00 1155                       /system/lib64/libutilscallstack.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007faa60000-7007faa61000 r--s 00000000 fe:00 990                        /system/usr/hyphen-data/hyph-as.hyb
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007faa61000-7007faa63000 r--s 00000000 fe:00 172                        /system/fonts/NotoSansInscriptionalParthian-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007faa63000-7007faa97000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                208 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faa97000-7007faaad000 r--p 00000000 fe:00 1540                       /system/lib64/libRScpp.so
+Size:                 88 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                  12 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd mr mw me 
+7007faaad000-7007faadb000 r-xp 00016000 fe:00 1540                       /system/lib64/libRScpp.so
+Size:                184 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me 
+7007faadb000-7007faadc000 rw-p 00044000 fe:00 1540                       /system/lib64/libRScpp.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faadc000-7007faadd000 r--p 00045000 fe:00 1540                       /system/lib64/libRScpp.so
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007faadd000-7007faade000 rw-p 00000000 00:00 0                          [anon:.bss]
+Name:           [anon:.bss]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faade000-7007faaf2000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faaf2000-7007fab32000 rw-p 00000000 00:00 0 
+Size:                256 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fab32000-7007fab33000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fab33000-7007fab36000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me ac 
+7007fab36000-7007fab37000 r--s 00046000 fe:00 1943                       /system/priv-app/SettingsProvider/SettingsProvider.apk
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         4 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr me ms 
+7007fab37000-7007fab38000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fab38000-7007fab3a000 r--s 00000000 fe:00 286                        /system/fonts/NotoSansInscriptionalPahlavi-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fab3a000-7007fab52000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 96 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fab52000-7007fab53000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fab53000-7007fab54000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fab54000-7007fab55000 rw-p 00000000 00:00 0                          [anon:linker_alloc_lob]
+Name:           [anon:linker_alloc_lob]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fab55000-7007fab61000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fab61000-7007fab62000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fab62000-7007fab65000 r--s 00000000 fe:00 149                        /system/fonts/NotoSansElbasan-Regular.otf
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fab65000-7007fab8d000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                160 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fab8d000-7007fab8f000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fab8f000-7007fab93000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fab93000-7007fab94000 rw-p 00000000 00:00 0                          [anon:linker_alloc_small_objects]
+Name:           [anon:linker_alloc_small_objects]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fab94000-7007fab95000 r--s 00000000 fe:30 18                         /vendor/overlay/framework-res__auto_generated_rro.apk
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fab95000-7007fab99000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fab99000-7007fab9a000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fab9a000-7007fab9b000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fab9b000-7007fab9c000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fab9c000-7007fab9d000 r--s 00004000 fe:30 18                         /vendor/overlay/framework-res__auto_generated_rro.apk
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fab9d000-7007fabad000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fabad000-7007fabae000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fabae000-7007fabd6000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                160 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fabd6000-7007fabd7000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fabd7000-7007fabda000 r--s 00000000 fe:00 229                        /system/fonts/NotoSansDeseret-Regular.ttf
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fabda000-7007fabee000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fabee000-7007fabef000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fabef000-7007fabf1000 r--s 00000000 fe:00 189                        /system/fonts/NotoSansImperialAramaic-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fabf1000-7007fabfd000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fabfd000-7007fabfe000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fabfe000-7007fabff000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fabff000-7007fac37000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                224 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fac37000-7007fac39000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fac39000-7007fac3b000 r--s 00000000 fe:00 265                        /system/fonts/NotoSansHanunoo-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fac3b000-7007fac57000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                112 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fac57000-7007fac58000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fac58000-7007fac5a000 r--s 00000000 fe:00 98                         /system/fonts/NotoSansGothic-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fac5a000-7007fac8e000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                208 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fac8e000-7007fac8f000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fac8f000-7007fac91000 r--s 00000000 fe:00 114                        /system/fonts/NotoSansCypriot-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fac91000-7007faca1000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faca1000-7007faca2000 r--p 00000000 00:00 0                          [anon:atexit handlers]
+Name:           [anon:atexit handlers]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007faca2000-7007faca3000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007faca3000-7007faca4000 r--s 00000000 fe:10 237571                     /data/resource-cache/vendor@overlay@framework-res__auto_generated_rro.apk@idmap
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007faca4000-7007faca6000 r--s 00000000 fe:00 61                         /system/fonts/NotoSansCarian-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007faca6000-7007facb6000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007facb6000-7007facb7000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007facb7000-7007facc7000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007facc7000-7007facc8000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007facc8000-7007facca000 r--s 00000000 fe:00 102                        /system/fonts/NotoSansBuhid-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007facca000-7007facce000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007facce000-7007faccf000 rw-p 00000000 00:00 0                          [anon:linker_alloc_small_objects]
+Name:           [anon:linker_alloc_small_objects]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007faccf000-7007face3000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007face3000-7007face4000 rw-p 00000000 00:00 0                          [anon:linker_alloc_small_objects]
+Name:           [anon:linker_alloc_small_objects]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007face4000-7007face6000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+7007face6000-7007face8000 r--s 00000000 fe:00 155                        /system/fonts/NotoSansBuginese-Regular.ttf
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007face8000-7007fad3c000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                336 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fad3c000-7007fad3d000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fad3d000-7007fad6d000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                192 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fad6d000-7007fad6e000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fad6e000-7007fad8a000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                112 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fad8a000-7007fad8b000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fad8b000-7007fad8c000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fad8c000-7007fada8000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                112 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fada8000-7007fada9000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fada9000-7007fadac000 r--s 00000000 fe:00 276                        /system/fonts/NotoSansAvestan-Regular.ttf
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fadac000-7007fadc0000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fadc0000-7007fadc1000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fadc1000-7007fadd1000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fadd1000-7007fadd2000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fadd2000-7007fadd3000 r--s 00000000 fe:00 3210                       /system/framework/android.test.base.impl.jar
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fadd3000-7007fade7000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fade7000-7007fade8000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fade8000-7007fade9000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fade9000-7007fadea000 r--s 00000000 fe:00 3394                       /system/framework/framework-oahl-backward-compatibility.jar
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fadea000-7007fae22000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                224 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fae22000-7007fae23000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fae23000-7007fae24000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fae24000-7007fae3c000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 96 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fae3c000-7007fae3d000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fae3d000-7007fae3e000 r--s 00000000 fe:00 3144                       /system/framework/ims-common.jar
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fae3e000-7007fae3f000 r--s 00000000 fe:00 3155                       /system/framework/voip-common.jar
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fae3f000-7007fae43000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fae43000-7007fae44000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fae44000-7007fae45000 r--s 00000000 fe:00 3395                       /system/framework/telephony-common.jar
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fae45000-7007fae46000 r--s 00000000 fe:00 3158                       /system/framework/framework.jar
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fae46000-7007fae47000 r--s 00004000 fe:00 3208                       /system/framework/apache-xml.jar
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fae47000-7007fae4b000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fae4b000-7007fae4c000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fae4c000-7007fae7c000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                192 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fae7c000-7007faebc000 rw-p 00000000 00:00 0                          [anon:dalvik-mark stack]
+Name:           [anon:dalvik-mark stack]
+Size:                256 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faebc000-7007faebd000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007faebd000-7007faebe000 r--s 00000000 fe:00 3153                       /system/framework/bouncycastle.jar
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007faebe000-7007faeca000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faeca000-7007faecb000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faecb000-7007faecc000 r--s 00000000 fe:00 3151                       /system/framework/okhttp.jar
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007faecc000-7007faecd000 r--s 00000000 fe:00 3349                       /system/framework/conscrypt.jar
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007faecd000-7007faed5000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faed5000-7007faed6000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007faed6000-7007faeda000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faeda000-7007faefa000 rw-p 00000000 00:00 0                          [anon:dalvik-large marked objects]
+Name:           [anon:dalvik-large marked objects]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+7007faefa000-7007faf1a000 rw-p 00000000 00:00 0                          [anon:dalvik-large live objects]
+Name:           [anon:dalvik-large live objects]
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faf1a000-7007faf3a000 r--s 00000000 00:13 6709                       /dev/__properties__/u:object_r:fingerprint_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   1 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr me ms 
+7007faf3a000-7007faf5a000 r--s 00000000 00:13 6747                       /dev/__properties__/u:object_r:vold_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007faf5a000-7007faf5b000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007faf5b000-7007faf5c000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faf5c000-7007faf78000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                112 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faf78000-7007faf98000 r--s 00000000 00:13 6678                       /dev/__properties__/u:object_r:config_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007faf98000-7007faf99000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faf99000-7007faf9a000 r--s 00000000 fe:00 3157                       /system/framework/core-simple.jar
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007faf9a000-7007faf9c000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faf9c000-7007fafb0000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fafb0000-7007fafb1000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fafb1000-7007fafb3000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fafb3000-7007fafb7000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fafb7000-7007fafd7000 r--s 00000000 00:13 6696                       /dev/__properties__/u:object_r:dalvik_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fafd7000-7007fafd8000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fafd8000-7007fafd9000 r--s 00004000 fe:00 3162                       /system/framework/core-libart.jar
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fafd9000-7007fafdb000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fafdb000-7007fafe7000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fafe7000-7007fafe8000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fafe8000-7007faff8000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faff8000-7007faff9000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faff9000-7007faffa000 r--s 00007000 fe:00 3215                       /system/framework/core-oj.jar
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007faffa000-7007faffc000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007faffc000-7007fb014000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 96 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb014000-7007fb034000 r--s 00000000 00:13 6740                       /dev/__properties__/u:object_r:system_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fb034000-7007fb036000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+7007fb036000-7007fb042000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb042000-7007fb043000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb043000-7007fb044000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fb044000-7007fb045000 rw-p 00000000 00:00 0                          [anon:linker_alloc_small_objects]
+Name:           [anon:linker_alloc_small_objects]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fb045000-7007fb046000 rw-p 00000000 00:00 0                          [anon:dalvik-mod union bitmap]
+Name:           [anon:dalvik-mod union bitmap]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fb046000-7007fb05a000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 80 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb05a000-7007fb07a000 r--s 00000000 00:13 6717                       /dev/__properties__/u:object_r:log_tag_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fb07a000-7007fb07b000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb07b000-7007fb07c000 r--p 00000000 00:00 0                          [anon:atexit handlers]
+Name:           [anon:atexit handlers]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fb07c000-7007fb07e000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fb07e000-7007fb093000 r--p 00af1000 fe:00 3165                       /system/framework/x86_64/boot-framework.art
+Size:                 84 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  64 kB
+Pss:                   3 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                3 kB
+VmFlags: rd mr mw me 
+7007fb093000-7007fb094000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fb094000-7007fb095000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fb095000-7007fb0a5000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 64 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb0a5000-7007fb0c5000 r--s 00000000 00:13 6718                       /dev/__properties__/u:object_r:logd_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fb0c5000-7007fb0c6000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb0c6000-7007fb0c7000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fb0c7000-7007fb0c9000 rw-p 00000000 00:00 0                          [anon:dalvik-concurrent copying sweep array free buffer]
+Name:           [anon:dalvik-concurrent copying sweep array free buffer]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb0c9000-7007fb0d5000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 48 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb0d5000-7007fb0d6000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fb0d6000-7007fb0de000 rw-p 00000000 00:00 0                          [anon:dalvik-thread local mark stack]
+Name:           [anon:dalvik-thread local mark stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb0de000-7007fb0fe000 r--s 00000000 00:13 6712                       /dev/__properties__/u:object_r:heapprofd_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fb0fe000-7007fb11e000 r--s 00000000 00:13 6699                       /dev/__properties__/u:object_r:default_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  24 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         24 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           24 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fb11e000-7007fb11f000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb11f000-7007fb121000 rw-p 00000000 00:00 0                          [anon:dalvik-concurrent copying sweep array free buffer]
+Name:           [anon:dalvik-concurrent copying sweep array free buffer]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb121000-7007fb123000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+7007fb123000-7007fb124000 r--p 00006000 fe:00 3170                       /system/framework/x86_64/boot-android.test.base.impl.art
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007fb124000-7007fb125000 r--p 00002000 fe:00 3201                       /system/framework/x86_64/boot-framework-oahl-backward-compatibility.art
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007fb125000-7007fb126000 r--p 0001c000 fe:00 3176                       /system/framework/x86_64/boot-ims-common.art
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007fb126000-7007fb127000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fb127000-7007fb128000 r--p 00011000 fe:00 3198                       /system/framework/x86_64/boot-voip-common.art
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007fb128000-7007fb12b000 r--p 00116000 fe:00 3178                       /system/framework/x86_64/boot-telephony-common.art
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007fb12b000-7007fb12c000 r--p 0004d000 fe:00 3196                       /system/framework/x86_64/boot-ext.art
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007fb12c000-7007fb12e000 r--p 00067000 fe:00 3167                       /system/framework/x86_64/boot-apache-xml.art
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007fb12e000-7007fb12f000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fb12f000-7007fb130000 r--p 00068000 fe:00 3175                       /system/framework/x86_64/boot-bouncycastle.art
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007fb130000-7007fb131000 r--p 0003d000 fe:00 3166                       /system/framework/x86_64/boot-okhttp.art
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   2 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me 
+7007fb131000-7007fb132000 r--p 00040000 fe:00 3168                       /system/framework/x86_64/boot-conscrypt.art
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007fb132000-7007fb137000 r--p 002d1000 fe:00 3184                       /system/framework/x86_64/boot.art
+Size:                 20 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  20 kB
+Pss:                   1 kB
+Shared_Clean:         20 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           20 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me 
+7007fb137000-7007fb13e000 rw-p 00000000 fe:00 944                        /system/etc/event-log-tags
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb13e000-7007fb140000 rw-p 00000000 00:00 0                          [anon:dalvik-indirect ref table]
+Name:           [anon:dalvik-indirect ref table]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb140000-7007fb141000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007fb141000-7007fb144000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb144000-7007fb145000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007fb145000-7007fb165000 r--s 00000000 00:13 6697                       /dev/__properties__/u:object_r:debug_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fb165000-7007fb166000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb166000-7007fb167000 r--p 00001000 fe:00 3197                       /system/framework/x86_64/boot-core-simple.art
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007fb167000-7007fb16a000 r--p 00132000 fe:00 3174                       /system/framework/x86_64/boot-core-libart.art
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   0 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me 
+7007fb16a000-7007fb16b000 r--s 00000000 00:13 6990                       /dev/event-log-tags
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fb16b000-7007fb16c000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fb16c000-7007fb170000 r--p 00000000 00:00 0                          [anon:atexit handlers]
+Name:           [anon:atexit handlers]
+Size:                 16 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  16 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         16 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fb170000-7007fb190000 r--s 00000000 00:13 6750                       /dev/__properties__/properties_serial
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fb190000-7007fb191000 rw-p 00000000 00:00 0                          [anon:System property context nodes]
+Name:           [anon:System property context nodes]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fb191000-7007fb194000 r--s 00000000 00:13 6672                       /dev/__properties__/property_info
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         12 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fb194000-7007fb195000 rw-p 00000000 00:00 0                          [anon:linker_alloc_small_objects]
+Name:           [anon:linker_alloc_small_objects]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fb195000-7007fb196000 rw-p 00000000 00:00 0                          [anon:arc4random data]
+Name:           [anon:arc4random data]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb196000-7007fb197000 rw-p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb197000-7007fb198000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fb198000-7007fb1a5000 rw-p 00000000 00:00 0                          [anon:linker_alloc_small_objects]
+Name:           [anon:linker_alloc_small_objects]
+Size:                 52 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  52 kB
+Pss:                  36 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         16 kB
+Private_Clean:         0 kB
+Private_Dirty:        36 kB
+Referenced:           48 kB
+Anonymous:            52 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               36 kB
+VmFlags: rd wr mr mw me ac 
+7007fb1a5000-7007fb1c5000 r--s 00000000 00:13 6699                       /dev/__properties__/u:object_r:default_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fb1c5000-7007fb1c7000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd mr mw me ac 
+7007fb1c7000-7007fb1c8000 rw-p 00000000 00:00 0                          [anon:linker_alloc_small_objects]
+Name:           [anon:linker_alloc_small_objects]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fb1c8000-7007fb1e8000 r--s 00000000 00:13 6697                       /dev/__properties__/u:object_r:debug_prop:s0
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fb1e8000-7007fb1e9000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007fb1e9000-7007fb1ea000 rw-p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb1ea000-7007fb1eb000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007fb1eb000-7007fb20b000 r--s 00000000 00:13 6750                       /dev/__properties__/properties_serial
+Size:                128 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fb20b000-7007fb20c000 rw-p 00000000 00:00 0                          [anon:System property context nodes]
+Name:           [anon:System property context nodes]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb20c000-7007fb20f000 r--s 00000000 00:13 6672                       /dev/__properties__/property_info
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         12 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr me ms 
+7007fb20f000-7007fb210000 r--p 00000000 00:00 0                          [anon:linker_alloc]
+Name:           [anon:linker_alloc]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd mr mw me ac 
+7007fb210000-7007fb212000 rw-p 00000000 00:00 0                          [anon:linker_alloc_small_objects]
+Name:           [anon:linker_alloc_small_objects]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+7007fb212000-7007fb213000 r--p 00000000 00:00 0                          [anon:atexit handlers]
+Name:           [anon:atexit handlers]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fb213000-7007fb214000 ---p 00000000 00:00 0                          [anon:thread signal stack guard]
+Name:           [anon:thread signal stack guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me ac 
+7007fb214000-7007fb21c000 rw-p 00000000 00:00 0                          [anon:thread signal stack]
+Name:           [anon:thread signal stack]
+Size:                 32 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd wr mr mw me ac 
+7007fb21c000-7007fb21d000 rw-p 00000000 00:00 0                          [anon:arc4random data]
+Name:           [anon:arc4random data]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me ac 
+7007fb21d000-7007fb21e000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007fb21e000-7007fb221000 rw-p 00000000 00:00 0                          [anon:bionic TLS]
+Name:           [anon:bionic TLS]
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+7007fb221000-7007fb222000 ---p 00000000 00:00 0                          [anon:bionic TLS guard]
+Name:           [anon:bionic TLS guard]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me 
+7007fb222000-7007fb267000 r--p 00000000 fe:00 312                        /system/bin/linker64
+Size:                276 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 116 kB
+Pss:                   1 kB
+Shared_Clean:        116 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          116 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                1 kB
+VmFlags: rd mr mw me dw 
+7007fb267000-7007fb355000 r-xp 00045000 fe:00 312                        /system/bin/linker64
+Size:                952 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 496 kB
+Pss:                   7 kB
+Shared_Clean:        496 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          496 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                7 kB
+VmFlags: rd ex mr mw me dw 
+7007fb355000-7007fb356000 rw-p 00133000 fe:00 312                        /system/bin/linker64
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                4 kB
+VmFlags: rd wr mr mw me dw ac 
+7007fb356000-7007fb361000 r--p 00134000 fe:00 312                        /system/bin/linker64
+Size:                 44 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  44 kB
+Pss:                   2 kB
+Shared_Clean:          0 kB
+Shared_Dirty:         44 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:            44 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                2 kB
+VmFlags: rd mr mw me dw ac 
+7007fb361000-7007fb368000 rw-p 00000000 00:00 0 
+Size:                 28 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                8 kB
+VmFlags: rd wr mr mw me ac 
+7007fb368000-7007fb369000 r--p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          4 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr mw me ac 
+7007fb369000-7007fb36c000 rw-p 00000000 00:00 0 
+Size:                 12 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  12 kB
+Pss:                  12 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        12 kB
+Referenced:           12 kB
+Anonymous:            12 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               12 kB
+VmFlags: rd wr mr mw me ac 
+7ffde4f08000-7ffde4f09000 ---p 00000000 00:00 0 
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: mr mw me gd ac 
+7ffde4f09000-7ffde5708000 rw-p 00000000 00:00 0                          [stack]
+Size:               8188 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                  40 kB
+Pss:                  32 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          8 kB
+Private_Clean:         0 kB
+Private_Dirty:        32 kB
+Referenced:           36 kB
+Anonymous:            40 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:               32 kB
+VmFlags: rd wr mr mw me gd ac 
+7ffde5771000-7ffde5773000 r--p 00000000 00:00 0                          [vvar]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd mr pf io de dd 
+7ffde5773000-7ffde5775000 r-xp 00000000 00:00 0                          [vdso]
+Size:                  8 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex mr mw me de 
+ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex 
diff --git a/libmeminfo/testdata1/smaps_short b/libmeminfo/testdata1/smaps_short
new file mode 100644
index 0000000..cee67b3
--- /dev/null
+++ b/libmeminfo/testdata1/smaps_short
@@ -0,0 +1,122 @@
+54c00000-56c00000 r-xp 00000000 00:00 0                                  [anon:dalvik-zygote-jit-code-cache]
+Name:           [anon:dalvik-zygote-jit-code-cache]
+Size:              32768 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                2048 kB
+Pss:                 113 kB
+Shared_Clean:          0 kB
+Shared_Dirty:       2048 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:         2048 kB
+Anonymous:          2048 kB
+AnonHugePages:      2048 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              113 kB
+VmFlags: rd ex mr mw me ac 
+701ea000-70cdb000 rw-p 00000000 fe:00 3165                               /system/framework/x86_64/boot-framework.art
+Size:              11204 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:               11188 kB
+Pss:                2200 kB
+Shared_Clean:         80 kB
+Shared_Dirty:       9448 kB
+Private_Clean:         0 kB
+Private_Dirty:      1660 kB
+Referenced:         9892 kB
+Anonymous:         11108 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             2200 kB
+VmFlags: rd wr mr mw me ac 
+70074dd8d000-70074ee0d000 rw-p 00000000 00:00 0                          [anon:libc_malloc]
+Name:           [anon:libc_malloc]
+Size:              16896 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:               15272 kB
+Pss:               15272 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:     15272 kB
+Referenced:        11156 kB
+Anonymous:         15272 kB
+AnonHugePages:      6144 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:            15272 kB
+VmFlags: rd wr mr mw me ac 
+700755a2d000-700755a6e000 r-xp 00016000 fe:00 1947                       /system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex
+Size:                260 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                 260 kB
+Pss:                 260 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:       260 kB
+Private_Dirty:         0 kB
+Referenced:          260 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:              260 kB
+VmFlags: rd ex mr mw me 
+7007f85b0000-7007f8b9b000 r-xp 001ee000 fe:00 1537                       /system/lib64/libhwui.so
+Size:               6060 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                4132 kB
+Pss:                1274 kB
+Shared_Clean:       4132 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:         4132 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:             1274 kB
+VmFlags: rd ex mr mw me 
+ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
+Size:                  4 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                  0 kB
+SwapPss:               0 kB
+Locked:                0 kB
+VmFlags: rd ex 
diff --git a/libmeminfo/testdata1/vmallocinfo b/libmeminfo/testdata1/vmallocinfo
new file mode 100644
index 0000000..d48d8bf
--- /dev/null
+++ b/libmeminfo/testdata1/vmallocinfo
@@ -0,0 +1,1774 @@
+0x0000000000000000-0x0000000000000000   69632 of_iomap+0x78/0xb0 phys=17a00000 ioremap

+0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=b220000 ioremap

+0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=17c90000 ioremap

+0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=17ca0000 ioremap

+0x0000000000000000-0x0000000000000000  266240 atomic_pool_init+0x0/0x200 user

+0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=146bf000 ioremap

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=146bf000 ioremap

+0x0000000000000000-0x0000000000000000   28672 devm_ioremap_resource+0xd8/0x194 phys=8c0000 ioremap

+0x0000000000000000-0x0000000000000000   28672 devm_ioremap_resource+0xd8/0x194 phys=ac0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=146bf000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=c264000 ioremap

+0x0000000000000000-0x0000000000000000   12288 devm_ioremap_resource+0xd8/0x194 phys=c440000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=17980000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 glink_mailbox_probe+0x55c/0xac4 phys=1885000 ioremap

+0x0000000000000000-0x0000000000000000  159744 devm_ioremap_resource+0xd8/0x194 phys=c40a000 ioremap

+0x0000000000000000-0x0000000000000000   73728 msm_watchdog_probe+0x5b8/0xc24 user

+0x0000000000000000-0x0000000000000000   73728 msm_watchdog_probe+0x5b8/0xc24 user

+0x0000000000000000-0x0000000000000000   73728 msm_watchdog_probe+0x5b8/0xc24 user

+0x0000000000000000-0x0000000000000000   73728 msm_watchdog_probe+0x5b8/0xc24 user

+0x0000000000000000-0x0000000000000000   36864 remote_spinlock_init_address+0x1c8/0x224 phys=1f40000 ioremap

+0x0000000000000000-0x0000000000000000   16384 n_tty_open+0x1c/0xac pages=3 vmalloc

+0x0000000000000000-0x0000000000000000    8192 glink_mailbox_probe+0x634/0xac4 phys=1886000 ioremap

+0x0000000000000000-0x0000000000000000 1052672 of_iomap+0x78/0xb0 phys=17a60000 ioremap

+0x0000000000000000-0x0000000000000000   73728 msm_watchdog_probe+0x5b8/0xc24 user

+0x0000000000000000-0x0000000000000000   73728 msm_watchdog_probe+0x5b8/0xc24 user

+0x0000000000000000-0x0000000000000000   73728 msm_watchdog_probe+0x5b8/0xc24 user

+0x0000000000000000-0x0000000000000000   73728 msm_watchdog_probe+0x5b8/0xc24 user

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_nocache+0x84/0xd8 phys=17990000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=179e0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=af20000 ioremap

+0x0000000000000000-0x0000000000000000   32768 msm_smem_probe+0x56c/0xdf0 phys=778000 ioremap

+0x0000000000000000-0x0000000000000000   20480 devm_ioremap_resource+0xd8/0x194 phys=179e0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 glink_smem_native_probe+0x42c/0x934 phys=17990000 ioremap

+0x0000000000000000-0x0000000000000000   20480 devm_ioremap_resource+0xd8/0x194 phys=af21000 ioremap

+0x0000000000000000-0x0000000000000000    8192 glink_smem_native_probe+0x42c/0x934 phys=17990000 ioremap

+0x0000000000000000-0x0000000000000000    8192 glink_smem_native_probe+0x42c/0x934 phys=17990000 ioremap

+0x0000000000000000-0x0000000000000000    8192 glink_smem_native_probe+0x42c/0x934 phys=17990000 ioremap

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=85fe0000 ioremap

+0x0000000000000000-0x0000000000000000   40960 mem_dump_probe+0x1d4/0x464 user

+0x0000000000000000-0x0000000000000000  167936 mem_dump_probe+0x1d4/0x464 user

+0x0000000000000000-0x0000000000000000   69632 mem_dump_probe+0x1d4/0x464 user

+0x0000000000000000-0x0000000000000000   69632 mem_dump_probe+0x1d4/0x464 user

+0x0000000000000000-0x0000000000000000   40960 mem_dump_probe+0x1d4/0x464 user

+0x0000000000000000-0x0000000000000000    8192 mem_dump_probe+0x1d4/0x464 user

+0x0000000000000000-0x0000000000000000    8192 mem_dump_probe+0x1d4/0x464 user

+0x0000000000000000-0x0000000000000000    8192 mem_dump_probe+0x1d4/0x464 user

+0x0000000000000000-0x0000000000000000    8192 mem_dump_probe+0x1d4/0x464 user

+0x0000000000000000-0x0000000000000000    8192 mem_dump_probe+0x1d4/0x464 user

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=ff1000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=16b000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=18d000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=175000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=177000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=10f000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=110000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=17d000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=17d000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=17d000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=17d000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=17d000 ioremap

+0x0000000000000000-0x0000000000000000 1052672 devm_ioremap_resource+0xd8/0x194 phys=e600000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=17d000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=17d000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=ad06000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=ad09000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=ad0a000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=ad07000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=ad08000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=ad0b000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=af03000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=5091000 ioremap

+0x0000000000000000-0x0000000000000000    8192 syscon_node_to_regmap+0x198/0x2dc phys=5091000 ioremap

+0x0000000000000000-0x0000000000000000    8192 syscon_node_to_regmap+0x198/0x2dc phys=5091000 ioremap

+0x0000000000000000-0x0000000000000000    8192 syscon_node_to_regmap+0x198/0x2dc phys=5091000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=ab00000 ioremap

+0x0000000000000000-0x0000000000000000  135168 _persistent_ram_buffer_map+0x1ec/0x238 phys=a1810000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=ab00000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=ab00000 ioremap

+0x0000000000000000-0x0000000000000000   12288 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=150c2000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=150c5000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=150c2000 ioremap

+0x0000000000000000-0x0000000000000000   40960 devm_ioremap+0x84/0xd8 phys=5090000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=150c9000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=150c2000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=150cd000 ioremap

+0x0000000000000000-0x0000000000000000  135168 _persistent_ram_buffer_map+0x1ec/0x238 phys=a1830000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=150c2000 ioremap

+0x0000000000000000-0x0000000000000000   20480 msm_bus_noc_qos_init+0x94/0x714 phys=10b8000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=150d1000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=150c2000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=150d5000 ioremap

+0x0000000000000000-0x0000000000000000   40960 devm_ioremap_resource+0xd8/0x194 phys=5090000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=150c2000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=150d9000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=150c2000 ioremap

+0x0000000000000000-0x0000000000000000  135168 _persistent_ram_buffer_map+0x1ec/0x238 phys=a1850000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=150dd000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=150c2000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=150e1000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=150c2000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=c222000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=c263000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=c223000 ioremap

+0x0000000000000000-0x0000000000000000   69632 devm_ioremap_resource+0xd8/0x194 phys=ad00000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=c265000 ioremap

+0x0000000000000000-0x0000000000000000   12288 devm_ioremap+0x84/0xd8 phys=1c08000 ioremap

+0x0000000000000000-0x0000000000000000   12288 devm_ioremap+0x84/0xd8 phys=1c0a000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=40000000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=40000000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=5091000 ioremap

+0x0000000000000000-0x0000000000000000   12288 devm_ioremap+0x84/0xd8 phys=17d41000 ioremap

+0x0000000000000000-0x0000000000000000   12288 devm_ioremap+0x84/0xd8 phys=17d43000 ioremap

+0x0000000000000000-0x0000000000000000   12288 devm_ioremap+0x84/0xd8 phys=17d45000 ioremap

+0x0000000000000000-0x0000000000000000    8192 syscon_node_to_regmap+0x198/0x2dc phys=17970000 ioremap

+0x0000000000000000-0x0000000000000000  659456 devm_ioremap_resource+0xd8/0x194 phys=e700000 ioremap

+0x0000000000000000-0x0000000000000000    8192 mdss_pll_probe+0x208/0x77c phys=ae94000 ioremap

+0x0000000000000000-0x0000000000000000    8192 mdss_pll_probe+0x360/0x77c phys=ae94000 ioremap

+0x0000000000000000-0x0000000000000000    8192 mdss_pll_probe+0x4f0/0x77c phys=af03000 ioremap

+0x0000000000000000-0x0000000000000000    8192 mdss_pll_probe+0x208/0x77c phys=ae96000 ioremap

+0x0000000000000000-0x0000000000000000    8192 mdss_pll_probe+0x360/0x77c phys=ae96000 ioremap

+0x0000000000000000-0x0000000000000000    8192 mdss_pll_probe+0x4f0/0x77c phys=af03000 ioremap

+0x0000000000000000-0x0000000000000000    8192 mdss_pll_probe+0x208/0x77c phys=88ea000 ioremap

+0x0000000000000000-0x0000000000000000   40960 syscon_node_to_regmap+0x198/0x2dc phys=5090000 ioremap

+0x0000000000000000-0x0000000000000000    8192 mdss_pll_probe+0x360/0x77c phys=88ea000 ioremap

+0x0000000000000000-0x0000000000000000    8192 mdss_pll_probe+0x428/0x77c phys=88ea000 ioremap

+0x0000000000000000-0x0000000000000000    8192 mdss_pll_probe+0x48c/0x77c phys=88ea000 ioremap

+0x0000000000000000-0x0000000000000000  135168 _persistent_ram_buffer_map+0x1ec/0x238 phys=a1870000 ioremap

+0x0000000000000000-0x0000000000000000    8192 mdss_pll_probe+0x4f0/0x77c phys=af03000 ioremap

+0x0000000000000000-0x0000000000000000    8192 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=1d87000 ioremap

+0x0000000000000000-0x0000000000000000   12288 pcpu_alloc+0x3e8/0x9e4 pages=2 vmalloc

+0x0000000000000000-0x0000000000000000   12288 devm_ioremap_resource+0xd8/0x194 phys=88e0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 msm_smp2p_probe+0xb0/0x3b0 phys=17990000 ioremap

+0x0000000000000000-0x0000000000000000    8192 msm_smp2p_probe+0xb0/0x3b0 phys=17990000 ioremap

+0x0000000000000000-0x0000000000000000    8192 msm_smp2p_probe+0xb0/0x3b0 phys=17990000 ioremap

+0x0000000000000000-0x0000000000000000    8192 msm_smp2p_probe+0xb0/0x3b0 phys=17990000 ioremap

+0x0000000000000000-0x0000000000000000    8192 msm_pil_init+0xe4/0x250 phys=146bf000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=1881000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=1881000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=1881000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=1881000 ioremap

+0x0000000000000000-0x0000000000000000 2101248 msm_smem_probe+0x23c/0xdf0 phys=86000000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=1882000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   69632 devm_ioremap_resource+0xd8/0x194 phys=af00000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=b221000 ioremap

+0x0000000000000000-0x0000000000000000    8192 qsee_ipc_irq_bridge_probe+0x414/0xb0c phys=1888000 ioremap

+0x0000000000000000-0x0000000000000000    8192 msm_rng_probe+0x90/0x4a8 phys=793000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_nocache+0x84/0xd8 phys=ae94000 ioremap

+0x0000000000000000-0x0000000000000000   69632 devm_ioremap_resource+0xd8/0x194 phys=ab00000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_nocache+0x84/0xd8 phys=ae96000 ioremap

+0x0000000000000000-0x0000000000000000   20480 devm_ioremap+0x84/0xd8 phys=898000 ioremap

+0x0000000000000000-0x0000000000000000   24576 diag_dci_init+0x25c/0x35c pages=5 vmalloc

+0x0000000000000000-0x0000000000000000   69632 devm_ioremap_resource+0xd8/0x194 phys=5040000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_nocache+0x84/0xd8 phys=ae94000 ioremap

+0x0000000000000000-0x0000000000000000   20480 devm_ioremap+0x84/0xd8 phys=a84000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_nocache+0x84/0xd8 phys=af08000 ioremap

+0x0000000000000000-0x0000000000000000   20480 msm_dss_ioremap_byname+0x84/0x134 phys=af30000 ioremap

+0x0000000000000000-0x0000000000000000   12288 msm_dss_ioremap_byname+0x84/0x134 phys=af20000 ioremap

+0x0000000000000000-0x0000000000000000   20480 alloc_and_map+0xb0/0x1cc user

+0x0000000000000000-0x0000000000000000   20480 alloc_and_map+0xb0/0x1cc user

+0x0000000000000000-0x0000000000000000  266240 devm_ioremap+0x84/0xd8 phys=16e0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 kgsl_sharedmem_alloc_contig+0xc4/0x1f8 user

+0x0000000000000000-0x0000000000000000    8192 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000    8192 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000    8192 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000   16384 devm_ioremap_resource+0xd8/0x194 phys=1d84000 ioremap

+0x0000000000000000-0x0000000000000000   69632 syscon_node_to_regmap+0x198/0x2dc phys=af00000 ioremap

+0x0000000000000000-0x0000000000000000    8192 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000   12288 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000   16384 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000   20480 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000    8192 dmam_alloc_coherent+0xb0/0x11c user

+0x0000000000000000-0x0000000000000000    8192 dmam_alloc_coherent+0xb0/0x11c user

+0x0000000000000000-0x0000000000000000 1052672 devm_ioremap_nocache+0x84/0xd8 phys=c300000 ioremap

+0x0000000000000000-0x0000000000000000   16384 n_tty_open+0x1c/0xac pages=3 vmalloc

+0x0000000000000000-0x0000000000000000    8192 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=5061000 ioremap

+0x0000000000000000-0x0000000000000000    8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000    8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000   69632 syscon_node_to_regmap+0x198/0x2dc phys=ab00000 ioremap

+0x0000000000000000-0x0000000000000000   65536 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000    8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000    8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000  266240 devm_ioremap+0x84/0xd8 phys=1700000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 msm_slim_sps_mem_alloc+0xb8/0x130 user

+0x0000000000000000-0x0000000000000000    8192 msm_slim_sps_mem_alloc+0xb8/0x130 user

+0x0000000000000000-0x0000000000000000    8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000   16384 devm_ioremap_nocache+0x84/0xd8 phys=88e8000 ioremap

+0x0000000000000000-0x0000000000000000   69632 syscon_node_to_regmap+0x198/0x2dc phys=ad00000 ioremap

+0x0000000000000000-0x0000000000000000    8192 msm_slim_sps_mem_alloc+0xb8/0x130 user

+0x0000000000000000-0x0000000000000000    8192 msm_slim_sps_mem_alloc+0xb8/0x130 user

+0x0000000000000000-0x0000000000000000   65536 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000   20480 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000    8192 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=88e2000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_nocache+0x84/0xd8 phys=780000 ioremap

+0x0000000000000000-0x0000000000000000  217088 ipa3_pre_init+0x984/0x20c4 phys=1e40000 ioremap

+0x0000000000000000-0x0000000000000000   20480 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000    8192 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=88e7000 ioremap

+0x0000000000000000-0x0000000000000000 1052672 _persistent_ram_buffer_map+0x1ec/0x238 phys=a1890000 ioremap

+0x0000000000000000-0x0000000000000000  135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc

+0x0000000000000000-0x0000000000000000  135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc

+0x0000000000000000-0x0000000000000000  135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000    8192 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000    8192 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000    8192 msm_slim_sps_mem_alloc+0xb8/0x130 user

+0x0000000000000000-0x0000000000000000    8192 msm_slim_sps_mem_alloc+0xb8/0x130 user

+0x0000000000000000-0x0000000000000000    8192 msm_slim_sps_mem_alloc+0xb8/0x130 user

+0x0000000000000000-0x0000000000000000    8192 msm_slim_sps_mem_alloc+0xb8/0x130 user

+0x0000000000000000-0x0000000000000000    8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000  528384 _persistent_ram_buffer_map+0x1ec/0x238 phys=a1990000 ioremap

+0x0000000000000000-0x0000000000000000  135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc

+0x0000000000000000-0x0000000000000000   40960 qce_sps_init_ep_conn+0x168/0x348 user

+0x0000000000000000-0x0000000000000000   77824 sps_bam_pipe_set_params+0x1e4/0x4c8 pages=18 vmalloc

+0x0000000000000000-0x0000000000000000  266240 devm_ioremap+0x84/0xd8 phys=1620000 ioremap

+0x0000000000000000-0x0000000000000000  135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc

+0x0000000000000000-0x0000000000000000  135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc

+0x0000000000000000-0x0000000000000000  135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc

+0x0000000000000000-0x0000000000000000   40960 qce_sps_init_ep_conn+0x168/0x348 user

+0x0000000000000000-0x0000000000000000   40960 qce_sps_init_ep_conn+0x168/0x348 user

+0x0000000000000000-0x0000000000000000   20480 devm_ioremap+0x84/0xd8 phys=880000 ioremap

+0x0000000000000000-0x0000000000000000 2035712 devm_ioremap_resource+0xd8/0x194 phys=100000 ioremap

+0x0000000000000000-0x0000000000000000   40960 qce_sps_init_ep_conn+0x168/0x348 user

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_nocache+0x84/0xd8 phys=a6f8000 ioremap

+0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=146bf000 ioremap

+0x0000000000000000-0x0000000000000000 12587008 devm_ioremap_resource+0xd8/0x194 phys=3400000 ioremap

+0x0000000000000000-0x0000000000000000  135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc

+0x0000000000000000-0x0000000000000000   77824 sps_bam_pipe_set_params+0x1e4/0x4c8 pages=18 vmalloc

+0x0000000000000000-0x0000000000000000   36864 kgsl_sharedmem_alloc_contig+0xc4/0x1f8 user

+0x0000000000000000-0x0000000000000000    8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac40000 ioremap

+0x0000000000000000-0x0000000000000000  266240 devm_ioremap+0x84/0xd8 phys=1500000 ioremap

+0x0000000000000000-0x0000000000000000  135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc

+0x0000000000000000-0x0000000000000000   77824 sps_bam_pipe_set_params+0x1e4/0x4c8 pages=18 vmalloc

+0x0000000000000000-0x0000000000000000    8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac48000 ioremap

+0x0000000000000000-0x0000000000000000   20480 devm_ioremap+0x84/0xd8 phys=890000 ioremap

+0x0000000000000000-0x0000000000000000    8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=acb3000 ioremap

+0x0000000000000000-0x0000000000000000  266240 devm_ioremap+0x84/0xd8 phys=14e0000 ioremap

+0x0000000000000000-0x0000000000000000  135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc

+0x0000000000000000-0x0000000000000000   77824 sps_bam_pipe_set_params+0x1e4/0x4c8 pages=18 vmalloc

+0x0000000000000000-0x0000000000000000    8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=acba000 ioremap

+0x0000000000000000-0x0000000000000000   20480 devm_ioremap+0x84/0xd8 phys=89c000 ioremap

+0x0000000000000000-0x0000000000000000    8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=acc8000 ioremap

+0x0000000000000000-0x0000000000000000  266240 devm_ioremap+0x84/0xd8 phys=17900000 ioremap

+0x0000000000000000-0x0000000000000000  135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc

+0x0000000000000000-0x0000000000000000    8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac65000 ioremap

+0x0000000000000000-0x0000000000000000    8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac66000 ioremap

+0x0000000000000000-0x0000000000000000    8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac67000 ioremap

+0x0000000000000000-0x0000000000000000   36864 devm_ioremap_resource+0xd8/0x194 phys=1d90000 ioremap

+0x0000000000000000-0x0000000000000000    8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac68000 ioremap

+0x0000000000000000-0x0000000000000000   20480 devm_ioremap+0x84/0xd8 phys=a8c000 ioremap

+0x0000000000000000-0x0000000000000000    8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac5a000 ioremap

+0x0000000000000000-0x0000000000000000   16384 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac18000 ioremap

+0x0000000000000000-0x0000000000000000  266240 devm_ioremap+0x84/0xd8 phys=1620000 ioremap

+0x0000000000000000-0x0000000000000000  135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc

+0x0000000000000000-0x0000000000000000  102400 dmam_alloc_coherent+0xb0/0x11c user

+0x0000000000000000-0x0000000000000000   16384 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac87000 ioremap

+0x0000000000000000-0x0000000000000000  266240 devm_ioremap+0x84/0xd8 phys=1380000 ioremap

+0x0000000000000000-0x0000000000000000  135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc

+0x0000000000000000-0x0000000000000000    8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac5b000 ioremap

+0x0000000000000000-0x0000000000000000   16384 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac91000 ioremap

+0x0000000000000000-0x0000000000000000   36864 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000    8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac6b000 ioremap

+0x0000000000000000-0x0000000000000000   20480 devm_ioremap_resource+0xd8/0x194 phys=888000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=17d78000 ioremap

+0x0000000000000000-0x0000000000000000   16384 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac6f000 ioremap

+0x0000000000000000-0x0000000000000000  266240 devm_ioremap+0x84/0xd8 phys=1380000 ioremap

+0x0000000000000000-0x0000000000000000  135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=17d43000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=17d78000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=17d70000 ioremap

+0x0000000000000000-0x0000000000000000   36864 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=17d45000 ioremap

+0x0000000000000000-0x0000000000000000   20480 devm_ioremap_resource+0xd8/0x194 phys=88c000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=17d70000 ioremap

+0x0000000000000000-0x0000000000000000   12288 devm_ioremap_nocache+0x84/0xd8 phys=858b2000 ioremap

+0x0000000000000000-0x0000000000000000  266240 devm_ioremap+0x84/0xd8 phys=1740000 ioremap

+0x0000000000000000-0x0000000000000000  135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc

+0x0000000000000000-0x0000000000000000  135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc

+0x0000000000000000-0x0000000000000000   20480 devm_ioremap_resource+0xd8/0x194 phys=894000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=1436000 ioremap

+0x0000000000000000-0x0000000000000000   16384 devm_ioremap_nocache+0x84/0xd8 phys=14693000 ioremap

+0x0000000000000000-0x0000000000000000   69632 kgsl_iommu_init+0x154/0x5a0 phys=5040000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=1436000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=114a000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_nocache+0x84/0xd8 phys=170f7000 ioremap

+0x0000000000000000-0x0000000000000000   36864 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_nocache+0x84/0xd8 phys=170f7000 ioremap

+0x0000000000000000-0x0000000000000000    8192 ebt_register_table+0xc4/0x3d4 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 ebt_register_table+0xd4/0x3d4 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000   24576 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac42000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=4080000 ioremap

+0x0000000000000000-0x0000000000000000 33558528 devm_ioremap_resource+0xd8/0x194 phys=c600000 ioremap

+0x0000000000000000-0x0000000000000000 33558528 mem_dump_probe+0x1d4/0x464 user

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=1f63000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=1f65000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=1f64000 ioremap

+0x0000000000000000-0x0000000000000000   36864 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=4180000 ioremap

+0x0000000000000000-0x0000000000000000   20480 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=acaf000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=c2b0000 ioremap

+0x0000000000000000-0x0000000000000000   20480 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=acb6000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=b2e0000 ioremap

+0x0000000000000000-0x0000000000000000   20480 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=acc4000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap+0x84/0xd8 phys=4180000 ioremap

+0x0000000000000000-0x0000000000000000   20480 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac4a000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000   28672 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac00000 ioremap

+0x0000000000000000-0x0000000000000000  266240 devm_ioremap+0x84/0xd8 phys=1620000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000   36864 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac10000 ioremap

+0x0000000000000000-0x0000000000000000    8192 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000   20480 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac4e000 ioremap

+0x0000000000000000-0x0000000000000000   20480 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac52000 ioremap

+0x0000000000000000-0x0000000000000000   20480 devm_ioremap_resource+0xd8/0x194 phys=a88000 ioremap

+0x0000000000000000-0x0000000000000000    8192 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000   20480 devm_ioremap_resource+0xd8/0x194 phys=a90000 ioremap

+0x0000000000000000-0x0000000000000000   20480 devm_ioremap_nocache+0x84/0xd8 phys=146bf000 ioremap

+0x0000000000000000-0x0000000000000000    8192 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000    8192 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000  266240 devm_ioremap+0x84/0xd8 phys=1380000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000    8192 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   16384 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000    8192 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_nocache+0x84/0xd8 phys=88ee000 ioremap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_resource+0xd8/0x194 phys=a60c000 ioremap

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init+0x15c/0x5c0 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init+0x1d0/0x5c0 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init+0x2a0/0x5c0 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000  266240 devm_ioremap+0x84/0xd8 phys=1380000 ioremap

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000    8192 __ipa_commit_hdr_v3_0+0x19c/0xefc user

+0x0000000000000000-0x0000000000000000   20480 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000    8192 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000  266240 devm_ioremap+0x84/0xd8 phys=1740000 ioremap

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user

+0x0000000000000000-0x0000000000000000    8192 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   98304 sps_register_bam_device+0x884/0xd9c phys=a704000 ioremap

+0x0000000000000000-0x0000000000000000    8192 dwc3_core_pre_init+0x438/0x480 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_msm_notify_event+0x704/0xee0 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_msm_notify_event+0x704/0xee0 user

+0x0000000000000000-0x0000000000000000    8192 dwc3_msm_notify_event+0x704/0xee0 user

+0x0000000000000000-0x0000000000000000  200704 devm_ioremap+0x84/0xd8 phys=506a000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000    8192 devm_ioremap_nocache+0x84/0xd8 phys=aeac000 ioremap

+0x0000000000000000-0x0000000000000000   16384 devm_ioremap_nocache+0x84/0xd8 phys=aeb0000 ioremap

+0x0000000000000000-0x0000000000000000  528384 devm_ioremap_resource+0xd8/0x194 phys=15000000 ioremap

+0x0000000000000000-0x0000000000000000   24576 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   20480 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000    8192 gpi_alloc_ring+0x238/0x2e0 user

+0x0000000000000000-0x0000000000000000    8192 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000    8192 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000  135168 qce_open+0x360/0x1760 phys=1de0000 ioremap

+0x0000000000000000-0x0000000000000000   36864 drm_ht_create+0x50/0x84 pages=8 vmalloc

+0x0000000000000000-0x0000000000000000    8192 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000    8192 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000    8192 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000    8192 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000    8192 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000   12288 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000   45056 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000    8192 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000    8192 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000   12288 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000   45056 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000    8192 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000    8192 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000   12288 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000    8192 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000    8192 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000  397312 devm_ioremap_nocache+0x84/0xd8 phys=800000 ioremap

+0x0000000000000000-0x0000000000000000   45056 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000   12288 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000   45056 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000    8192 gpi_alloc_ring+0x238/0x2e0 user

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000   16384 n_tty_open+0x1c/0xac pages=3 vmalloc

+0x0000000000000000-0x0000000000000000  139264 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000  106496 mnh_alloc_coherent+0x94/0xd0 user

+0x0000000000000000-0x0000000000000000    8192 firmware_data_write+0xe8/0x228 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 firmware_loading_store+0x1b8/0x244

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 firmware_data_write+0xe8/0x228 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 firmware_loading_store+0x1b8/0x244

+0x0000000000000000-0x0000000000000000   12288 gpi_alloc_ring+0x238/0x2e0 user

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   16384 n_tty_open+0x1c/0xac pages=3 vmalloc

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   16384 n_tty_open+0x1c/0xac pages=3 vmalloc

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   16384 n_tty_open+0x1c/0xac pages=3 vmalloc

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   16384 n_tty_open+0x1c/0xac pages=3 vmalloc

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 SyS_swapon+0x720/0xd20 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 swap_cgroup_swapon+0x38/0x140 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000  528384 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000   12288 disksize_store+0x9c/0x154 pages=2 vmalloc

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   24576 lz4_init+0x1c/0x48 pages=5 vmalloc

+0x0000000000000000-0x0000000000000000   24576 lz4_init+0x1c/0x48 pages=5 vmalloc

+0x0000000000000000-0x0000000000000000   24576 lz4_init+0x1c/0x48 pages=5 vmalloc

+0x0000000000000000-0x0000000000000000   24576 lz4_init+0x1c/0x48 pages=5 vmalloc

+0x0000000000000000-0x0000000000000000   24576 lz4_init+0x1c/0x48 pages=5 vmalloc

+0x0000000000000000-0x0000000000000000   24576 lz4_init+0x1c/0x48 pages=5 vmalloc

+0x0000000000000000-0x0000000000000000   24576 lz4_init+0x1c/0x48 pages=5 vmalloc

+0x0000000000000000-0x0000000000000000   24576 lz4_init+0x1c/0x48 pages=5 vmalloc

+0x0000000000000000-0x0000000000000000    8192 ipa3_uc_event_handler+0x378/0x6c8 phys=1e47000 ioremap

+0x0000000000000000-0x0000000000000000    8192 ipa3_uc_wdi_event_log_info_handler+0x1bc/0x350 phys=1e47000 ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   16384 n_tty_open+0x1c/0xac pages=3 vmalloc

+0x0000000000000000-0x0000000000000000   16384 n_tty_open+0x1c/0xac pages=3 vmalloc

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000  110592 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 ipahal_fltrt_allocate_hw_sys_tbl+0x1cc/0x34c user

+0x0000000000000000-0x0000000000000000    8192 ipahal_fltrt_allocate_hw_sys_tbl+0x1cc/0x34c user

+0x0000000000000000-0x0000000000000000    8192 ipahal_fltrt_allocate_hw_sys_tbl+0x1cc/0x34c user

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 ipahal_fltrt_allocate_hw_sys_tbl+0x1cc/0x34c user

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000   16384 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000   40960 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000   40960 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000   16384 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000   16384 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   16384 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000  249856 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000  266240 SyS_swapon+0x650/0xd20 pages=64 vmalloc

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000   20480 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000  139264 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000   20480 pcpu_alloc+0x3e8/0x9e4 pages=4 vmalloc

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000 1052672 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000 20975616 memremap+0x188/0x200 phys=8ab00000 ioremap

+0x0000000000000000-0x0000000000000000  372736 qce_open+0x68c/0x1760 user

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000  397312 devm_ioremap_nocache+0x84/0xd8 phys=a00000 ioremap

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000   12288 lpm_probe+0x194/0x2e4 user

+0x0000000000000000-0x0000000000000000 1052672 devm_ioremap+0x84/0xd8 phys=40100000 ioremap

+0x0000000000000000-0x0000000000000000 1052672 msm_rtb_probe+0x114/0x268 user

+0x0000000000000000-0x0000000000000000    8192 pci_ioremap_bar+0x80/0xb4 phys=41c00000 ioremap

+0x0000000000000000-0x0000000000000000   16384 sde_rot_ioremap_byname+0x84/0x144 phys=aeb8000 ioremap

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 dm_create+0x78/0x490 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 dm_table_create+0x84/0xf0 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 dm_create+0x78/0x490 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 dm_table_create+0x84/0xf0 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 dm_create+0x78/0x490 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 dm_table_create+0x84/0xf0 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 dm_create+0x78/0x490 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 dm_table_create+0x84/0xf0 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000   61440 verity_ctr+0x6c4/0x8c4 pages=14 vmalloc

+0x0000000000000000-0x0000000000000000    8192 alloc_buffer+0x16c/0x208 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 alloc_buffer+0x16c/0x208 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000  266240 devm_ioremap+0x84/0xd8 phys=5000000 ioremap

+0x0000000000000000-0x0000000000000000    8192 alloc_buffer+0x16c/0x208 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 dm_create+0x78/0x490 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 dm_table_create+0x84/0xf0 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000   20480 verity_ctr+0x6c4/0x8c4 pages=4 vmalloc

+0x0000000000000000-0x0000000000000000    8192 alloc_buffer+0x16c/0x208 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 alloc_buffer+0x16c/0x208 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 alloc_buffer+0x16c/0x208 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 dm_create+0x78/0x490 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 dm_table_create+0x84/0xf0 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000   32768 verity_ctr+0x6c4/0x8c4 pages=7 vmalloc

+0x0000000000000000-0x0000000000000000    8192 alloc_buffer+0x16c/0x208 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 alloc_buffer+0x16c/0x208 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 alloc_buffer+0x16c/0x208 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000  151552 qce_open+0xc08/0x1760 phys=1dc4000 ioremap

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000   20480 load_module+0x24ac/0x3408 pages=4 vmalloc

+0x0000000000000000-0x0000000000000000    8192 gpi_alloc_ring+0x238/0x2e0 user

+0x0000000000000000-0x0000000000000000    8192 gpi_alloc_ring+0x238/0x2e0 user

+0x0000000000000000-0x0000000000000000   12288 gpi_alloc_ring+0x238/0x2e0 user

+0x0000000000000000-0x0000000000000000    8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 dm_create+0x78/0x490 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000 4198400 disksize_store+0x9c/0x154 pages=1024 vmalloc vpages

+0x0000000000000000-0x0000000000000000 1052672 devm_ioremap+0x84/0xd8 phys=40200000 ioremap

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 dm_table_create+0x84/0xf0 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000  135168 qce_open+0x360/0x1760 phys=1de0000 ioremap

+0x0000000000000000-0x0000000000000000  372736 qce_open+0x68c/0x1760 user

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000  200704 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 kgsl_sharedmem_alloc_contig+0xc4/0x1f8 user

+0x0000000000000000-0x0000000000000000 2035712 syscon_node_to_regmap+0x198/0x2dc phys=100000 ioremap

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000   16384 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000 2428928 syscon_node_to_regmap+0x198/0x2dc phys=1100000 ioremap

+0x0000000000000000-0x0000000000000000 1052672 dmam_alloc_coherent+0xb0/0x11c user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000  266240 usbpd_create+0x3c/0x824 pages=64 vmalloc

+0x0000000000000000-0x0000000000000000    8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000   36864 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000   36864 alloc_and_map+0xb0/0x1cc user

+0x0000000000000000-0x0000000000000000   12288 kgsl_sharedmem_page_alloc_user+0x114/0x4ec pages=2 vmalloc

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000   12288 kgsl_sharedmem_page_alloc_user+0x114/0x4ec pages=2 vmalloc

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000   12288 kgsl_sharedmem_page_alloc_user+0x114/0x4ec pages=2 vmalloc

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000 3149824 devm_ioremap+0x84/0xd8 phys=b200000 ioremap

+0x0000000000000000-0x0000000000000000    8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000   12288 kgsl_sharedmem_page_alloc_user+0x114/0x4ec pages=2 vmalloc

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000    8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000   36864 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000   20480 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user

+0x0000000000000000-0x0000000000000000    8192 ipa3_setup_sys_pipe+0x131c/0x2794 user

+0x0000000000000000-0x0000000000000000    8192 ipa3_setup_sys_pipe+0x14cc/0x2794 user

+0x0000000000000000-0x0000000000000000    8192 ipa3_setup_sys_pipe+0x131c/0x2794 user

+0x0000000000000000-0x0000000000000000    8192 ipa3_setup_sys_pipe+0x14cc/0x2794 user

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000  184320 ngd_slim_probe+0x334/0x9b0 phys=171c0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000  176128 ngd_slim_probe+0x378/0x9b0 phys=17184000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   16384 n_tty_open+0x1c/0xac pages=3 vmalloc

+0x0000000000000000-0x0000000000000000  184320 ngd_slim_probe+0x334/0x9b0 phys=17240000 ioremap

+0x0000000000000000-0x0000000000000000    8192 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000    8192 ipahal_fltrt_allocate_hw_sys_tbl+0x1cc/0x34c user

+0x0000000000000000-0x0000000000000000   16384 n_tty_open+0x1c/0xac pages=3 vmalloc

+0x0000000000000000-0x0000000000000000    8192 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000   12288 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000 8392704 devm_ioremap+0x84/0xd8 phys=18800000 ioremap

+0x0000000000000000-0x0000000000000000 2109440 msm_sharedmem_probe+0x1dc/0x354 user

+0x0000000000000000-0x0000000000000000   16384 fastrpc_internal_invoke+0xab8/0x1c50 user

+0x0000000000000000-0x0000000000000000    8192 ipahal_fltrt_allocate_hw_sys_tbl+0x1cc/0x34c user

+0x0000000000000000-0x0000000000000000   28672 pktlog_alloc_buf+0xc4/0x15c [wlan] pages=6 vmalloc

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000    8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc

+0x0000000000000000-0x0000000000000000  135168 ngd_slim_probe+0x378/0x9b0 phys=17204000 ioremap

+0x0000000000000000-0x0000000000000000   12288 pcpu_alloc+0x3e8/0x9e4 pages=2 vmalloc

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap

+0x0000000000000000-0x0000000000000000   12288 drm_property_create_blob+0x44/0xec pages=2 vmalloc

+0x0000000000000000-0x0000000000000000    8192 msm_gem_get_vaddr+0xb0/0xf0 vmap

+0x0000000000000000-0x0000000000000000    8192 wlan_logging_sock_init_svc+0xf8/0x4f0 [wlan] pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 gpi_alloc_ring+0x238/0x2e0 user

+0x0000000000000000-0x0000000000000000    8192 gpi_alloc_ring+0x238/0x2e0 user

+0x0000000000000000-0x0000000000000000 1052672 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000 2101248 devm_ioremap_nocache+0x84/0xd8 phys=aa00000 ioremap

+0x0000000000000000-0x0000000000000000   45056 drm_property_create_blob+0x44/0xec pages=10 vmalloc

+0x0000000000000000-0x0000000000000000  135168 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000  135168 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000   12288 gpi_alloc_ring+0x238/0x2e0 user

+0x0000000000000000-0x0000000000000000 1052672 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000    8192 ipahal_fltrt_init+0x9e4/0xc7c user

+0x0000000000000000-0x0000000000000000 1024000 devm_ioremap_nocache+0x84/0xd8 phys=a600000 ioremap

+0x0000000000000000-0x0000000000000000    8192 ipa3_allocate_dma_task_for_gsi+0xfc/0x318 user

+0x0000000000000000-0x0000000000000000    8192 ipa3_nat_ipv6ct_init_devices+0x160/0x438 user

+0x0000000000000000-0x0000000000000000  536576 devm_ioremap_nocache+0x84/0xd8 phys=ae00000 ioremap

+0x0000000000000000-0x0000000000000000  708608 sde_rot_ioremap_byname+0x84/0x144 phys=ae00000 ioremap

+0x0000000000000000-0x0000000000000000 33558528 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 8392704 pci_ioremap_bar+0x80/0xb4 phys=40800000 ioremap

+0x0000000000000000-0x0000000000000000    8192 ipa3_post_init+0x7fc/0x2c98 user

+0x0000000000000000-0x0000000000000000  442368 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 4198400 pci_ioremap_bar+0x80/0xb4 phys=41800000 ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1052672 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000   36864 ipa3_alloc_common_event_ring+0x178/0x24c user

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 2166784 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000    8192 ipa3_setup_sys_pipe+0x131c/0x2794 user

+0x0000000000000000-0x0000000000000000   12288 ipa3_setup_sys_pipe+0x14cc/0x2794 user

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 2166784 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000    8192 ipa3_setup_sys_pipe+0x131c/0x2794 user

+0x0000000000000000-0x0000000000000000 2166784 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000    8192 ipa3_setup_sys_pipe+0x14cc/0x2794 user

+0x0000000000000000-0x0000000000000000   20480 ipa3_setup_sys_pipe+0x14cc/0x2794 user

+0x0000000000000000-0x0000000000000000    8192 ipa3_uc_interface_init+0x104/0x390 phys=1e47000 ioremap

+0x0000000000000000-0x0000000000000000    8192 ipahal_fltrt_allocate_hw_sys_tbl+0x1cc/0x34c user

+0x0000000000000000-0x0000000000000000    8192 ipahal_fltrt_allocate_hw_sys_tbl+0x1cc/0x34c user

+0x0000000000000000-0x0000000000000000    8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000  184320 devm_ioremap_nocache+0x84/0xd8 phys=1e04000 ioremap

+0x0000000000000000-0x0000000000000000  266240 bpf_map_area_alloc+0x74/0x94 pages=64 vmalloc

+0x0000000000000000-0x0000000000000000  266240 bpf_map_area_alloc+0x74/0x94 pages=64 vmalloc

+0x0000000000000000-0x0000000000000000  266240 bpf_map_area_alloc+0x74/0x94 pages=64 vmalloc

+0x0000000000000000-0x0000000000000000 2166784 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap

+0x0000000000000000-0x0000000000000000  266240 bpf_map_area_alloc+0x74/0x94 pages=64 vmalloc

+0x0000000000000000-0x0000000000000000  700416 ipa3_qmi_service_init_worker+0x78/0x55c pages=170 vmalloc

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000    8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000    8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 5246976 removed_alloc+0x2f8/0x3ac phys=8bf00000 ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1052672 removed_alloc+0x2f8/0x3ac phys=98500000 ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 3141632 ion_heap_map_kernel+0x108/0x158 vmap

+0x0000000000000000-0x0000000000000000 533729280 devm_ioremap+0x84/0xd8 phys=40300000 ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap

+0x0000000000000000-0x0000000000000000 10498048 pktlog_alloc_buf+0xc4/0x15c [wlan] pages=2562 vmalloc vpages

+0x0000000000000000-0x0000000000000000 18350080 setup_arch+0x20c/0x668 phys=80080000 vmap

+0x0000000000000000-0x0000000000000000 8388608 setup_arch+0x20c/0x668 phys=81200000 vmap

+0x0000000000000000-0x0000000000000000 6291456 setup_arch+0x20c/0x668 phys=81a00000 vmap

+0x0000000000000000-0x0000000000000000 11005952 setup_arch+0x20c/0x668 phys=82000000 vmap

+0x0000000000000000-0x0000000000000000 8073216 load_module+0x24ac/0x3408 pages=1970 vmalloc vpages

+0x0000000000000000-0x0000000000000000   20480 load_module+0x24ac/0x3408 pages=4 vmalloc

+0x0000000000000000-0x0000000000000000  151552 load_module+0x24ac/0x3408 pages=36 vmalloc

+0x0000000000000000-0x0000000000000000   28672 load_module+0x24ac/0x3408 pages=6 vmalloc

+0x0000000000000000-0x0000000000000000   94208 load_module+0x24ac/0x3408 pages=22 vmalloc

+0x0000000000000000-0x0000000000000000  409600 load_module+0x24ac/0x3408 pages=99 vmalloc

+0x0000000000000000-0x0000000000000000   36864 load_module+0x24ac/0x3408 pages=8 vmalloc

+0x0000000000000000-0x0000000000000000  303104 load_module+0x24ac/0x3408 pages=73 vmalloc

+0x0000000000000000-0x0000000000000000  172032 load_module+0x24ac/0x3408 pages=41 vmalloc

+0x0000000000000000-0x0000000000000000   20480 load_module+0x24ac/0x3408 pages=4 vmalloc

+0x0000000000000000-0x0000000000000000   20480 load_module+0x24ac/0x3408 pages=4 vmalloc

+0x0000000000000000-0x0000000000000000   20480 load_module+0x24ac/0x3408 pages=4 vmalloc

+0x0000000000000000-0x0000000000000000  245760 load_module+0x24ac/0x3408 pages=59 vmalloc

+0x0000000000000000-0x0000000000000000  786432 pcpu_get_vm_areas+0x0/0x800 vmalloc

+0x0000000000000000-0x0000000000000000  786432 pcpu_get_vm_areas+0x0/0x800 vmalloc

diff --git a/libmeminfo/testdata2/mem_used_total b/libmeminfo/testdata2/mem_used_total
new file mode 100644
index 0000000..97fcf41
--- /dev/null
+++ b/libmeminfo/testdata2/mem_used_total
@@ -0,0 +1 @@
+31236096
diff --git a/libmeminfo/tools/Android.bp b/libmeminfo/tools/Android.bp
new file mode 100644
index 0000000..2e89c41
--- /dev/null
+++ b/libmeminfo/tools/Android.bp
@@ -0,0 +1,83 @@
+// 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: "librank",
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    srcs: ["librank.cpp"],
+    shared_libs: [
+        "libbase",
+        "libmeminfo",
+    ],
+}
+
+cc_binary {
+    name: "procmem",
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    srcs: ["procmem.cpp"],
+    shared_libs: [
+        "libbase",
+        "libmeminfo",
+    ],
+}
+
+cc_binary {
+    name: "procrank",
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    srcs: ["procrank.cpp"],
+    shared_libs: [
+        "libbase",
+        "libmeminfo",
+    ],
+}
+
+cc_binary {
+    name: "showmap",
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    srcs: ["showmap.cpp"],
+    shared_libs: [
+        "libbase",
+        "libmeminfo",
+    ],
+}
+
+cc_binary {
+    name: "wsstop",
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    srcs: ["wsstop.cpp"],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libmeminfo",
+    ],
+}
diff --git a/libmeminfo/tools/librank.cpp b/libmeminfo/tools/librank.cpp
new file mode 100644
index 0000000..e53c746
--- /dev/null
+++ b/libmeminfo/tools/librank.cpp
@@ -0,0 +1,351 @@
+/*
+ * 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 <dirent.h>
+#include <errno.h>
+#include <error.h>
+#include <inttypes.h>
+#include <linux/kernel-page-flags.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <meminfo/procmeminfo.h>
+
+using ::android::meminfo::MemUsage;
+using ::android::meminfo::ProcMemInfo;
+using ::android::meminfo::Vma;
+
+[[noreturn]] static void usage(int exit_status) {
+    fprintf(stderr,
+            "Usage: %s [ -P | -L ] [ -v | -r | -p | -u | -s | -h ]\n"
+            "\n"
+            "Sort options:\n"
+            "    -v  Sort processes by VSS.\n"
+            "    -r  Sort processes by RSS.\n"
+            "    -p  Sort processes by PSS.\n"
+            "    -u  Sort processes by USS.\n"
+            "    -s  Sort processes by swap.\n"
+            "        (Default sort order is PSS.)\n"
+            "    -a  Show all mappings, including stack, heap and anon.\n"
+            "    -P /path  Limit libraries displayed to those in path.\n"
+            "    -R  Reverse sort order (default is descending).\n"
+            "    -m [r][w][x] Only list pages that exactly match permissions\n"
+            "    -c  Only show cached (storage backed) pages\n"
+            "    -C  Only show non-cached (ram/swap backed) pages\n"
+            "    -k  Only show pages collapsed by KSM\n"
+            "    -h  Display this help screen.\n",
+            getprogname());
+    exit(exit_status);
+}
+
+static void add_mem_usage(MemUsage* to, const MemUsage& from) {
+    to->vss += from.vss;
+    to->rss += from.rss;
+    to->pss += from.pss;
+    to->uss += from.uss;
+
+    to->swap += from.swap;
+
+    to->private_clean += from.private_clean;
+    to->private_dirty += from.private_dirty;
+
+    to->shared_clean += from.shared_clean;
+    to->shared_dirty += from.shared_dirty;
+}
+
+struct ProcessRecord {
+  public:
+    ProcessRecord(pid_t pid) : pid_(-1), cmdline_("") {
+        std::string fname = ::android::base::StringPrintf("/proc/%d/cmdline", pid);
+        std::string cmdline;
+        if (!::android::base::ReadFileToString(fname, &cmdline)) {
+            fprintf(stderr, "Failed to read cmdline from: %s\n", fname.c_str());
+            return;
+        }
+        // We deliberately don't read the proc/<pid>cmdline file directly into 'cmdline_'
+        // because of some processes showing up cmdlines that end with "0x00 0x0A 0x00"
+        // e.g. xtra-daemon, lowi-server
+        // The .c_str() assignment below then takes care of trimming the cmdline at the first
+        // 0x00. This is how original procrank worked (luckily)
+        cmdline_ = cmdline.c_str();
+        pid_ = pid;
+        usage_.clear();
+    }
+
+    ~ProcessRecord() = default;
+
+    bool valid() const { return pid_ != -1; }
+
+    // Getters
+    pid_t pid() const { return pid_; }
+    const std::string& cmdline() const { return cmdline_; }
+    const MemUsage& usage() const { return usage_; }
+
+    // Add to the usage
+    void AddUsage(const MemUsage& mem_usage) { add_mem_usage(&usage_, mem_usage); }
+
+  private:
+    pid_t pid_;
+    std::string cmdline_;
+    MemUsage usage_;
+};
+
+struct LibRecord {
+  public:
+    LibRecord(const std::string& name) : name_(name) {}
+    ~LibRecord() = default;
+
+    const std::string& name() const { return name_; }
+    const MemUsage& usage() const { return usage_; }
+    const std::map<pid_t, ProcessRecord>& processes() const { return procs_; }
+    uint64_t pss() const { return usage_.pss; }
+    void AddUsage(const ProcessRecord& proc, const MemUsage& mem_usage) {
+        auto [it, inserted] = procs_.insert(std::pair<pid_t, ProcessRecord>(proc.pid(), proc));
+        it->second.AddUsage(mem_usage);
+        add_mem_usage(&usage_, mem_usage);
+    }
+
+  private:
+    std::string name_;
+    MemUsage usage_;
+    std::map<pid_t, ProcessRecord> procs_;
+};
+
+// List of every library / map
+static std::map<std::string, LibRecord> g_libs;
+
+// List of library/map names that we don't want to show by default
+static const std::vector<std::string> g_blacklisted_libs = {"[heap]", "[stack]"};
+
+// Global flags affected by command line
+static uint64_t g_pgflags = 0;
+static uint64_t g_pgflags_mask = 0;
+static uint16_t g_mapflags_mask = 0;
+static bool g_all_libs = false;
+static bool g_has_swap = false;
+static bool g_reverse_sort = false;
+static std::string g_prefix_filter = "";
+
+static bool read_all_pids(std::function<bool(pid_t pid)> for_each_pid) {
+    std::unique_ptr<DIR, int (*)(DIR*)> procdir(opendir("/proc"), closedir);
+    if (!procdir) return false;
+
+    struct dirent* dir;
+    pid_t pid;
+    while ((dir = readdir(procdir.get()))) {
+        if (!::android::base::ParseInt(dir->d_name, &pid)) continue;
+        if (!for_each_pid(pid)) return false;
+    }
+
+    return true;
+}
+
+static bool scan_libs_per_process(pid_t pid) {
+    ProcMemInfo pmem(pid, false, g_pgflags, g_pgflags_mask);
+    const std::vector<Vma> maps = pmem.Maps();
+    if (maps.size() == 0) {
+        // nothing to do here, continue
+        return true;
+    }
+
+    ProcessRecord proc(pid);
+    if (!proc.valid()) {
+        fprintf(stderr, "Failed to create process record for process: %d\n", pid);
+        return false;
+    }
+
+    for (auto& map : maps) {
+        // skip library / map if prefix for the path doesn't match
+        if (!g_prefix_filter.empty() && !::android::base::StartsWith(map.name, g_prefix_filter)) {
+            continue;
+        }
+        // Skip maps based on map permissions
+        if (g_mapflags_mask &&
+            ((map.flags & (PROT_READ | PROT_WRITE | PROT_EXEC)) != g_mapflags_mask)) {
+            continue;
+        }
+
+        // skip blacklisted library / map names
+        if (!g_all_libs && (std::find(g_blacklisted_libs.begin(), g_blacklisted_libs.end(),
+                                      map.name) != g_blacklisted_libs.end())) {
+            continue;
+        }
+
+        auto [it, inserted] =
+            g_libs.insert(std::pair<std::string, LibRecord>(map.name, LibRecord(map.name)));
+        it->second.AddUsage(proc, map.usage);
+
+        if (!g_has_swap && map.usage.swap) {
+            g_has_swap = true;
+        }
+    }
+
+    return true;
+}
+
+static uint16_t parse_mapflags(const char* mapflags) {
+    uint16_t ret = 0;
+    for (const char* p = mapflags; *p; p++) {
+        switch (*p) {
+            case 'r':
+                ret |= PROT_READ;
+                break;
+            case 'w':
+                ret |= PROT_WRITE;
+                break;
+            case 'x':
+                ret |= PROT_EXEC;
+                break;
+            default:
+                error(EXIT_FAILURE, 0, "Invalid permissions string: %s, %s", mapflags, p);
+        }
+    }
+
+    return ret;
+}
+
+int main(int argc, char* argv[]) {
+    int opt;
+
+    auto pss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+        return g_reverse_sort ? a.usage().pss < b.usage().pss : a.usage().pss > b.usage().pss;
+    };
+
+    auto uss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+        return g_reverse_sort ? a.usage().uss < b.usage().uss : a.usage().uss > b.usage().uss;
+    };
+
+    auto vss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+        return g_reverse_sort ? a.usage().vss < b.usage().vss : a.usage().vss > b.usage().vss;
+    };
+
+    auto rss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+        return g_reverse_sort ? a.usage().rss < b.usage().rss : a.usage().rss > b.usage().rss;
+    };
+
+    auto swap_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+        return g_reverse_sort ? a.usage().swap < b.usage().swap : a.usage().swap > b.usage().swap;
+    };
+
+    std::function<bool(const ProcessRecord&, const ProcessRecord&)> sort_func = pss_sort;
+
+    while ((opt = getopt(argc, argv, "acChkm:pP:uvrsR")) != -1) {
+        switch (opt) {
+            case 'a':
+                g_all_libs = true;
+                break;
+            case 'c':
+                g_pgflags = 0;
+                g_pgflags_mask = (1 << KPF_SWAPBACKED);
+                break;
+            case 'C':
+                g_pgflags = g_pgflags_mask = (1 << KPF_SWAPBACKED);
+                break;
+            case 'h':
+                usage(EXIT_SUCCESS);
+            case 'k':
+                g_pgflags = g_pgflags_mask = (1 << KPF_KSM);
+                break;
+            case 'm':
+                g_mapflags_mask = parse_mapflags(optarg);
+                break;
+            case 'p':
+                sort_func = pss_sort;
+                break;
+            case 'P':
+                g_prefix_filter = optarg;
+                break;
+            case 'u':
+                sort_func = uss_sort;
+                break;
+            case 'v':
+                sort_func = vss_sort;
+                break;
+            case 'r':
+                sort_func = rss_sort;
+                break;
+            case 's':
+                sort_func = swap_sort;
+                break;
+            case 'R':
+                g_reverse_sort = true;
+                break;
+            default:
+                usage(EXIT_FAILURE);
+        }
+    }
+
+    if (!read_all_pids(scan_libs_per_process)) {
+        error(EXIT_FAILURE, 0, "Failed to read all pids from the system");
+    }
+
+    printf(" %6s   %7s   %6s   %6s   %6s  ", "RSStot", "VSS", "RSS", "PSS", "USS");
+    if (g_has_swap) {
+        printf(" %6s  ", "Swap");
+    }
+    printf("Name/PID\n");
+
+    std::vector<LibRecord> v_libs;
+    v_libs.reserve(g_libs.size());
+    std::transform(g_libs.begin(), g_libs.end(), std::back_inserter(v_libs),
+        [] (std::pair<std::string, LibRecord> const& pair) { return pair.second; });
+
+    // sort the libraries by their pss
+    std::sort(v_libs.begin(), v_libs.end(),
+              [](const LibRecord& l1, const LibRecord& l2) { return l1.pss() > l2.pss(); });
+
+    for (auto& lib : v_libs) {
+        printf("%6" PRIu64 "K   %7s   %6s   %6s   %6s  ", lib.pss() / 1024, "", "", "", "");
+        if (g_has_swap) {
+            printf(" %6s  ", "");
+        }
+        printf("%s\n", lib.name().c_str());
+
+        // sort all mappings first
+
+        std::vector<ProcessRecord> procs;
+        procs.reserve(lib.processes().size());
+        std::transform(lib.processes().begin(), lib.processes().end(), std::back_inserter(procs),
+            [] (std::pair<pid_t, ProcessRecord> const& pair) { return pair.second; });
+
+        std::sort(procs.begin(), procs.end(), sort_func);
+
+        for (auto& p : procs) {
+            const MemUsage& usage = p.usage();
+            printf(" %6s  %7" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  ", "",
+                   usage.vss / 1024, usage.rss / 1024, usage.pss / 1024, usage.uss / 1024);
+            if (g_has_swap) {
+                printf("%6" PRIu64 "K  ", usage.swap / 1024);
+            }
+            printf("  %s [%d]\n", p.cmdline().c_str(), p.pid());
+        }
+    }
+
+    return 0;
+}
diff --git a/libmeminfo/tools/procmem.cpp b/libmeminfo/tools/procmem.cpp
new file mode 100644
index 0000000..47881ed
--- /dev/null
+++ b/libmeminfo/tools/procmem.cpp
@@ -0,0 +1,192 @@
+/*
+ * 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 <inttypes.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <meminfo/procmeminfo.h>
+
+using Vma = ::android::meminfo::Vma;
+using ProcMemInfo = ::android::meminfo::ProcMemInfo;
+using MemUsage = ::android::meminfo::MemUsage;
+
+// Global flags to control procmem output
+
+// Set to use page idle bits for working set detection
+bool use_pageidle = false;
+// hides map entries with zero rss
+bool hide_zeroes = false;
+// Reset working set and exit
+bool reset_wss = false;
+// Show working set, mutually exclusive with reset_wss;
+bool show_wss = false;
+
+[[noreturn]] static void usage(int exit_status) {
+    fprintf(stderr,
+            "Usage: %s [-i] [ -w | -W ] [ -p | -m ] [ -h ] pid\n"
+            "    -i  Uses idle page tracking for working set statistics.\n"
+            "    -w  Displays statistics for the working set only.\n"
+            "    -W  Resets the working set of the process.\n"
+            "    -p  Sort by PSS.\n"
+            "    -u  Sort by USS.\n"
+            "    -m  Sort by mapping order (as read from /proc).\n"
+            "    -h  Hide maps with no RSS.\n",
+            getprogname());
+
+    exit(exit_status);
+}
+
+static void print_separator(std::stringstream& ss) {
+    if (show_wss) {
+        ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n", "-------",
+                                            "-------", "-------", "-------", "-------", "-------",
+                                            "-------", "");
+        return;
+    }
+    ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n", "-------",
+                                        "-------", "-------", "-------", "-------", "-------",
+                                        "-------", "-------", "");
+}
+
+static void print_header(std::stringstream& ss) {
+    if (show_wss) {
+        ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n", "WRss",
+                                            "WPss", "WUss", "WShCl", "WShDi", "WPrCl", "WPrDi",
+                                            "Name");
+    } else {
+        ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n", "Vss",
+                                            "Rss", "Pss", "Uss", "ShCl", "ShDi", "PrCl", "PrDi",
+                                            "Name");
+    }
+    print_separator(ss);
+}
+
+static void print_stats(std::stringstream& ss, const MemUsage& stats) {
+    if (!show_wss) {
+        ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", stats.vss / 1024);
+    }
+
+    ss << ::android::base::StringPrintf("%6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64
+                                        "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  ",
+                                        stats.rss / 1024, stats.pss / 1024, stats.uss / 1024,
+                                        stats.shared_clean / 1024, stats.shared_dirty / 1024,
+                                        stats.private_clean / 1024, stats.private_dirty / 1024);
+}
+
+static int show(const MemUsage& proc_stats, const std::vector<Vma>& maps) {
+    std::stringstream ss;
+    print_header(ss);
+    for (auto& vma : maps) {
+        const MemUsage& vma_stats = vma.usage;
+        if (hide_zeroes && vma_stats.rss == 0) {
+            continue;
+        }
+        print_stats(ss, vma_stats);
+        ss << vma.name << std::endl;
+    }
+    print_separator(ss);
+    print_stats(ss, proc_stats);
+    ss << "TOTAL" << std::endl;
+    std::cout << ss.str();
+
+    return 0;
+}
+
+int main(int argc, char* argv[]) {
+    int opt;
+    auto pss_sort = [](const Vma& a, const Vma& b) {
+        uint64_t pss_a = a.usage.pss;
+        uint64_t pss_b = b.usage.pss;
+        return pss_a > pss_b;
+    };
+
+    auto uss_sort = [](const Vma& a, const Vma& b) {
+        uint64_t uss_a = a.usage.uss;
+        uint64_t uss_b = b.usage.uss;
+        return uss_a > uss_b;
+    };
+
+    std::function<bool(const Vma& a, const Vma& b)> sort_func = nullptr;
+    while ((opt = getopt(argc, argv, "himpuWw")) != -1) {
+        switch (opt) {
+            case 'h':
+                hide_zeroes = true;
+                break;
+            case 'i':
+                // TODO: libmeminfo doesn't support the flag to chose
+                // between idle page tracking vs clear_refs. So for now,
+                // this flag is unused and the library defaults to using
+                // /proc/<pid>/clear_refs for finding the working set.
+                use_pageidle = true;
+                break;
+            case 'm':
+                // this is the default
+                break;
+            case 'p':
+                sort_func = pss_sort;
+                break;
+            case 'u':
+                sort_func = uss_sort;
+                break;
+            case 'W':
+                reset_wss = true;
+                break;
+            case 'w':
+                show_wss = true;
+                break;
+            case '?':
+                usage(EXIT_SUCCESS);
+            default:
+                usage(EXIT_FAILURE);
+        }
+    }
+
+    if (optind != (argc - 1)) {
+        fprintf(stderr, "Need exactly one pid at the end\n");
+        usage(EXIT_FAILURE);
+    }
+
+    pid_t pid = atoi(argv[optind]);
+    if (pid == 0) {
+        std::cerr << "Invalid process id" << std::endl;
+        exit(EXIT_FAILURE);
+    }
+
+    if (reset_wss) {
+        if (!ProcMemInfo::ResetWorkingSet(pid)) {
+            std::cerr << "Failed to reset working set of pid : " << pid << std::endl;
+            exit(EXIT_FAILURE);
+        }
+        return 0;
+    }
+
+    ProcMemInfo proc(pid, show_wss);
+    const MemUsage& proc_stats = proc.Usage();
+    std::vector<Vma> maps(proc.Maps());
+    if (sort_func != nullptr) {
+        std::sort(maps.begin(), maps.end(), sort_func);
+    }
+
+    return show(proc_stats, maps);
+}
diff --git a/libmeminfo/tools/procrank.cpp b/libmeminfo/tools/procrank.cpp
new file mode 100644
index 0000000..1e44ff9
--- /dev/null
+++ b/libmeminfo/tools/procrank.cpp
@@ -0,0 +1,536 @@
+/*
+ * 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/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <dirent.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <linux/kernel-page-flags.h>
+#include <linux/oom.h>
+#include <meminfo/procmeminfo.h>
+#include <meminfo/sysmeminfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <vector>
+
+using ::android::meminfo::MemUsage;
+using ::android::meminfo::ProcMemInfo;
+
+struct ProcessRecord {
+  public:
+    ProcessRecord(pid_t pid, bool get_wss = false, uint64_t pgflags = 0, uint64_t pgflags_mask = 0)
+        : pid_(-1),
+          oomadj_(OOM_SCORE_ADJ_MAX + 1),
+          cmdline_(""),
+          proportional_swap_(0),
+          unique_swap_(0),
+          zswap_(0) {
+        std::unique_ptr<ProcMemInfo> procmem =
+                std::make_unique<ProcMemInfo>(pid, get_wss, pgflags, pgflags_mask);
+        if (procmem == nullptr) {
+            std::cerr << "Failed to create ProcMemInfo for: " << pid << std::endl;
+            return;
+        }
+
+        std::string fname = ::android::base::StringPrintf("/proc/%d/oom_score_adj", pid);
+        auto oomscore_fp =
+                std::unique_ptr<FILE, decltype(&fclose)>{fopen(fname.c_str(), "re"), fclose};
+        if (oomscore_fp == nullptr) {
+            std::cerr << "Failed to open oom_score_adj file: " << fname << std::endl;
+            return;
+        }
+
+        if (fscanf(oomscore_fp.get(), "%d\n", &oomadj_) != 1) {
+            std::cerr << "Failed to read oomadj from: " << fname << std::endl;
+            return;
+        }
+
+        fname = ::android::base::StringPrintf("/proc/%d/cmdline", pid);
+        if (!::android::base::ReadFileToString(fname, &cmdline_)) {
+            std::cerr << "Failed to read cmdline from: " << fname << std::endl;
+            cmdline_ = "<unknown>";
+        }
+        // We deliberately don't read the proc/<pid>cmdline file directly into 'cmdline_'
+        // because of some processes showing up cmdlines that end with "0x00 0x0A 0x00"
+        // e.g. xtra-daemon, lowi-server
+        // The .c_str() assignment below then takes care of trimming the cmdline at the first
+        // 0x00. This is how original procrank worked (luckily)
+        cmdline_.resize(strlen(cmdline_.c_str()));
+        usage_or_wss_ = get_wss ? procmem->Wss() : procmem->Usage();
+        swap_offsets_ = procmem->SwapOffsets();
+        pid_ = pid;
+    }
+
+    bool valid() const { return pid_ != -1; }
+
+    void CalculateSwap(const uint16_t* swap_offset_array, float zram_compression_ratio) {
+        for (auto& off : swap_offsets_) {
+            proportional_swap_ += getpagesize() / swap_offset_array[off];
+            unique_swap_ += swap_offset_array[off] == 1 ? getpagesize() : 0;
+            zswap_ = proportional_swap_ * zram_compression_ratio;
+        }
+    }
+
+    // Getters
+    pid_t pid() const { return pid_; }
+    const std::string& cmdline() const { return cmdline_; }
+    int32_t oomadj() const { return oomadj_; }
+    uint64_t proportional_swap() const { return proportional_swap_; }
+    uint64_t unique_swap() const { return unique_swap_; }
+    uint64_t zswap() const { return zswap_; }
+
+    // Wrappers to ProcMemInfo
+    const std::vector<uint16_t>& SwapOffsets() const { return swap_offsets_; }
+    const MemUsage& Usage() const { return usage_or_wss_; }
+    const MemUsage& Wss() const { return usage_or_wss_; }
+
+  private:
+    pid_t pid_;
+    int32_t oomadj_;
+    std::string cmdline_;
+    uint64_t proportional_swap_;
+    uint64_t unique_swap_;
+    uint64_t zswap_;
+    MemUsage usage_or_wss_;
+    std::vector<uint16_t> swap_offsets_;
+};
+
+// Show working set instead of memory consumption
+bool show_wss = false;
+// Reset working set of each process
+bool reset_wss = false;
+// Show per-process oom_score_adj column
+bool show_oomadj = false;
+// True if the device has swap enabled
+bool has_swap = false;
+// True, if device has zram enabled
+bool has_zram = false;
+// If zram is enabled, the compression ratio is zram used / swap used.
+float zram_compression_ratio = 0.0;
+// Sort process in reverse, default is descending
+bool reverse_sort = false;
+
+// Calculated total memory usage across all processes in the system
+uint64_t total_pss = 0;
+uint64_t total_uss = 0;
+uint64_t total_swap = 0;
+uint64_t total_pswap = 0;
+uint64_t total_uswap = 0;
+uint64_t total_zswap = 0;
+
+[[noreturn]] static void usage(int exit_status) {
+    std::cerr << "Usage: " << getprogname() << " [ -W ] [ -v | -r | -p | -u | -s | -h ]"
+              << std::endl
+              << "    -v  Sort by VSS." << std::endl
+              << "    -r  Sort by RSS." << std::endl
+              << "    -p  Sort by PSS." << std::endl
+              << "    -u  Sort by USS." << std::endl
+              << "    -s  Sort by swap." << std::endl
+              << "        (Default sort order is PSS.)" << std::endl
+              << "    -R  Reverse sort order (default is descending)." << std::endl
+              << "    -c  Only show cached (storage backed) pages" << std::endl
+              << "    -C  Only show non-cached (ram/swap backed) pages" << std::endl
+              << "    -k  Only show pages collapsed by KSM" << std::endl
+              << "    -w  Display statistics for working set only." << std::endl
+              << "    -W  Reset working set of all processes." << std::endl
+              << "    -o  Show and sort by oom score against lowmemorykiller thresholds."
+              << std::endl
+              << "    -h  Display this help screen." << std::endl;
+    exit(exit_status);
+}
+
+static bool read_all_pids(std::vector<pid_t>* pids, std::function<bool(pid_t pid)> for_each_pid) {
+    pids->clear();
+    std::unique_ptr<DIR, int (*)(DIR*)> procdir(opendir("/proc"), closedir);
+    if (!procdir) return false;
+
+    struct dirent* dir;
+    pid_t pid;
+    while ((dir = readdir(procdir.get()))) {
+        if (!::android::base::ParseInt(dir->d_name, &pid)) continue;
+        if (!for_each_pid(pid)) return false;
+        pids->emplace_back(pid);
+    }
+
+    return true;
+}
+
+static bool count_swap_offsets(const ProcessRecord& proc, uint16_t* swap_offset_array,
+                               uint32_t size) {
+    const std::vector<uint16_t>& swp_offs = proc.SwapOffsets();
+    for (auto& off : swp_offs) {
+        if (off >= size) {
+            std::cerr << "swap offset " << off << " is out of bounds for process: " << proc.pid()
+                      << std::endl;
+            return false;
+        }
+
+        if (swap_offset_array[off] == USHRT_MAX) {
+            std::cerr << "swap offset " << off << " ref count overflow in process: " << proc.pid()
+                      << std::endl;
+            return false;
+        }
+
+        swap_offset_array[off]++;
+    }
+
+    return true;
+}
+
+static void print_header(std::stringstream& ss) {
+    ss.str("");
+    ss << ::android::base::StringPrintf("%5s  ", "PID");
+    if (show_oomadj) {
+        ss << ::android::base::StringPrintf("%5s  ", "oom");
+    }
+
+    if (show_wss) {
+        ss << ::android::base::StringPrintf("%7s  %7s  %7s  ", "WRss", "WPss", "WUss");
+        // now swap statistics here, working set pages by definition shouldn't end up in swap.
+    } else {
+        ss << ::android::base::StringPrintf("%8s  %7s  %7s  %7s  ", "Vss", "Rss", "Pss", "Uss");
+        if (has_swap) {
+            ss << ::android::base::StringPrintf("%7s  %7s  %7s  ", "Swap", "PSwap", "USwap");
+            if (has_zram) {
+                ss << ::android::base::StringPrintf("%7s  ", "ZSwap");
+            }
+        }
+    }
+
+    ss << "cmdline";
+}
+
+static void print_process_record(std::stringstream& ss, ProcessRecord& proc) {
+    ss << ::android::base::StringPrintf("%5d  ", proc.pid());
+    if (show_oomadj) {
+        ss << ::android::base::StringPrintf("%5d  ", proc.oomadj());
+    }
+
+    if (show_wss) {
+        ss << ::android::base::StringPrintf("%6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  ",
+                                            proc.Wss().rss / 1024, proc.Wss().pss / 1024,
+                                            proc.Wss().uss / 1024);
+    } else {
+        ss << ::android::base::StringPrintf("%7" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64
+                                            "K  ",
+                                            proc.Usage().vss / 1024, proc.Usage().rss / 1024,
+                                            proc.Usage().pss / 1024, proc.Usage().uss / 1024);
+        if (has_swap) {
+            ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", proc.Usage().swap / 1024);
+            ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", proc.proportional_swap() / 1024);
+            ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", proc.unique_swap() / 1024);
+            if (has_zram) {
+                ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", (proc.zswap() / 1024));
+            }
+        }
+    }
+}
+
+static void print_processes(std::stringstream& ss, std::vector<ProcessRecord>& procs,
+                            uint16_t* swap_offset_array) {
+    for (auto& proc : procs) {
+        total_pss += show_wss ? proc.Wss().pss : proc.Usage().pss;
+        total_uss += show_wss ? proc.Wss().uss : proc.Usage().uss;
+        if (!show_wss && has_swap) {
+            proc.CalculateSwap(swap_offset_array, zram_compression_ratio);
+            total_swap += proc.Usage().swap;
+            total_pswap += proc.proportional_swap();
+            total_uswap += proc.unique_swap();
+            if (has_zram) {
+                total_zswap += proc.zswap();
+            }
+        }
+
+        print_process_record(ss, proc);
+        ss << proc.cmdline() << std::endl;
+    }
+}
+
+static void print_separator(std::stringstream& ss) {
+    ss << ::android::base::StringPrintf("%5s  ", "");
+    if (show_oomadj) {
+        ss << ::android::base::StringPrintf("%5s  ", "");
+    }
+
+    if (show_wss) {
+        ss << ::android::base::StringPrintf("%7s  %7s  %7s  ", "", "------", "------");
+    } else {
+        ss << ::android::base::StringPrintf("%8s  %7s  %7s  %7s  ", "", "", "------", "------");
+        if (has_swap) {
+            ss << ::android::base::StringPrintf("%7s  %7s  %7s  ", "------", "------", "------");
+            if (has_zram) {
+                ss << ::android::base::StringPrintf("%7s  ", "------");
+            }
+        }
+    }
+
+    ss << ::android::base::StringPrintf("%s", "------");
+}
+
+static void print_totals(std::stringstream& ss) {
+    ss << ::android::base::StringPrintf("%5s  ", "");
+    if (show_oomadj) {
+        ss << ::android::base::StringPrintf("%5s  ", "");
+    }
+
+    if (show_wss) {
+        ss << ::android::base::StringPrintf("%7s  %6" PRIu64 "K  %6" PRIu64 "K  ", "",
+                                            total_pss / 1024, total_uss / 1024);
+    } else {
+        ss << ::android::base::StringPrintf("%8s  %7s  %6" PRIu64 "K  %6" PRIu64 "K  ", "", "",
+                                            total_pss / 1024, total_uss / 1024);
+        if (has_swap) {
+            ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", total_swap / 1024);
+            ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", total_pswap / 1024);
+            ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", total_uswap / 1024);
+            if (has_zram) {
+                ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", total_zswap / 1024);
+            }
+        }
+    }
+    ss << "TOTAL";
+}
+
+static void print_sysmeminfo(std::stringstream& ss, ::android::meminfo::SysMemInfo& smi) {
+    if (has_swap) {
+        ss << ::android::base::StringPrintf("ZRAM: %" PRIu64 "K physical used for %" PRIu64
+                                            "K in swap "
+                                            "(%" PRIu64 "K total swap)",
+                                            smi.mem_zram_kb(),
+                                            (smi.mem_swap_kb() - smi.mem_swap_free_kb()),
+                                            smi.mem_swap_kb())
+           << std::endl;
+    }
+
+    ss << ::android::base::StringPrintf(" RAM: %" PRIu64 "K total, %" PRIu64 "K free, %" PRIu64
+                                        "K buffers, "
+                                        "%" PRIu64 "K cached, %" PRIu64 "K shmem, %" PRIu64
+                                        "K slab",
+                                        smi.mem_total_kb(), smi.mem_free_kb(), smi.mem_buffers_kb(),
+                                        smi.mem_cached_kb(), smi.mem_shmem_kb(), smi.mem_slab_kb());
+}
+
+int main(int argc, char* argv[]) {
+    auto pss_sort = [](ProcessRecord& a, ProcessRecord& b) {
+        MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
+        MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
+        return reverse_sort ? stats_a.pss < stats_b.pss : stats_a.pss > stats_b.pss;
+    };
+
+    auto uss_sort = [](ProcessRecord& a, ProcessRecord& b) {
+        MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
+        MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
+        return reverse_sort ? stats_a.uss < stats_b.uss : stats_a.uss > stats_b.uss;
+    };
+
+    auto rss_sort = [](ProcessRecord& a, ProcessRecord& b) {
+        MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
+        MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
+        return reverse_sort ? stats_a.rss < stats_b.rss : stats_a.rss > stats_b.rss;
+    };
+
+    auto vss_sort = [](ProcessRecord& a, ProcessRecord& b) {
+        MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
+        MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
+        return reverse_sort ? stats_a.vss < stats_b.vss : stats_a.vss > stats_b.vss;
+    };
+
+    auto swap_sort = [](ProcessRecord& a, ProcessRecord& b) {
+        MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
+        MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
+        return reverse_sort ? stats_a.swap < stats_b.swap : stats_a.swap > stats_b.swap;
+    };
+
+    auto oomadj_sort = [](ProcessRecord& a, ProcessRecord& b) {
+        return reverse_sort ? a.oomadj() < b.oomadj() : a.oomadj() > b.oomadj();
+    };
+
+    // default PSS sort
+    std::function<bool(ProcessRecord & a, ProcessRecord & b)> proc_sort = pss_sort;
+
+    // count all pages by default
+    uint64_t pgflags = 0;
+    uint64_t pgflags_mask = 0;
+
+    int opt;
+    while ((opt = getopt(argc, argv, "cChkoprRsuvwW")) != -1) {
+        switch (opt) {
+            case 'c':
+                pgflags = 0;
+                pgflags_mask = (1 << KPF_SWAPBACKED);
+                break;
+            case 'C':
+                pgflags = (1 << KPF_SWAPBACKED);
+                pgflags_mask = (1 << KPF_SWAPBACKED);
+                break;
+            case 'h':
+                usage(EXIT_SUCCESS);
+            case 'k':
+                pgflags = (1 << KPF_KSM);
+                pgflags_mask = (1 << KPF_KSM);
+                break;
+            case 'o':
+                proc_sort = oomadj_sort;
+                show_oomadj = true;
+                break;
+            case 'p':
+                proc_sort = pss_sort;
+                break;
+            case 'r':
+                proc_sort = rss_sort;
+                break;
+            case 'R':
+                reverse_sort = true;
+                break;
+            case 's':
+                proc_sort = swap_sort;
+                break;
+            case 'u':
+                proc_sort = uss_sort;
+                break;
+            case 'v':
+                proc_sort = vss_sort;
+                break;
+            case 'w':
+                show_wss = true;
+                break;
+            case 'W':
+                reset_wss = true;
+                break;
+            default:
+                usage(EXIT_FAILURE);
+        }
+    }
+
+    std::vector<pid_t> pids;
+    std::vector<ProcessRecord> procs;
+    if (reset_wss) {
+        if (!read_all_pids(&pids,
+                           [&](pid_t pid) -> bool { return ProcMemInfo::ResetWorkingSet(pid); })) {
+            std::cerr << "Failed to reset working set of all processes" << std::endl;
+            exit(EXIT_FAILURE);
+        }
+        // we are done, all other options passed to procrank are ignored in the presence of '-W'
+        return 0;
+    }
+
+    ::android::meminfo::SysMemInfo smi;
+    if (!smi.ReadMemInfo()) {
+        std::cerr << "Failed to get system memory info" << std::endl;
+        exit(EXIT_FAILURE);
+    }
+
+    // Figure out swap and zram
+    uint64_t swap_total = smi.mem_swap_kb() * 1024;
+    has_swap = swap_total > 0;
+    // Allocate the swap array
+    auto swap_offset_array = std::make_unique<uint16_t[]>(swap_total / getpagesize());
+    if (has_swap) {
+        has_zram = smi.mem_zram_kb() > 0;
+        if (has_zram) {
+            zram_compression_ratio = static_cast<float>(smi.mem_zram_kb()) /
+                                     (smi.mem_swap_kb() - smi.mem_swap_free_kb());
+        }
+    }
+
+    auto mark_swap_usage = [&](pid_t pid) -> bool {
+        ProcessRecord proc(pid, show_wss, pgflags, pgflags_mask);
+        if (!proc.valid()) {
+            // Check to see if the process is still around, skip the process if the proc
+            // directory is inaccessible. It was most likely killed while creating the process
+            // record
+            std::string procdir = ::android::base::StringPrintf("/proc/%d", pid);
+            if (access(procdir.c_str(), F_OK | R_OK)) return true;
+
+            // Warn if we failed to gather process stats even while it is still alive.
+            // Return success here, so we continue to print stats for other processes.
+            std::cerr << "warning: failed to create process record for: " << pid << std::endl;
+            return true;
+        }
+
+        // Skip processes with no memory mappings
+        uint64_t vss = show_wss ? proc.Wss().vss : proc.Usage().vss;
+        if (vss == 0) return true;
+
+        // collect swap_offset counts from all processes in 1st pass
+        if (!show_wss && has_swap &&
+            !count_swap_offsets(proc, swap_offset_array.get(), swap_total / getpagesize())) {
+            std::cerr << "Failed to count swap offsets for process: " << pid << std::endl;
+            return false;
+        }
+
+        procs.emplace_back(std::move(proc));
+        return true;
+    };
+
+    // Get a list of all pids currently running in the system in 1st pass through all processes.
+    // Mark each swap offset used by the process as we find them for calculating proportional
+    // swap usage later.
+    if (!read_all_pids(&pids, mark_swap_usage)) {
+        std::cerr << "Failed to read all pids from the system" << std::endl;
+        exit(EXIT_FAILURE);
+    }
+
+    std::stringstream ss;
+    if (procs.empty()) {
+        // This would happen in corner cases where procrank is being run to find KSM usage on a
+        // system with no KSM and combined with working set determination as follows
+        //   procrank -w -u -k
+        //   procrank -w -s -k
+        //   procrank -w -o -k
+        ss << "<empty>" << std::endl << std::endl;
+        print_sysmeminfo(ss, smi);
+        ss << std::endl;
+        std::cout << ss.str();
+        return 0;
+    }
+
+    // Sort all process records, default is PSS descending
+    std::sort(procs.begin(), procs.end(), proc_sort);
+
+    // start dumping output in string stream
+    print_header(ss);
+    ss << std::endl;
+
+    // 2nd pass to calculate and get per process stats to add them up
+    print_processes(ss, procs, swap_offset_array.get());
+
+    // Add separator to output
+    print_separator(ss);
+    ss << std::endl;
+
+    // Add totals to output
+    print_totals(ss);
+    ss << std::endl << std::endl;
+
+    // Add system information at the end
+    print_sysmeminfo(ss, smi);
+    ss << std::endl;
+
+    // dump on the screen
+    std::cout << ss.str();
+
+    return 0;
+}
diff --git a/libmeminfo/tools/showmap.cpp b/libmeminfo/tools/showmap.cpp
new file mode 100644
index 0000000..a80fa76
--- /dev/null
+++ b/libmeminfo/tools/showmap.cpp
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <meminfo/procmeminfo.h>
+
+using ::android::meminfo::Vma;
+
+struct VmaInfo {
+    Vma vma;
+    bool is_bss;
+    uint32_t count;
+
+    VmaInfo() = default;
+    VmaInfo(const Vma& v) : vma(v), is_bss(false), count(1) {}
+    VmaInfo(const Vma& v, bool bss) : vma(v), is_bss(bss), count(1) {}
+    VmaInfo(const Vma& v, const std::string& name, bool bss) : vma(v), is_bss(bss), count(1) {
+        vma.name = name;
+    }
+};
+
+// Global options
+static std::string g_filename = "";
+static bool g_merge_by_names = false;
+static bool g_terse = false;
+static bool g_verbose = false;
+static bool g_show_addr = false;
+static bool g_quiet = false;
+static pid_t g_pid = -1;
+
+static VmaInfo g_total;
+static std::vector<VmaInfo> g_vmas;
+
+[[noreturn]] static void usage(int exit_status) {
+    fprintf(stderr,
+            "%s [-aqtv] [-f FILE] PID\n"
+            "-a\taddresses (show virtual memory map)\n"
+            "-q\tquiet (don't show error if map could not be read)\n"
+            "-t\tterse (show only items with private pages)\n"
+            "-v\tverbose (don't coalesce maps with the same name)\n"
+            "-f\tFILE (read from input from FILE instead of PID)\n",
+            getprogname());
+
+    exit(exit_status);
+}
+
+static bool is_library(const std::string& name) {
+    return (name.size() > 4) && (name[0] == '/') && ::android::base::EndsWith(name, ".so");
+}
+
+static bool insert_before(const VmaInfo& a, const VmaInfo& b) {
+    if (g_show_addr) {
+        return (a.vma.start < b.vma.start || (a.vma.start == b.vma.start && a.vma.end < b.vma.end));
+    }
+
+    return strcmp(a.vma.name.c_str(), b.vma.name.c_str()) < 0;
+}
+
+static void collect_vma(const Vma& vma) {
+    if (g_vmas.empty()) {
+        g_vmas.emplace_back(vma);
+        return;
+    }
+
+    VmaInfo current(vma);
+    VmaInfo& last = g_vmas.back();
+    // determine if this is bss;
+    if (vma.name.empty()) {
+        if (last.vma.end == current.vma.start && is_library(last.vma.name)) {
+            current.vma.name = last.vma.name;
+            current.is_bss = true;
+        } else {
+            current.vma.name = "[anon]";
+        }
+    }
+
+    std::vector<VmaInfo>::iterator it;
+    for (it = g_vmas.begin(); it != g_vmas.end(); it++) {
+        if (g_merge_by_names && (it->vma.name == current.vma.name)) {
+            it->vma.usage.vss += current.vma.usage.vss;
+            it->vma.usage.rss += current.vma.usage.rss;
+            it->vma.usage.pss += current.vma.usage.pss;
+
+            it->vma.usage.shared_clean += current.vma.usage.shared_clean;
+            it->vma.usage.shared_dirty += current.vma.usage.shared_dirty;
+            it->vma.usage.private_clean += current.vma.usage.private_clean;
+            it->vma.usage.private_dirty += current.vma.usage.private_dirty;
+            it->vma.usage.swap += current.vma.usage.swap;
+            it->vma.usage.swap_pss += current.vma.usage.swap_pss;
+            it->is_bss &= current.is_bss;
+            it->count++;
+            break;
+        }
+
+        if (insert_before(current, *it)) {
+            g_vmas.insert(it, current);
+            break;
+        }
+    }
+
+    if (it == g_vmas.end()) {
+        g_vmas.emplace_back(current);
+    }
+}
+
+static void print_header() {
+    const char* addr1 = g_show_addr ? "           start              end " : "";
+    const char* addr2 = g_show_addr ? "            addr             addr " : "";
+
+    printf("%s virtual                     shared   shared  private  private\n", addr1);
+    printf("%s    size      RSS      PSS    clean    dirty    clean    dirty     swap  swapPSS",
+           addr2);
+    if (!g_verbose && !g_show_addr) {
+        printf("   # ");
+    }
+    printf(" object\n");
+}
+
+static void print_divider() {
+    if (g_show_addr) {
+        printf("-------- -------- ");
+    }
+    printf("-------- -------- -------- -------- -------- -------- -------- -------- -------- ");
+    if (!g_verbose && !g_show_addr) {
+        printf("---- ");
+    }
+    printf("------------------------------\n");
+}
+
+static void print_vmainfo(const VmaInfo& v, bool total) {
+    if (g_show_addr) {
+        if (total) {
+            printf("                                  ");
+        } else {
+            printf("%16" PRIx64 " %16" PRIx64 " ", v.vma.start, v.vma.end);
+        }
+    }
+    printf("%8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64
+           " %8" PRIu64 " %8" PRIu64 " ",
+           v.vma.usage.vss, v.vma.usage.rss, v.vma.usage.pss, v.vma.usage.shared_clean,
+           v.vma.usage.shared_dirty, v.vma.usage.private_clean, v.vma.usage.private_dirty,
+           v.vma.usage.swap, v.vma.usage.swap_pss);
+    if (!g_verbose && !g_show_addr) {
+        printf("%4" PRIu32 " ", v.count);
+    }
+}
+
+static int showmap(void) {
+    if (!::android::meminfo::ForEachVmaFromFile(g_filename, collect_vma)) {
+        if (!g_quiet) {
+            fprintf(stderr, "Failed to parse file %s\n", g_filename.c_str());
+        }
+        return 1;
+    }
+
+    print_header();
+    print_divider();
+
+    for (const auto& v : g_vmas) {
+        g_total.vma.usage.vss += v.vma.usage.vss;
+        g_total.vma.usage.rss += v.vma.usage.rss;
+        g_total.vma.usage.pss += v.vma.usage.pss;
+
+        g_total.vma.usage.private_clean += v.vma.usage.private_clean;
+        g_total.vma.usage.private_dirty += v.vma.usage.private_dirty;
+        g_total.vma.usage.shared_clean += v.vma.usage.shared_clean;
+        g_total.vma.usage.shared_dirty += v.vma.usage.shared_dirty;
+
+        g_total.vma.usage.swap += v.vma.usage.swap;
+        g_total.vma.usage.swap_pss += v.vma.usage.swap_pss;
+        g_total.count += v.count;
+
+        if (g_terse && !(v.vma.usage.private_dirty || v.vma.usage.private_clean)) {
+            continue;
+        }
+
+        print_vmainfo(v, false);
+        printf("%s%s\n", v.vma.name.c_str(), v.is_bss ? " [bss]" : "");
+    }
+
+    print_divider();
+    print_header();
+    print_divider();
+
+    print_vmainfo(g_total, true);
+    printf("TOTAL\n");
+
+    return 0;
+}
+
+int main(int argc, char* argv[]) {
+    signal(SIGPIPE, SIG_IGN);
+    struct option longopts[] = {
+            {"help", no_argument, nullptr, 'h'},
+            {0, 0, nullptr, 0},
+    };
+
+    int opt;
+    while ((opt = getopt_long(argc, argv, "tvaqf:h", longopts, nullptr)) != -1) {
+        switch (opt) {
+            case 't':
+                g_terse = true;
+                break;
+            case 'a':
+                g_show_addr = true;
+                break;
+            case 'v':
+                g_verbose = true;
+                break;
+            case 'q':
+                g_quiet = true;
+                break;
+            case 'f':
+                g_filename = optarg;
+                break;
+            case 'h':
+                usage(EXIT_SUCCESS);
+            default:
+                usage(EXIT_FAILURE);
+        }
+    }
+
+    if (g_filename.empty()) {
+        if ((argc - 1) < optind) {
+            fprintf(stderr, "Invalid arguments: Must provide <pid> at the end\n");
+            usage(EXIT_FAILURE);
+        }
+
+        g_pid = atoi(argv[optind]);
+        if (g_pid <= 0) {
+            fprintf(stderr, "Invalid process id %s\n", argv[optind]);
+            usage(EXIT_FAILURE);
+        }
+
+        g_filename = ::android::base::StringPrintf("/proc/%d/smaps", g_pid);
+    }
+
+    g_merge_by_names = !g_verbose && !g_show_addr;
+    return showmap();
+}
diff --git a/libmeminfo/tools/wsstop.cpp b/libmeminfo/tools/wsstop.cpp
new file mode 100644
index 0000000..368d04e
--- /dev/null
+++ b/libmeminfo/tools/wsstop.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <meminfo/pageacct.h>
+#include <meminfo/procmeminfo.h>
+
+using ::android::meminfo::ProcMemInfo;
+using ::android::meminfo::Vma;
+
+// Global options
+static int32_t g_delay = 0;
+static int32_t g_total = 2;
+static pid_t g_pid = -1;
+
+[[noreturn]] static void usage(int exit_status) {
+    fprintf(stderr,
+            "%s [-d DELAY_BETWEEN_EACH_SAMPLE] [-n REFRESH_TOTAL] PID\n"
+            "-d\tdelay between each working set sample (default 0)\n"
+            "-n\ttotal number of refreshes before we exit (default 2)\n",
+            getprogname());
+
+    exit(exit_status);
+}
+
+static void print_header() {
+    const char* addr1 = "           start              end ";
+    const char* addr2 = "            addr             addr ";
+
+    printf("%s  virtual                        shared    shared   private   private\n", addr1);
+    printf("%s     size       RSS       PSS     clean     dirty     clean     dirty      swap   "
+           "swapPSS",
+           addr2);
+    printf(" object\n");
+}
+
+static void print_divider() {
+    printf("---------------- ---------------- ");
+    printf("--------- --------- --------- --------- --------- --------- --------- --------- "
+           "--------- ");
+    printf("------------------------------\n");
+}
+
+static void print_vma(const Vma& v) {
+    printf("%16" PRIx64 " %16" PRIx64 " ", v.start, v.end);
+    printf("%8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64
+           "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K ",
+           v.usage.vss / 1024, v.usage.rss / 1024, v.usage.pss / 1024, v.usage.shared_clean / 1024,
+           v.usage.shared_dirty / 1024, v.usage.private_clean / 1024, v.usage.private_dirty / 1024,
+           v.usage.swap / 1024, v.usage.swap_pss / 1024);
+    printf("%s\n", v.name.c_str());
+}
+
+static bool same_vma(const Vma& cur, const Vma& last) {
+    return (cur.start == last.start && cur.end == last.end && cur.name == last.name &&
+            cur.flags == last.flags && cur.offset == last.offset);
+}
+
+static Vma diff_vma_params(const Vma& cur, const Vma& last) {
+    Vma res;
+    res.usage.shared_clean = cur.usage.shared_clean > last.usage.shared_clean
+                                     ? cur.usage.shared_clean - last.usage.shared_clean
+                                     : 0;
+    res.usage.shared_dirty = cur.usage.shared_dirty > last.usage.shared_dirty
+                                     ? cur.usage.shared_dirty - last.usage.shared_dirty
+                                     : 0;
+    res.usage.private_clean = cur.usage.private_clean > last.usage.private_clean
+                                      ? cur.usage.private_clean - last.usage.private_clean
+                                      : 0;
+    res.usage.private_dirty = cur.usage.private_dirty > last.usage.private_dirty
+                                      ? cur.usage.private_dirty - last.usage.private_dirty
+                                      : 0;
+
+    res.usage.rss = cur.usage.rss > last.usage.rss ? cur.usage.rss - last.usage.rss : 0;
+    res.usage.pss = cur.usage.pss > last.usage.pss ? cur.usage.pss - last.usage.pss : 0;
+    res.usage.uss = cur.usage.uss > last.usage.uss ? cur.usage.uss - last.usage.uss : 0;
+    res.usage.swap = cur.usage.swap > last.usage.swap ? cur.usage.swap - last.usage.swap : 0;
+    res.usage.swap_pss =
+            cur.usage.swap_pss > last.usage.swap_pss ? cur.usage.swap_pss - last.usage.swap_pss : 0;
+
+    // set vma properties to the same as the current one.
+    res.start = cur.start;
+    res.end = cur.end;
+    res.offset = cur.offset;
+    res.flags = cur.flags;
+    res.name = cur.name;
+    return res;
+}
+
+static void diff_workingset(std::vector<Vma>& wss, std::vector<Vma>& old, std::vector<Vma>* res) {
+    res->clear();
+    auto vma_sorter = [](const Vma& a, const Vma& b) { return a.start < b.start; };
+    std::sort(wss.begin(), wss.end(), vma_sorter);
+    std::sort(old.begin(), old.end(), vma_sorter);
+    if (old.empty()) {
+        *res = wss;
+        return;
+    }
+
+    for (auto& i : wss) {
+        bool found_same_vma = false;
+        // TODO: This is highly inefficient, fix it if it takes
+        // too long. Worst case will be system_server
+        for (auto& j : old) {
+            if (same_vma(i, j)) {
+                res->emplace_back(diff_vma_params(i, j));
+                found_same_vma = true;
+                break;
+            }
+        }
+
+        if (!found_same_vma) {
+            res->emplace_back(i);
+        }
+    }
+
+    std::sort(res->begin(), res->end(), vma_sorter);
+    return;
+}
+
+static int workingset() {
+    std::vector<Vma> last_wss = {};
+    std::vector<Vma> diff_wss = {};
+    uint32_t nr_refresh = 0;
+
+    while (true) {
+        std::unique_ptr<ProcMemInfo> proc_mem = std::make_unique<ProcMemInfo>(g_pid, true);
+        std::vector<Vma> wss = proc_mem->MapsWithPageIdle();
+
+        diff_workingset(wss, last_wss, &diff_wss);
+        diff_wss.erase(std::remove_if(diff_wss.begin(), diff_wss.end(),
+                                      [](const auto& v) { return v.usage.rss == 0; }),
+                       diff_wss.end());
+        if ((nr_refresh % 5) == 0) {
+            print_header();
+            print_divider();
+        }
+
+        for (const auto& v : diff_wss) {
+            print_vma(v);
+        }
+
+        nr_refresh++;
+        if (nr_refresh == g_total) {
+            break;
+        }
+
+        last_wss = wss;
+        sleep(g_delay);
+        print_divider();
+    }
+
+    return 0;
+}
+
+int main(int argc, char* argv[]) {
+    struct option longopts[] = {
+            {"help", no_argument, nullptr, 'h'},
+            {0, 0, nullptr, 0},
+    };
+
+    int opt;
+    while ((opt = getopt_long(argc, argv, "d:n:h", longopts, nullptr)) != -1) {
+        switch (opt) {
+            case 'd':
+                g_delay = atoi(optarg);
+                break;
+            case 'n':
+                g_total = atoi(optarg);
+                break;
+            case 'h':
+                usage(EXIT_SUCCESS);
+            default:
+                usage(EXIT_FAILURE);
+        }
+    }
+
+    if ((argc - 1) < optind) {
+        fprintf(stderr, "Invalid arguments: Must provide <pid> at the end\n");
+        usage(EXIT_FAILURE);
+    }
+
+    g_pid = atoi(argv[optind]);
+    if (g_pid <= 0) {
+        fprintf(stderr, "Invalid process id %s\n", argv[optind]);
+        usage(EXIT_FAILURE);
+    }
+
+    if (!::android::meminfo::PageAcct::KernelHasPageIdle()) {
+        fprintf(stderr, "Missing support for Idle page tracking in the kernel\n");
+        return 0;
+    }
+
+    return workingset();
+}
diff --git a/libmeminfo/vts/Android.bp b/libmeminfo/vts/Android.bp
new file mode 100644
index 0000000..5a3a23b
--- /dev/null
+++ b/libmeminfo/vts/Android.bp
@@ -0,0 +1,20 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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: "vts_meminfo_test",
+    defaults: ["libmeminfo_defaults"],
+    srcs: ["vts_meminfo_test.cpp"],
+    static_libs: ["libmeminfo"],
+}
diff --git a/libmeminfo/vts/Android.mk b/libmeminfo/vts/Android.mk
new file mode 100644
index 0000000..62d68d9
--- /dev/null
+++ b/libmeminfo/vts/Android.mk
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 := VtsKernelMemInfoTest
+-include test/vts/tools/build/Android.host_config.mk
diff --git a/libmeminfo/vts/AndroidTest.xml b/libmeminfo/vts/AndroidTest.xml
new file mode 100644
index 0000000..530d16e
--- /dev/null
+++ b/libmeminfo/vts/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for VTS VtsKernelMemInfoTest.">
+    <option name="config-descriptor:metadata" key="plan" value="vts-kernel" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+        <option name="abort-on-push-failure" value="false"/>
+        <option name="push-group" value="HostDrivenTest.push"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+        <option name="test-module-name" value="VtsKernelMemInfoTest"/>
+        <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_meminfo_test/vts_meminfo_test" />
+        <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_meminfo_test/vts_meminfo_test" />
+        <option name="binary-test-type" value="gtest"/>
+        <option name="test-timeout" value="10m"/>
+    </test>
+</configuration>
diff --git a/libmeminfo/vts/vts_meminfo_test.cpp b/libmeminfo/vts/vts_meminfo_test.cpp
new file mode 100644
index 0000000..3193c31
--- /dev/null
+++ b/libmeminfo/vts/vts_meminfo_test.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <meminfo/procmeminfo.h>
+
+namespace android {
+namespace meminfo {
+
+// /proc/<pid>/smaps_rollup support is required.
+TEST(SmapsRollup, IsSupported) {
+    // Use init's pid for this test since it's the only known pid.
+    ASSERT_TRUE(IsSmapsRollupSupported(1));
+}
+
+}  // namespace meminfo
+}  // namespace android
diff --git a/libmemtrack/.clang-format b/libmemtrack/.clang-format
new file mode 120000
index 0000000..1af4f51
--- /dev/null
+++ b/libmemtrack/.clang-format
@@ -0,0 +1 @@
+../.clang-format-4
\ No newline at end of file
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
index 0955633..4e4554a 100644
--- a/libmemtrack/Android.bp
+++ b/libmemtrack/Android.bp
@@ -28,10 +28,11 @@
 
 cc_binary {
     name: "memtrack_test",
-    srcs: ["memtrack_test.c"],
+    srcs: ["memtrack_test.cpp"],
+    static_libs: ["libc++fs"],
     shared_libs: [
+        "libbase",
         "libmemtrack",
-        "libpagemap",
     ],
     cflags: [
         "-Wall",
diff --git a/libmemtrack/memtrack_test.c b/libmemtrack/memtrack_test.c
deleted file mode 100644
index 77c935e..0000000
--- a/libmemtrack/memtrack_test.c
+++ /dev/null
@@ -1,139 +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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
-#include <memtrack/memtrack.h>
-
-#include <pagemap/pagemap.h>
-
-#define DIV_ROUND_UP(x,y) (((x) + (y) - 1) / (y))
-
-static int getprocname(pid_t pid, char *buf, int len) {
-    char *filename;
-    FILE *f;
-    int rc = 0;
-    static const char* unknown_cmdline = "<unknown>";
-
-    if (len <= 0) {
-        return -1;
-    }
-
-    if (asprintf(&filename, "/proc/%d/cmdline", pid) < 0) {
-        rc = 1;
-        goto exit;
-    }
-
-    f = fopen(filename, "r");
-    if (f == NULL) {
-        rc = 2;
-        goto releasefilename;
-    }
-
-    if (fgets(buf, len, f) == NULL) {
-        rc = 3;
-        goto closefile;
-    }
-
-closefile:
-    (void) fclose(f);
-releasefilename:
-    free(filename);
-exit:
-    if (rc != 0) {
-        /*
-         * The process went away before we could read its process name. Try
-         * to give the user "<unknown>" here, but otherwise they get to look
-         * at a blank.
-         */
-        if (strlcpy(buf, unknown_cmdline, (size_t)len) >= (size_t)len) {
-            rc = 4;
-        }
-    }
-
-    return rc;
-}
-
-int main(int argc, char *argv[])
-{
-    int ret;
-    pm_kernel_t *ker;
-    size_t num_procs;
-    pid_t *pids;
-    struct memtrack_proc *p;
-    size_t i;
-
-    (void)argc;
-    (void)argv;
-
-    ret = pm_kernel_create(&ker);
-    if (ret) {
-        fprintf(stderr, "Error creating kernel interface -- "
-                        "does this kernel have pagemap?\n");
-        exit(EXIT_FAILURE);
-    }
-
-    ret = pm_kernel_pids(ker, &pids, &num_procs);
-    if (ret) {
-        fprintf(stderr, "Error listing processes.\n");
-        exit(EXIT_FAILURE);
-    }
-
-    p = memtrack_proc_new();
-    if (ret) {
-        fprintf(stderr, "failed to create memtrack process handle\n");
-        exit(EXIT_FAILURE);
-    }
-
-    for (i = 0; i < num_procs; i++) {
-        pid_t pid = pids[i];
-        char cmdline[256];
-        size_t v1;
-        size_t v2;
-        size_t v3;
-        size_t v4;
-        size_t v5;
-        size_t v6;
-
-        getprocname(pid, cmdline, (int)sizeof(cmdline));
-
-        ret = memtrack_proc_get(p, pid);
-        if (ret) {
-            fprintf(stderr, "failed to get memory info for pid %d: %s (%d)\n",
-                    pid, strerror(-ret), ret);
-            continue;
-        }
-
-        v1 = DIV_ROUND_UP(memtrack_proc_graphics_total(p), 1024);
-        v2 = DIV_ROUND_UP(memtrack_proc_graphics_pss(p), 1024);
-        v3 = DIV_ROUND_UP(memtrack_proc_gl_total(p), 1024);
-        v4 = DIV_ROUND_UP(memtrack_proc_gl_pss(p), 1024);
-        v5 = DIV_ROUND_UP(memtrack_proc_other_total(p), 1024);
-        v6 = DIV_ROUND_UP(memtrack_proc_other_pss(p), 1024);
-
-        if (v1 | v2 | v3 | v4 | v5 | v6) {
-            printf("%5d %6zu %6zu %6zu %6zu %6zu %6zu %s\n", pid,
-                   v1, v2, v3, v4, v5, v6, cmdline);
-        }
-    }
-
-    memtrack_proc_destroy(p);
-
-    return 0;
-}
diff --git a/libmemtrack/memtrack_test.cpp b/libmemtrack/memtrack_test.cpp
new file mode 100644
index 0000000..aeeaf24
--- /dev/null
+++ b/libmemtrack/memtrack_test.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <filesystem>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <memtrack/memtrack.h>
+
+#define DIV_ROUND_UP(x, y) (((x) + (y)-1) / (y))
+
+static void getprocname(pid_t pid, std::string* name) {
+    std::string fname = ::android::base::StringPrintf("/proc/%d/cmdline", pid);
+    if (!::android::base::ReadFileToString(fname, name)) {
+        fprintf(stderr, "Failed to read cmdline from: %s\n", fname.c_str());
+        *name = "<unknown>";
+    }
+}
+
+int main(int /* argc */, char** /* argv */) {
+    int ret;
+    struct memtrack_proc* p;
+    std::vector<pid_t> pids;
+
+    p = memtrack_proc_new();
+    if (p == nullptr) {
+        fprintf(stderr, "failed to create memtrack process handle\n");
+        exit(EXIT_FAILURE);
+    }
+
+    for (auto& de : std::filesystem::directory_iterator("/proc")) {
+        if (!std::filesystem::is_directory(de.status())) {
+            continue;
+        }
+
+        pid_t pid;
+        if (!::android::base::ParseInt(de.path().filename().string(), &pid)) {
+            continue;
+        }
+        pids.emplace_back(pid);
+    }
+
+    for (auto& pid : pids) {
+        size_t v1;
+        size_t v2;
+        size_t v3;
+        size_t v4;
+        size_t v5;
+        size_t v6;
+        std::string cmdline;
+
+        getprocname(pid, &cmdline);
+
+        ret = memtrack_proc_get(p, pid);
+        if (ret) {
+            fprintf(stderr, "failed to get memory info for pid %d: %s (%d)\n", pid, strerror(-ret),
+                    ret);
+            continue;
+        }
+
+        v1 = DIV_ROUND_UP(memtrack_proc_graphics_total(p), 1024);
+        v2 = DIV_ROUND_UP(memtrack_proc_graphics_pss(p), 1024);
+        v3 = DIV_ROUND_UP(memtrack_proc_gl_total(p), 1024);
+        v4 = DIV_ROUND_UP(memtrack_proc_gl_pss(p), 1024);
+        v5 = DIV_ROUND_UP(memtrack_proc_other_total(p), 1024);
+        v6 = DIV_ROUND_UP(memtrack_proc_other_pss(p), 1024);
+
+        if (v1 | v2 | v3 | v4 | v5 | v6) {
+            fprintf(stdout, "%5d %6zu %6zu %6zu %6zu %6zu %6zu %s\n", pid, v1, v2, v3, v4, v5, v6,
+                    cmdline.c_str());
+        }
+    }
+
+    memtrack_proc_destroy(p);
+
+    return ret;
+}
diff --git a/libmemunreachable/Allocator.cpp b/libmemunreachable/Allocator.cpp
index 213be17..1eb7e98 100644
--- a/libmemunreachable/Allocator.cpp
+++ b/libmemunreachable/Allocator.cpp
@@ -24,6 +24,7 @@
 
 #include <sys/cdefs.h>
 #include <sys/mman.h>
+#include <sys/prctl.h>
 
 #include <cmath>
 #include <cstddef>
@@ -35,7 +36,6 @@
 
 #include "Allocator.h"
 #include "LinkedList.h"
-#include "anon_vma_naming.h"
 
 namespace android {
 
@@ -153,10 +153,10 @@
     munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + size), map_size - size);
   }
 
-#define PR_SET_VMA 0x53564d41
-#define PR_SET_VMA_ANON_NAME 0
+#if defined(PR_SET_VMA)
   prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<uintptr_t>(ptr), size,
         "leak_detector_malloc");
+#endif
 
   return ptr;
 }
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index f872d0f..62a7266 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -22,6 +22,7 @@
 
 cc_library {
     name: "libmemunreachable",
+    vendor_available: true,
     defaults: ["libmemunreachable_defaults"],
     srcs: [
         "Allocator.cpp",
@@ -29,7 +30,6 @@
         "HeapWalker.cpp",
         "LeakFolding.cpp",
         "LeakPipe.cpp",
-        "LineBuffer.cpp",
         "MemUnreachable.cpp",
         "ProcessMappings.cpp",
         "PtracerThread.cpp",
@@ -38,6 +38,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.
@@ -48,10 +49,23 @@
     },
     export_include_dirs: ["include"],
     local_include_dirs: ["include"],
+    version_script: "libmemunreachable.map",
+}
+
+// Integration test that runs against the public API of libmemunreachable
+cc_test {
+    name: "memunreachable_test",
+    defaults: ["libmemunreachable_defaults"],
+    srcs: [
+        "tests/MemUnreachable_test.cpp",
+    ],
+    shared_libs: ["libmemunreachable"],
+
+    test_suites: ["device-tests"],
 }
 
 cc_test {
-    name: "memunreachable_test",
+    name: "memunreachable_unit_test",
     defaults: ["libmemunreachable_defaults"],
     host_supported: true,
     srcs: [
@@ -67,8 +81,9 @@
                 "tests/MemUnreachable_test.cpp",
                 "tests/ThreadCapture_test.cpp",
             ],
-            shared_libs: [
+            static_libs: [
                 "libmemunreachable",
+                "libc_malloc_debug_backtrace",
             ],
         },
         host: {
@@ -83,6 +98,8 @@
             enabled: false,
         },
     },
+
+    test_suites: ["device-tests"],
 }
 
 cc_test {
@@ -97,4 +114,5 @@
         "libhwbinder",
         "libutils",
     ],
+    test_suites: ["device-tests"],
 }
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
index 2403ad0..7cae048 100644
--- a/libmemunreachable/HeapWalker.cpp
+++ b/libmemunreachable/HeapWalker.cpp
@@ -35,6 +35,13 @@
     end = begin + 1;
   }
   Range range{begin, end};
+  if (valid_mappings_range_.end != 0 &&
+      (begin < valid_mappings_range_.begin || end > valid_mappings_range_.end)) {
+    MEM_LOG_ALWAYS_FATAL("allocation %p-%p is outside mapping range %p-%p",
+                         reinterpret_cast<void*>(begin), reinterpret_cast<void*>(end),
+                         reinterpret_cast<void*>(valid_mappings_range_.begin),
+                         reinterpret_cast<void*>(valid_mappings_range_.end));
+  }
   auto inserted = allocations_.insert(std::pair<Range, AllocationInfo>(range, AllocationInfo{}));
   if (inserted.second) {
     valid_allocations_range_.begin = std::min(valid_allocations_range_.begin, begin);
@@ -52,12 +59,19 @@
   }
 }
 
+// Sanitizers may consider certain memory inaccessible through certain pointers.
+// With MTE this will need to use unchecked instructions or disable tag checking globally.
+static uintptr_t ReadWordAtAddressUnsafe(uintptr_t word_ptr)
+    __attribute__((no_sanitize("address", "hwaddress"))) {
+  return *reinterpret_cast<uintptr_t*>(word_ptr);
+}
+
 bool HeapWalker::WordContainsAllocationPtr(uintptr_t word_ptr, Range* range, AllocationInfo** info) {
   walking_ptr_ = word_ptr;
   // This access may segfault if the process under test has done something strange,
   // for example mprotect(PROT_NONE) on a native heap page.  If so, it will be
   // caught and handled by mmaping a zero page over the faulting page.
-  uintptr_t value = *reinterpret_cast<uintptr_t*>(word_ptr);
+  uintptr_t value = ReadWordAtAddressUnsafe(word_ptr);
   walking_ptr_ = 0;
   if (value >= valid_allocations_range_.begin && value < valid_allocations_range_.end) {
     AllocationMap::iterator it = allocations_.find(Range{value, value + 1});
@@ -76,15 +90,22 @@
     Range range = to_do.back();
     to_do.pop_back();
 
+    walking_range_ = range;
     ForEachPtrInRange(range, [&](Range& ref_range, AllocationInfo* ref_info) {
       if (!ref_info->referenced_from_root) {
         ref_info->referenced_from_root = true;
         to_do.push_back(ref_range);
       }
     });
+    walking_range_ = Range{0, 0};
   }
 }
 
+void HeapWalker::Mapping(uintptr_t begin, uintptr_t end) {
+  valid_mappings_range_.begin = std::min(valid_mappings_range_.begin, begin);
+  valid_mappings_range_.end = std::max(valid_mappings_range_.end, end);
+}
+
 void HeapWalker::Root(uintptr_t begin, uintptr_t end) {
   roots_.push_back(Range{begin, end});
 }
@@ -113,6 +134,10 @@
 
   RecurseRoot(vals);
 
+  if (segv_page_count_ > 0) {
+    MEM_ALOGE("%zu pages skipped due to segfaults", segv_page_count_);
+  }
+
   return true;
 }
 
@@ -168,12 +193,20 @@
     handler.reset();
     return;
   }
-  MEM_ALOGW("failed to read page at %p, signal %d", si->si_addr, signal);
+  if (!segv_logged_) {
+    MEM_ALOGW("failed to read page at %p, signal %d", si->si_addr, signal);
+    if (walking_range_.begin != 0U) {
+      MEM_ALOGW("while walking range %p-%p", reinterpret_cast<void*>(walking_range_.begin),
+                reinterpret_cast<void*>(walking_range_.end));
+    }
+    segv_logged_ = true;
+  }
+  segv_page_count_++;
   if (!MapOverPage(si->si_addr)) {
     handler.reset();
   }
 }
 
-ScopedSignalHandler::SignalFn ScopedSignalHandler::handler_;
+Allocator<ScopedSignalHandler::SignalFnMap>::unique_ptr ScopedSignalHandler::handler_map_;
 
 }  // namespace android
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
index 5c7ec13..f00bcca 100644
--- a/libmemunreachable/HeapWalker.h
+++ b/libmemunreachable/HeapWalker.h
@@ -52,19 +52,30 @@
         allocation_bytes_(0),
         roots_(allocator),
         root_vals_(allocator),
-        segv_handler_(allocator),
-        walking_ptr_(0) {
+        sigsegv_handler_(allocator),
+        sigbus_handler_(allocator),
+        walking_ptr_(0),
+        walking_range_{0, 0},
+        segv_logged_(false),
+        segv_page_count_(0) {
     valid_allocations_range_.end = 0;
     valid_allocations_range_.begin = ~valid_allocations_range_.end;
+    valid_mappings_range_.end = 0;
+    valid_mappings_range_.begin = ~valid_allocations_range_.end;
 
-    segv_handler_.install(
+    sigsegv_handler_.install(
         SIGSEGV, [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
           this->HandleSegFault(handler, signal, siginfo, uctx);
         });
+    sigbus_handler_.install(
+        SIGBUS, [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
+          this->HandleSegFault(handler, signal, siginfo, uctx);
+        });
   }
 
   ~HeapWalker() {}
   bool Allocation(uintptr_t begin, uintptr_t end);
+  void Mapping(uintptr_t begin, uintptr_t end);
   void Root(uintptr_t begin, uintptr_t end);
   void Root(const allocator::vector<uintptr_t>& vals);
 
@@ -95,12 +106,17 @@
   AllocationMap allocations_;
   size_t allocation_bytes_;
   Range valid_allocations_range_;
+  Range valid_mappings_range_;
 
   allocator::vector<Range> roots_;
   allocator::vector<uintptr_t> root_vals_;
 
-  ScopedSignalHandler segv_handler_;
-  uintptr_t walking_ptr_;
+  ScopedSignalHandler sigsegv_handler_;
+  ScopedSignalHandler sigbus_handler_;
+  volatile uintptr_t walking_ptr_;
+  Range walking_range_;
+  bool segv_logged_;
+  size_t segv_page_count_;
 };
 
 template <class F>
diff --git a/libmemunreachable/LeakFolding.cpp b/libmemunreachable/LeakFolding.cpp
index 69f320c..074dc48 100644
--- a/libmemunreachable/LeakFolding.cpp
+++ b/libmemunreachable/LeakFolding.cpp
@@ -57,7 +57,7 @@
 }
 
 void LeakFolding::AccumulateLeaks(SCCInfo* dominator) {
-  std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_, [&](SCCInfo* scc) {
+  std::function<void(SCCInfo*)> walk([&](SCCInfo* scc) {
     if (scc->accumulator != dominator) {
       scc->accumulator = dominator;
       dominator->cuumulative_size += scc->size;
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 529a043..ce937fd 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -87,6 +87,11 @@
                                         const allocator::vector<Mapping>& mappings,
                                         const allocator::vector<uintptr_t>& refs) {
   MEM_ALOGI("searching process %d for allocations", pid_);
+
+  for (auto it = mappings.begin(); it != mappings.end(); it++) {
+    heap_walker_.Mapping(it->begin, it->end);
+  }
+
   allocator::vector<Mapping> heap_mappings{mappings};
   allocator::vector<Mapping> anon_mappings{mappings};
   allocator::vector<Mapping> globals_mappings{mappings};
@@ -212,6 +217,10 @@
   return ret == 0;
 }
 
+static bool is_sanitizer_mapping(const allocator::string& s) {
+  return s == "[anon:low shadow]" || s == "[anon:high shadow]" || has_prefix(s, "[anon:hwasan");
+}
+
 bool MemUnreachable::ClassifyMappings(const allocator::vector<Mapping>& mappings,
                                       allocator::vector<Mapping>& heap_mappings,
                                       allocator::vector<Mapping>& anon_mappings,
@@ -244,7 +253,7 @@
     } else if (mapping_name == "[anon:libc_malloc]") {
       // named malloc mapping
       heap_mappings.emplace_back(*it);
-    } else if (has_prefix(mapping_name, "/dev/ashmem/dalvik")) {
+    } else if (has_prefix(mapping_name, "[anon:dalvik-")) {
       // named dalvik heap mapping
       globals_mappings.emplace_back(*it);
     } else if (has_prefix(mapping_name, "[stack")) {
@@ -253,7 +262,8 @@
     } else if (mapping_name.size() == 0) {
       globals_mappings.emplace_back(*it);
     } else if (has_prefix(mapping_name, "[anon:") &&
-               mapping_name != "[anon:leak_detector_malloc]") {
+               mapping_name != "[anon:leak_detector_malloc]" &&
+               !is_sanitizer_mapping(mapping_name)) {
       // TODO(ccross): it would be nice to treat named anonymous mappings as
       // possible leaks, but naming something in a .bss or .data section makes
       // it impossible to distinguish them from mmaped and then named mappings.
@@ -270,6 +280,12 @@
 }
 
 bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit) {
+  if (info.version > 0) {
+    MEM_ALOGE("unsupported UnreachableMemoryInfo.version %zu in GetUnreachableMemory",
+              info.version);
+    return false;
+  }
+
   int parent_pid = getpid();
   int parent_tid = gettid();
 
diff --git a/libmemunreachable/ProcessMappings.cpp b/libmemunreachable/ProcessMappings.cpp
index 9a06870..8e1be4c 100644
--- a/libmemunreachable/ProcessMappings.cpp
+++ b/libmemunreachable/ProcessMappings.cpp
@@ -14,21 +14,32 @@
  * limitations under the License.
  */
 
+#include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <string.h>
+#include <sys/types.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, ino_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 +47,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 61a1d24..d2fca49 100644
--- a/libmemunreachable/PtracerThread.cpp
+++ b/libmemunreachable/PtracerThread.cpp
@@ -24,6 +24,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
+#include <sys/prctl.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
 #include <sys/wait.h>
@@ -32,7 +33,6 @@
 #include "android-base/macros.h"
 
 #include "PtracerThread.h"
-#include "anon_vma_naming.h"
 #include "log.h"
 
 namespace android {
diff --git a/libmemunreachable/README.md b/libmemunreachable/README.md
index ae8fa94..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)` ####
diff --git a/libmemunreachable/ScopedPipe.h b/libmemunreachable/ScopedPipe.h
index adabfd8..b9dead5 100644
--- a/libmemunreachable/ScopedPipe.h
+++ b/libmemunreachable/ScopedPipe.h
@@ -33,12 +33,12 @@
   }
   ~ScopedPipe() { Close(); }
 
-  ScopedPipe(ScopedPipe&& other) {
+  ScopedPipe(ScopedPipe&& other) noexcept {
     SetReceiver(other.ReleaseReceiver());
     SetSender(other.ReleaseSender());
   }
 
-  ScopedPipe& operator=(ScopedPipe&& other) {
+  ScopedPipe& operator=(ScopedPipe&& other) noexcept {
     SetReceiver(other.ReleaseReceiver());
     SetSender(other.ReleaseSender());
     return *this;
diff --git a/libmemunreachable/ScopedSignalHandler.h b/libmemunreachable/ScopedSignalHandler.h
index ff53fad..ef4473f 100644
--- a/libmemunreachable/ScopedSignalHandler.h
+++ b/libmemunreachable/ScopedSignalHandler.h
@@ -24,6 +24,7 @@
 
 #include "android-base/macros.h"
 
+#include "Allocator.h"
 #include "log.h"
 
 namespace android {
@@ -32,18 +33,29 @@
  public:
   using Fn = std::function<void(ScopedSignalHandler&, int, siginfo_t*, void*)>;
 
-  explicit ScopedSignalHandler(Allocator<Fn> allocator) : allocator_(allocator), signal_(-1) {}
+  explicit ScopedSignalHandler(Allocator<ScopedSignalHandler> allocator) : signal_(-1) {
+    if (handler_map_ == nullptr) {
+      Allocator<SignalFnMap> map_allocator = allocator;
+      handler_map_ = map_allocator.make_unique(allocator);
+    }
+  }
   ~ScopedSignalHandler() { reset(); }
 
   template <class F>
   void install(int signal, F&& f) {
     if (signal_ != -1) MEM_LOG_ALWAYS_FATAL("ScopedSignalHandler already installed");
 
-    handler_ = SignalFn(std::allocator_arg, allocator_,
-                        [=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
+    if (handler_map_->find(signal) != handler_map_->end()) {
+      MEM_LOG_ALWAYS_FATAL("ScopedSignalHandler already installed for %d", signal);
+    }
+
+    (*handler_map_)[signal] =
+        SignalFn([=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
 
     struct sigaction act {};
-    act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) { handler_(signal, si, uctx); };
+    act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) {
+      ((*handler_map_)[signal])(signal, si, uctx);
+    };
     act.sa_flags = SA_SIGINFO;
 
     int ret = sigaction(signal, &act, &old_act_);
@@ -60,20 +72,22 @@
       if (ret < 0) {
         MEM_ALOGE("failed to uninstall segfault handler");
       }
-      handler_ = SignalFn{};
+
+      handler_map_->erase(signal_);
+      if (handler_map_->empty()) {
+        handler_map_.reset();
+      }
       signal_ = -1;
     }
   }
 
  private:
   using SignalFn = std::function<void(int, siginfo_t*, void*)>;
+  using SignalFnMap = allocator::unordered_map<int, SignalFn>;
   DISALLOW_COPY_AND_ASSIGN(ScopedSignalHandler);
-  Allocator<Fn> allocator_;
   int signal_;
   struct sigaction old_act_;
-  // TODO(ccross): to support multiple ScopedSignalHandlers handler_ would need
-  // to be a static map of signals to handlers, but allocated with Allocator.
-  static SignalFn handler_;
+  static Allocator<SignalFnMap>::unique_ptr handler_map_;
 };
 
 }  // namespace android
diff --git a/libmemunreachable/Tarjan.h b/libmemunreachable/Tarjan.h
index 355679f..f3ab652 100644
--- a/libmemunreachable/Tarjan.h
+++ b/libmemunreachable/Tarjan.h
@@ -38,7 +38,7 @@
 
   Node(T* ptr, Allocator<Node> allocator)
       : references_in(allocator), references_out(allocator), ptr(ptr){};
-  Node(Node&& rhs) = default;
+  Node(Node&& rhs) noexcept = default;
   void Edge(Node<T>* ref) {
     references_out.emplace(ref);
     ref->references_in.emplace(this);
diff --git a/libmemunreachable/anon_vma_naming.h b/libmemunreachable/anon_vma_naming.h
deleted file mode 100644
index fb31e41..0000000
--- a/libmemunreachable/anon_vma_naming.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 LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
-#define LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
-
-#include <sys/prctl.h>
-
-#define PR_SET_VMA 0x53564d41
-#define PR_SET_VMA_ANON_NAME 0
-
-#endif  // LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
diff --git a/libmemunreachable/include/memunreachable/memunreachable.h b/libmemunreachable/include/memunreachable/memunreachable.h
index c028eab..011443f 100644
--- a/libmemunreachable/include/memunreachable/memunreachable.h
+++ b/libmemunreachable/include/memunreachable/memunreachable.h
@@ -28,38 +28,45 @@
 namespace android {
 
 struct Leak {
-  uintptr_t begin;
-  size_t size;
+  uintptr_t begin = 0;
+  size_t size = 0;
 
-  size_t referenced_count;
-  size_t referenced_size;
+  size_t referenced_count = 0;
+  size_t referenced_size = 0;
 
-  size_t similar_count;
-  size_t similar_size;
-  size_t similar_referenced_count;
-  size_t similar_referenced_size;
+  size_t similar_count = 0;
+  size_t similar_size = 0;
+  size_t similar_referenced_count = 0;
+  size_t similar_referenced_size = 0;
 
-  size_t total_size;
+  size_t total_size = 0;
 
   static const size_t contents_length = 32;
-  char contents[contents_length];
+  char contents[contents_length] = {};
 
   struct Backtrace {
-    size_t num_frames;
+    size_t num_frames = 0;
 
     static const size_t max_frames = 16;
-    uintptr_t frames[max_frames];
+    uintptr_t frames[max_frames] = {};
+
+    size_t reserved[8] = {};
   } backtrace;
 
+  size_t reserved[8] = {};
+
   std::string ToString(bool log_contents) const;
 };
 
 struct UnreachableMemoryInfo {
   std::vector<Leak> leaks;
-  size_t num_leaks;
-  size_t leak_bytes;
-  size_t num_allocations;
-  size_t allocation_bytes;
+  size_t num_leaks = 0;
+  size_t leak_bytes = 0;
+  size_t num_allocations = 0;
+  size_t allocation_bytes = 0;
+
+  size_t version = 0;  // Must be 0
+  size_t reserved[8] = {};
 
   UnreachableMemoryInfo() {}
   ~UnreachableMemoryInfo();
diff --git a/libmemunreachable/libmemunreachable.map b/libmemunreachable/libmemunreachable.map
new file mode 100644
index 0000000..0d0d954
--- /dev/null
+++ b/libmemunreachable/libmemunreachable.map
@@ -0,0 +1,13 @@
+LIBMEMUNREACHABLE {
+  global:
+    LogUnreachableMemory;
+    NoLeaks;
+    extern "C++" {
+      android::GetUnreachableMemory*;
+      android::GetUnreachableMemoryString*;
+      android::Leak::*;
+      android::UnreachableMemoryInfo::*;
+    };
+  local:
+    *;
+};
diff --git a/libmemunreachable/tests/HeapWalker_test.cpp b/libmemunreachable/tests/HeapWalker_test.cpp
index 84a0ec6..9610cd6 100644
--- a/libmemunreachable/tests/HeapWalker_test.cpp
+++ b/libmemunreachable/tests/HeapWalker_test.cpp
@@ -73,6 +73,24 @@
   ASSERT_FALSE(heap_walker.Allocation(2, 3));
 }
 
+TEST_F(HeapWalkerTest, mapping) {
+  HeapWalker heap_walker(heap_);
+  heap_walker.Mapping(2, 3);
+  heap_walker.Mapping(4, 5);
+  ASSERT_TRUE(heap_walker.Allocation(2, 3));
+  ASSERT_TRUE(heap_walker.Allocation(4, 5));
+  // space between mappings is not checked, but could be in the future
+  ASSERT_TRUE(heap_walker.Allocation(3, 4));
+
+  // re-enable malloc, ASSERT_DEATH may allocate
+  disable_malloc_.Enable();
+  ASSERT_DEATH({ heap_walker.Allocation(1, 2); }, "0x1-0x2.*outside.*0x2-0x5");
+  ASSERT_DEATH({ heap_walker.Allocation(1, 3); }, "0x1-0x3.*outside.*0x2-0x5");
+  ASSERT_DEATH({ heap_walker.Allocation(4, 6); }, "0x4-0x6.*outside.*0x2-0x5");
+  ASSERT_DEATH({ heap_walker.Allocation(5, 6); }, "0x5-0x6.*outside.*0x2-0x5");
+  ASSERT_DEATH({ heap_walker.Allocation(1, 6); }, "0x1-0x6.*outside.*0x2-0x5");
+}
+
 #define buffer_begin(buffer) reinterpret_cast<uintptr_t>(buffer)
 #define buffer_end(buffer) (reinterpret_cast<uintptr_t>(buffer) + sizeof(buffer))
 
diff --git a/libmemunreachable/tests/MemUnreachable_test.cpp b/libmemunreachable/tests/MemUnreachable_test.cpp
index bba0c6d..9cb1623 100644
--- a/libmemunreachable/tests/MemUnreachable_test.cpp
+++ b/libmemunreachable/tests/MemUnreachable_test.cpp
@@ -47,7 +47,8 @@
 
 // Trick the compiler into thinking a value on the stack is still referenced.
 static void Ref(void** ptr) {
-  write(0, ptr, 0);
+  void** volatile storage;
+  storage = ptr;
 }
 
 class MemunreachableTest : public ::testing::Test {
@@ -264,4 +265,12 @@
   ASSERT_TRUE(LogUnreachableMemory(true, 100));
 }
 
+TEST_F(MemunreachableTest, version) {
+  UnreachableMemoryInfo info;
+  info.version = 1;
+
+  ASSERT_FALSE(GetUnreachableMemory(info));
+  ASSERT_EQ(0U, info.leaks.size());
+}
+
 }  // namespace android
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 e6e17ce..7d7554b 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -11,7 +11,11 @@
 
     export_include_dirs: ["include"],
     local_include_dirs: ["include"],
-    shared_libs: ["liblog"],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libstatssocket",
+    ],
     whole_static_libs: ["libgtest_prod"],
 
     cflags: [
@@ -23,17 +27,11 @@
 
 // metricslogger shared library
 // -----------------------------------------------------------------------------
-cc_library_shared {
+cc_library {
     name: "libmetricslogger",
     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"],
+    export_shared_lib_headers: ["libstatssocket"],
 }
 
 // metricslogger shared library, debug
@@ -54,12 +52,12 @@
 // -----------------------------------------------------------------------------
 cc_test {
     name: "metricslogger_tests",
+    isolated: true,
     defaults: ["metricslogger_defaults"],
     shared_libs: [
         "libbase",
         "libmetricslogger_debug",
     ],
-    static_libs: ["libBionicGtestMain"],
     srcs: [
         "metrics_logger_test.cpp",
     ],
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
index 88575be..71c04a6 100644
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -15,6 +15,7 @@
  */
 
 #include <log/log_event_list.h>
+#include <stats_event_list.h>
 #include <cstdint>
 #include <string>
 
@@ -43,6 +44,7 @@
 class ComplexEventLogger {
   private:
     android_log_event_list logger;
+    stats_event_list stats_logger;
 
   public:
     // Create a complex event with category|category|.
diff --git a/libmetricslogger/metrics_logger.cpp b/libmetricslogger/metrics_logger.cpp
index 6a32153..2a1b137 100644
--- a/libmetricslogger/metrics_logger.cpp
+++ b/libmetricslogger/metrics_logger.cpp
@@ -18,11 +18,15 @@
 
 #include <cstdlib>
 
+#include <android-base/chrono_utils.h>
 #include <log/event_tag_map.h>
-#include <log/log_event_list.h>
+
+using namespace android;
 
 namespace {
 
+const static int kStatsEventTag = 1937006964;
+const static int kKeyValuePairAtomId = 83;
 #ifdef __ANDROID__
 EventTagMap* kEventTagMap = android_openEventTagMap(nullptr);
 const int kSysuiMultiActionTag = android_lookupEventTagNum(
@@ -32,6 +36,12 @@
 const int kSysuiMultiActionTag = 0;
 #endif
 
+int64_t getElapsedTimeNanoSinceBoot() {
+    return std::chrono::duration_cast<std::chrono::nanoseconds>(
+                   android::base::boot_clock::now().time_since_epoch())
+            .count();
+}
+
 }  // namespace
 
 namespace android {
@@ -42,6 +52,12 @@
     android_log_event_list log(kSysuiMultiActionTag);
     log << LOGBUILDER_CATEGORY << LOGBUILDER_HISTOGRAM << LOGBUILDER_NAME << event
         << LOGBUILDER_BUCKET << data << LOGBUILDER_VALUE << 1 << LOG_ID_EVENTS;
+
+    stats_event_list stats_log(kStatsEventTag);
+    stats_log << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
+              << LOGBUILDER_HISTOGRAM << LOGBUILDER_NAME << event << LOGBUILDER_BUCKET << data
+              << LOGBUILDER_VALUE << 1;
+    stats_log.write(LOG_ID_STATS);
 }
 
 // Mirror com.android.internal.logging.MetricsLogger#count().
@@ -49,6 +65,11 @@
     android_log_event_list log(kSysuiMultiActionTag);
     log << LOGBUILDER_CATEGORY << LOGBUILDER_COUNTER << LOGBUILDER_NAME << name << LOGBUILDER_VALUE
         << val << LOG_ID_EVENTS;
+
+    stats_event_list stats_log(kStatsEventTag);
+    stats_log << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
+              << LOGBUILDER_COUNTER << LOGBUILDER_NAME << name << LOGBUILDER_VALUE << val;
+    stats_log.write(LOG_ID_STATS);
 }
 
 // Mirror com.android.internal.logging.MetricsLogger#action().
@@ -56,34 +77,48 @@
     android_log_event_list log(kSysuiMultiActionTag);
     log << LOGBUILDER_CATEGORY << category << LOGBUILDER_TYPE << TYPE_ACTION
         << field << value << LOG_ID_EVENTS;
+
+    stats_event_list stats_log(kStatsEventTag);
+    stats_log << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
+              << category << LOGBUILDER_TYPE << TYPE_ACTION << field << value;
+    stats_log.write(LOG_ID_STATS);
 }
 
-ComplexEventLogger::ComplexEventLogger(int category) : logger(kSysuiMultiActionTag) {
+ComplexEventLogger::ComplexEventLogger(int category)
+    : logger(kSysuiMultiActionTag), stats_logger(kStatsEventTag) {
     logger << LOGBUILDER_CATEGORY << category;
+    stats_logger << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
+                 << category;
 }
 
 void ComplexEventLogger::SetPackageName(const std::string& package_name) {
     logger << LOGBUILDER_PACKAGENAME << package_name;
+    stats_logger << LOGBUILDER_PACKAGENAME << package_name;
 }
 
 void ComplexEventLogger::AddTaggedData(int tag, int32_t value) {
     logger << tag << value;
+    stats_logger << tag << value;
 }
 
 void ComplexEventLogger::AddTaggedData(int tag, const std::string& value) {
     logger << tag << value;
+    stats_logger << tag << value;
 }
 
 void ComplexEventLogger::AddTaggedData(int tag, int64_t value) {
     logger << tag << value;
+    stats_logger << tag << value;
 }
 
 void ComplexEventLogger::AddTaggedData(int tag, float value) {
     logger << tag << value;
+    stats_logger << tag << value;
 }
 
 void ComplexEventLogger::Record() {
     logger << LOG_ID_EVENTS;
+    stats_logger.write(LOG_ID_STATS);
 }
 
 }  // namespace metricslogger
diff --git a/libmodprobe/Android.bp b/libmodprobe/Android.bp
new file mode 100644
index 0000000..a2824d1
--- /dev/null
+++ b/libmodprobe/Android.bp
@@ -0,0 +1,30 @@
+cc_library_static {
+    name: "libmodprobe",
+    cflags: [
+        "-Werror",
+    ],
+    recovery_available: true,
+    srcs: [
+        "libmodprobe.cpp",
+        "libmodprobe_ext.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+    export_include_dirs: ["include/"],
+}
+
+cc_test {
+    name: "libmodprobe_tests",
+    cflags: ["-Werror"],
+    shared_libs: [
+        "libbase",
+    ],
+    local_include_dirs: ["include/"],
+    srcs: [
+        "libmodprobe_test.cpp",
+        "libmodprobe.cpp",
+        "libmodprobe_ext_test.cpp",
+    ],
+    test_suites: ["device-tests"],
+}
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
new file mode 100644
index 0000000..0ec766a
--- /dev/null
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -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.
+ */
+
+#pragma once
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+class Modprobe {
+  public:
+    Modprobe(const std::vector<std::string>&);
+
+    bool LoadListedModules();
+    bool LoadWithAliases(const std::string& module_name, bool strict);
+
+  private:
+    std::string MakeCanonical(const std::string& module_path);
+    bool InsmodWithDeps(const std::string& module_name);
+    bool Insmod(const std::string& path_name);
+    std::vector<std::string> GetDependencies(const std::string& module);
+    bool ModuleExists(const std::string& module_name);
+
+    bool ParseDepCallback(const std::string& base_path, const std::vector<std::string>& args);
+    bool ParseAliasCallback(const std::vector<std::string>& args);
+    bool ParseSoftdepCallback(const std::vector<std::string>& args);
+    bool ParseLoadCallback(const std::vector<std::string>& args);
+    bool ParseOptionsCallback(const std::vector<std::string>& args);
+    void ParseCfg(const std::string& cfg, std::function<bool(const std::vector<std::string>&)> f);
+
+    std::vector<std::pair<std::string, std::string>> module_aliases_;
+    std::unordered_map<std::string, std::vector<std::string>> module_deps_;
+    std::vector<std::pair<std::string, std::string>> module_pre_softdep_;
+    std::vector<std::pair<std::string, std::string>> module_post_softdep_;
+    std::vector<std::string> module_load_;
+    std::unordered_map<std::string, std::string> module_options_;
+};
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
new file mode 100644
index 0000000..01cf2e3
--- /dev/null
+++ b/libmodprobe/libmodprobe.cpp
@@ -0,0 +1,321 @@
+/*
+ * 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 <modprobe/modprobe.h>
+
+#include <fnmatch.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+
+#include <algorithm>
+#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 <android-base/unique_fd.h>
+
+std::string Modprobe::MakeCanonical(const std::string& module_path) {
+    auto start = module_path.find_last_of('/');
+    if (start == std::string::npos) {
+        start = 0;
+    } else {
+        start += 1;
+    }
+    auto end = module_path.size();
+    if (android::base::EndsWith(module_path, ".ko")) {
+        end -= 3;
+    }
+    if ((end - start) <= 1) {
+        LOG(ERROR) << "malformed module name: " << module_path;
+        return "";
+    }
+    std::string module_name = module_path.substr(start, end - start);
+    // module names can have '-', but their file names will have '_'
+    std::replace(module_name.begin(), module_name.end(), '-', '_');
+    return module_name;
+}
+
+bool Modprobe::ParseDepCallback(const std::string& base_path,
+                                const std::vector<std::string>& args) {
+    std::vector<std::string> deps;
+    std::string prefix = "";
+
+    // Set first item as our modules path
+    std::string::size_type pos = args[0].find(':');
+    if (args[0][0] != '/') {
+        prefix = base_path + "/";
+    }
+    if (pos != std::string::npos) {
+        deps.emplace_back(prefix + args[0].substr(0, pos));
+    } else {
+        LOG(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) {
+        if ((*arg)[0] != '/') {
+            prefix = base_path + "/";
+        } else {
+            prefix = "";
+        }
+        deps.push_back(prefix + *arg);
+    }
+
+    std::string canonical_name = MakeCanonical(args[0].substr(0, pos));
+    if (canonical_name.empty()) {
+        return false;
+    }
+    this->module_deps_[canonical_name] = deps;
+
+    return true;
+}
+
+bool Modprobe::ParseAliasCallback(const std::vector<std::string>& args) {
+    auto it = args.begin();
+    const std::string& type = *it++;
+
+    if (type != "alias") {
+        LOG(ERROR) << "non-alias line encountered in modules.alias, found " << type;
+        return false;
+    }
+
+    if (args.size() != 3) {
+        LOG(ERROR) << "alias lines in modules.alias must have 3 entries, not " << args.size();
+        return false;
+    }
+
+    const std::string& alias = *it++;
+    const std::string& module_name = *it++;
+    this->module_aliases_.emplace_back(alias, module_name);
+
+    return true;
+}
+
+bool Modprobe::ParseSoftdepCallback(const std::vector<std::string>& args) {
+    auto it = args.begin();
+    const std::string& type = *it++;
+    std::string state = "";
+
+    if (type != "softdep") {
+        LOG(ERROR) << "non-softdep line encountered in modules.softdep, found " << type;
+        return false;
+    }
+
+    if (args.size() < 4) {
+        LOG(ERROR) << "softdep lines in modules.softdep must have at least 4 entries";
+        return false;
+    }
+
+    const std::string& module = *it++;
+    while (it != args.end()) {
+        const std::string& token = *it++;
+        if (token == "pre:" || token == "post:") {
+            state = token;
+            continue;
+        }
+        if (state == "") {
+            LOG(ERROR) << "malformed modules.softdep at token " << token;
+            return false;
+        }
+        if (state == "pre:") {
+            this->module_pre_softdep_.emplace_back(module, token);
+        } else {
+            this->module_post_softdep_.emplace_back(module, token);
+        }
+    }
+
+    return true;
+}
+
+bool Modprobe::ParseLoadCallback(const std::vector<std::string>& args) {
+    auto it = args.begin();
+    const std::string& module = *it++;
+
+    const std::string& canonical_name = MakeCanonical(module);
+    if (canonical_name.empty()) {
+        return false;
+    }
+    this->module_load_.emplace_back(canonical_name);
+
+    return true;
+}
+
+bool Modprobe::ParseOptionsCallback(const std::vector<std::string>& args) {
+    auto it = args.begin();
+    const std::string& type = *it++;
+
+    if (type != "options") {
+        LOG(ERROR) << "non-options line encountered in modules.options";
+        return false;
+    }
+
+    if (args.size() < 2) {
+        LOG(ERROR) << "lines in modules.options must have at least 2 entries, not " << args.size();
+        return false;
+    }
+
+    const std::string& module = *it++;
+    std::string options = "";
+
+    const std::string& canonical_name = MakeCanonical(module);
+    if (canonical_name.empty()) {
+        return false;
+    }
+
+    while (it != args.end()) {
+        options += *it++;
+        if (it != args.end()) {
+            options += " ";
+        }
+    }
+
+    auto [unused, inserted] = this->module_options_.emplace(canonical_name, options);
+    if (!inserted) {
+        LOG(ERROR) << "multiple options lines present for module " << module;
+        return false;
+    }
+    return true;
+}
+
+void Modprobe::ParseCfg(const std::string& cfg,
+                        std::function<bool(const std::vector<std::string>&)> f) {
+    std::string cfg_contents;
+    if (!android::base::ReadFileToString(cfg, &cfg_contents, false)) {
+        return;
+    }
+
+    std::vector<std::string> lines = android::base::Split(cfg_contents, "\n");
+    for (const std::string line : lines) {
+        if (line.empty() || line[0] == '#') {
+            continue;
+        }
+        const std::vector<std::string> args = android::base::Split(line, " ");
+        if (args.empty()) continue;
+        f(args);
+    }
+    return;
+}
+
+Modprobe::Modprobe(const std::vector<std::string>& base_paths) {
+    using namespace std::placeholders;
+
+    for (const auto& base_path : base_paths) {
+        auto alias_callback = std::bind(&Modprobe::ParseAliasCallback, this, _1);
+        ParseCfg(base_path + "/modules.alias", alias_callback);
+
+        auto dep_callback = std::bind(&Modprobe::ParseDepCallback, this, base_path, _1);
+        ParseCfg(base_path + "/modules.dep", dep_callback);
+
+        auto softdep_callback = std::bind(&Modprobe::ParseSoftdepCallback, this, _1);
+        ParseCfg(base_path + "/modules.softdep", softdep_callback);
+
+        auto load_callback = std::bind(&Modprobe::ParseLoadCallback, this, _1);
+        ParseCfg(base_path + "/modules.load", load_callback);
+
+        auto options_callback = std::bind(&Modprobe::ParseOptionsCallback, this, _1);
+        ParseCfg(base_path + "/modules.options", options_callback);
+    }
+}
+
+std::vector<std::string> Modprobe::GetDependencies(const std::string& module) {
+    auto it = module_deps_.find(module);
+    if (it == module_deps_.end()) {
+        return {};
+    }
+    return it->second;
+}
+
+bool Modprobe::InsmodWithDeps(const std::string& module_name) {
+    if (module_name.empty()) {
+        LOG(ERROR) << "Need valid module name, given: " << module_name;
+        return false;
+    }
+
+    auto dependencies = GetDependencies(module_name);
+    if (dependencies.empty()) {
+        LOG(ERROR) << "Module " << module_name << " not in dependency file";
+        return false;
+    }
+
+    // load module dependencies in reverse order
+    for (auto dep = dependencies.rbegin(); dep != dependencies.rend() - 1; ++dep) {
+        const std::string& canonical_name = MakeCanonical(*dep);
+        if (canonical_name.empty()) {
+            return false;
+        }
+        if (!LoadWithAliases(canonical_name, true)) {
+            return false;
+        }
+    }
+
+    // try to load soft pre-dependencies
+    for (const auto& [module, softdep] : module_pre_softdep_) {
+        if (module_name == module) {
+            LoadWithAliases(softdep, false);
+        }
+    }
+
+    // load target module itself with args
+    if (!Insmod(dependencies[0])) {
+        return false;
+    }
+
+    // try to load soft post-dependencies
+    for (const auto& [module, softdep] : module_post_softdep_) {
+        if (module_name == module) {
+            LoadWithAliases(softdep, false);
+        }
+    }
+
+    return true;
+}
+
+bool Modprobe::LoadWithAliases(const std::string& module_name, bool strict) {
+    std::set<std::string> modules_to_load = {module_name};
+    bool module_loaded = false;
+
+    // use aliases to expand list of modules to load (multiple modules
+    // may alias themselves to the requested name)
+    for (const auto& [alias, aliased_module] : module_aliases_) {
+        if (fnmatch(alias.c_str(), module_name.c_str(), 0) != 0) continue;
+        modules_to_load.emplace(aliased_module);
+    }
+
+    // attempt to load all modules aliased to this name
+    for (const auto& module : modules_to_load) {
+        if (!ModuleExists(module)) continue;
+        if (InsmodWithDeps(module)) module_loaded = true;
+    }
+
+    if (strict && !module_loaded) {
+        LOG(ERROR) << "LoadWithAliases did not find a module for " << module_name;
+        return false;
+    }
+    return true;
+}
+
+bool Modprobe::LoadListedModules() {
+    for (const auto& module : module_load_) {
+        if (!LoadWithAliases(module, true)) {
+            return false;
+        }
+    }
+    return true;
+}
diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp
new file mode 100644
index 0000000..5f3a04d
--- /dev/null
+++ b/libmodprobe/libmodprobe_ext.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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 <sys/stat.h>
+#include <sys/syscall.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include <modprobe/modprobe.h>
+
+bool Modprobe::Insmod(const std::string& path_name) {
+    android::base::unique_fd fd(
+            TEMP_FAILURE_RETRY(open(path_name.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+    if (fd == -1) {
+        LOG(ERROR) << "Could not open module '" << path_name << "'";
+        return false;
+    }
+
+    std::string options = "";
+    auto options_iter = module_options_.find(MakeCanonical(path_name));
+    if (options_iter != module_options_.end()) {
+        options = options_iter->second;
+    }
+
+    LOG(INFO) << "Loading module " << path_name << " with args \"" << options << "\"";
+    int ret = syscall(__NR_finit_module, fd.get(), options.c_str(), 0);
+    if (ret != 0) {
+        if (errno == EEXIST) {
+            // Module already loaded
+            return true;
+        }
+        LOG(ERROR) << "Failed to insmod '" << path_name << "' with args '" << options << "'";
+        return false;
+    }
+
+    LOG(INFO) << "Loaded kernel module " << path_name;
+    return true;
+}
+
+bool Modprobe::ModuleExists(const std::string& module_name) {
+    struct stat fileStat;
+    auto deps = GetDependencies(module_name);
+    if (deps.empty()) {
+        // missing deps can happen in the case of an alias
+        return false;
+    }
+    if (stat(deps.front().c_str(), &fileStat)) {
+        return false;
+    }
+    if (!S_ISREG(fileStat.st_mode)) {
+        return false;
+    }
+    return true;
+}
diff --git a/libmodprobe/libmodprobe_ext_test.cpp b/libmodprobe/libmodprobe_ext_test.cpp
new file mode 100644
index 0000000..0f073cb
--- /dev/null
+++ b/libmodprobe/libmodprobe_ext_test.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/stat.h>
+#include <sys/syscall.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+#include <modprobe/modprobe.h>
+
+#include "libmodprobe_test.h"
+
+bool Modprobe::Insmod(const std::string& path_name) {
+    auto deps = GetDependencies(MakeCanonical(path_name));
+    if (deps.empty()) {
+        return false;
+    }
+    if (std::find(test_modules.begin(), test_modules.end(), deps.front()) == test_modules.end()) {
+        return false;
+    }
+    for (auto it = modules_loaded.begin(); it != modules_loaded.end(); ++it) {
+        if (android::base::StartsWith(*it, path_name)) {
+            return true;
+        }
+    }
+    std::string options;
+    auto options_iter = module_options_.find(MakeCanonical(path_name));
+    if (options_iter != module_options_.end()) {
+        options = " " + options_iter->second;
+    }
+    modules_loaded.emplace_back(path_name + options);
+    return true;
+}
+
+bool Modprobe::ModuleExists(const std::string& module_name) {
+    auto deps = GetDependencies(module_name);
+    if (deps.empty()) {
+        // missing deps can happen in the case of an alias
+        return false;
+    }
+    return std::find(test_modules.begin(), test_modules.end(), deps.front()) != test_modules.end();
+}
diff --git a/libmodprobe/libmodprobe_test.cpp b/libmodprobe/libmodprobe_test.cpp
new file mode 100644
index 0000000..481658d
--- /dev/null
+++ b/libmodprobe/libmodprobe_test.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <functional>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+#include <modprobe/modprobe.h>
+
+#include "libmodprobe_test.h"
+
+// Used by libmodprobe_ext_test to check if requested modules are present.
+std::vector<std::string> test_modules;
+
+// Used by libmodprobe_ext_test to report which modules would have been loaded.
+std::vector<std::string> modules_loaded;
+
+TEST(libmodprobe, Test) {
+    test_modules = {
+            "/test1.ko",  "/test2.ko",  "/test3.ko",  "/test4.ko",  "/test5.ko",
+            "/test6.ko",  "/test7.ko",  "/test8.ko",  "/test9.ko",  "/test10.ko",
+            "/test11.ko", "/test12.ko", "/test13.ko", "/test14.ko", "/test15.ko",
+    };
+
+    std::vector<std::string> expected_modules_loaded = {
+            "/test14.ko",
+            "/test15.ko",
+            "/test3.ko",
+            "/test4.ko",
+            "/test1.ko",
+            "/test6.ko",
+            "/test2.ko",
+            "/test5.ko",
+            "/test8.ko",
+            "/test7.ko param1=4",
+            "/test9.ko param_x=1 param_y=2 param_z=3",
+            "/test10.ko",
+            "/test12.ko",
+            "/test11.ko",
+            "/test13.ko",
+    };
+
+    const std::string modules_dep =
+            "test1.ko:\n"
+            "test2.ko:\n"
+            "test3.ko:\n"
+            "test4.ko: test3.ko\n"
+            "test5.ko: test2.ko test6.ko\n"
+            "test6.ko:\n"
+            "test7.ko:\n"
+            "test8.ko:\n"
+            "test9.ko:\n"
+            "test10.ko:\n"
+            "test11.ko:\n"
+            "test12.ko:\n"
+            "test13.ko:\n"
+            "test14.ko:\n"
+            "test15.ko:\n";
+
+    const std::string modules_softdep =
+            "softdep test7 pre: test8\n"
+            "softdep test9 post: test10\n"
+            "softdep test11 pre: test12 post: test13\n"
+            "softdep test3 pre: test141516\n";
+
+    const std::string modules_alias =
+            "# Aliases extracted from modules themselves.\n"
+            "\n"
+            "alias test141516 test14\n"
+            "alias test141516 test15\n"
+            "alias test141516 test16\n";
+
+    const std::string modules_options =
+            "options test7.ko param1=4\n"
+            "options test9.ko param_x=1 param_y=2 param_z=3\n"
+            "options test100.ko param_1=1\n";
+
+    const std::string modules_load =
+            "test4.ko\n"
+            "test1.ko\n"
+            "test3.ko\n"
+            "test5.ko\n"
+            "test7.ko\n"
+            "test9.ko\n"
+            "test11.ko\n";
+
+    TemporaryDir dir;
+    ASSERT_TRUE(android::base::WriteStringToFile(
+            modules_alias, std::string(dir.path) + "/modules.alias", 0600, getuid(), getgid()));
+
+    ASSERT_TRUE(android::base::WriteStringToFile(
+            modules_dep, std::string(dir.path) + "/modules.dep", 0600, getuid(), getgid()));
+    ASSERT_TRUE(android::base::WriteStringToFile(
+            modules_softdep, std::string(dir.path) + "/modules.softdep", 0600, getuid(), getgid()));
+    ASSERT_TRUE(android::base::WriteStringToFile(
+            modules_options, std::string(dir.path) + "/modules.options", 0600, getuid(), getgid()));
+    ASSERT_TRUE(android::base::WriteStringToFile(
+            modules_load, std::string(dir.path) + "/modules.load", 0600, getuid(), getgid()));
+
+    for (auto i = test_modules.begin(); i != test_modules.end(); ++i) {
+        *i = dir.path + *i;
+    }
+
+    Modprobe m({dir.path});
+    EXPECT_TRUE(m.LoadListedModules());
+
+    GTEST_LOG_(INFO) << "Expected modules loaded (in order):";
+    for (auto i = expected_modules_loaded.begin(); i != expected_modules_loaded.end(); ++i) {
+        *i = dir.path + *i;
+        GTEST_LOG_(INFO) << "\"" << *i << "\"";
+    }
+    GTEST_LOG_(INFO) << "Actual modules loaded (in order):";
+    for (auto i = modules_loaded.begin(); i != modules_loaded.end(); ++i) {
+        GTEST_LOG_(INFO) << "\"" << *i << "\"";
+    }
+
+    EXPECT_TRUE(modules_loaded == expected_modules_loaded);
+}
diff --git a/libmodprobe/libmodprobe_test.h b/libmodprobe/libmodprobe_test.h
new file mode 100644
index 0000000..a001b69
--- /dev/null
+++ b/libmodprobe/libmodprobe_test.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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>
+
+extern std::vector<std::string> test_modules;
+extern std::vector<std::string> modules_loaded;
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index 6e63b74..10d42e4 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -1,22 +1,5 @@
-cc_library_headers {
-    name: "libnativebridge-dummy-headers",
-
-    host_supported: true,
-    export_include_dirs: ["include"],
-}
-
-cc_library {
-    name: "libnativebridge",
-
-    host_supported: true,
-    srcs: ["native_bridge.cc"],
-    shared_libs: [
-        "liblog",
-        "libbase",
-    ],
-
-    export_include_dirs: ["include"],
-
+cc_defaults {
+    name: "libnativebridge-defaults",
     cflags: [
         "-Werror",
         "-Wall",
@@ -24,6 +7,55 @@
     cppflags: [
         "-fvisibility=protected",
     ],
+    header_libs: ["libnativebridge-headers"],
+    export_header_lib_headers: ["libnativebridge-headers"],
+}
+
+cc_library_headers {
+    name: "libnativebridge-headers",
+
+    host_supported: true,
+    export_include_dirs: ["include"],
+}
+
+cc_library {
+    name: "libnativebridge",
+    defaults: ["libnativebridge-defaults"],
+
+    host_supported: true,
+    srcs: ["native_bridge.cc"],
+    header_libs: [
+        "libbase_headers",
+    ],
+    shared_libs: [
+        "liblog",
+    ],
+    // TODO(jiyong): remove this line after aosp/885921 lands
+    export_include_dirs: ["include"],
+
+    target: {
+        android: {
+            version_script: "libnativebridge.map.txt",
+        },
+        linux: {
+            version_script: "libnativebridge.map.txt",
+        },
+    },
+
+    stubs: {
+        symbol_file: "libnativebridge.map.txt",
+        versions: ["1"],
+    },
+}
+
+// TODO(b/124250621): eliminate the need for this library
+cc_library {
+    name: "libnativebridge_lazy",
+    defaults: ["libnativebridge-defaults"],
+
+    host_supported: false,
+    srcs: ["native_bridge_lazy.cc"],
+    required: ["libnativebridge"],
 }
 
 subdirs = ["tests"]
diff --git a/libnativebridge/Android.mk b/libnativebridge/Android.mk
deleted file mode 100644
index 3887b1b..0000000
--- a/libnativebridge/Android.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(LOCAL_PATH)/tests/Android.mk
diff --git a/libnativebridge/OWNERS b/libnativebridge/OWNERS
index f2cc942..daf87f4 100644
--- a/libnativebridge/OWNERS
+++ b/libnativebridge/OWNERS
@@ -1 +1,4 @@
 dimitry@google.com
+eaeltsin@google.com
+ngeoffray@google.com
+oth@google.com
diff --git a/libnativebridge/include/nativebridge/native_bridge.h b/libnativebridge/include/nativebridge/native_bridge.h
index 9bfc935..e9c9500 100644
--- a/libnativebridge/include/nativebridge/native_bridge.h
+++ b/libnativebridge/include/nativebridge/native_bridge.h
@@ -17,12 +17,17 @@
 #ifndef NATIVE_BRIDGE_H_
 #define NATIVE_BRIDGE_H_
 
-#include "jni.h"
 #include <signal.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <sys/types.h>
 
+#include "jni.h"
+
+#ifdef __cplusplus
 namespace android {
+extern "C" {
+#endif  // __cplusplus
 
 struct NativeBridgeRuntimeCallbacks;
 struct NativeBridgeRuntimeValues;
@@ -32,11 +37,10 @@
 // to the chain.
 typedef bool (*NativeBridgeSignalHandlerFn)(int, siginfo_t*, void*);
 
-
 // Open the native bridge, if any. Should be called by Runtime::Init(). A null library filename
 // signals that we do not want to load a native bridge.
 bool LoadNativeBridge(const char* native_bridge_library_filename,
-                      const NativeBridgeRuntimeCallbacks* runtime_callbacks);
+                      const struct NativeBridgeRuntimeCallbacks* runtime_callbacks);
 
 // Quick check whether a native bridge will be needed. This is based off of the instruction set
 // of the process.
@@ -99,7 +103,7 @@
 bool NativeBridgeNameAcceptable(const char* native_bridge_library_filename);
 
 // Decrements the reference count on the dynamic library handler. If the reference count drops
-// to zero then the dynamic library is unloaded.
+// to zero then the dynamic library is unloaded. Returns 0 on success and non-zero on error.
 int NativeBridgeUnloadLibrary(void* handle);
 
 // Get last error message of native bridge when fail to load library or search symbol.
@@ -138,19 +142,17 @@
 //
 // Starting with v3, NativeBridge has two scenarios: with/without namespace.
 // Should not use in non-namespace scenario.
-native_bridge_namespace_t* NativeBridgeCreateNamespace(const char* name,
-                                                       const char* ld_library_path,
-                                                       const char* default_library_path,
-                                                       uint64_t type,
-                                                       const char* permitted_when_isolated_path,
-                                                       native_bridge_namespace_t* parent_ns);
+struct native_bridge_namespace_t* NativeBridgeCreateNamespace(
+    const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type,
+    const char* permitted_when_isolated_path, struct native_bridge_namespace_t* parent_ns);
 
 // Creates a link which shares some libraries from one namespace to another.
 // NativeBridge's peer of android_link_namespaces() of dynamic linker.
 //
 // Starting with v3, NativeBridge has two scenarios: with/without namespace.
 // Should not use in non-namespace scenario.
-bool NativeBridgeLinkNamespaces(native_bridge_namespace_t* from, native_bridge_namespace_t* to,
+bool NativeBridgeLinkNamespaces(struct native_bridge_namespace_t* from,
+                                struct native_bridge_namespace_t* to,
                                 const char* shared_libs_sonames);
 
 // Load a shared library with namespace key that is supported by the native bridge.
@@ -159,10 +161,12 @@
 //
 // Starting with v3, NativeBridge has two scenarios: with/without namespace.
 // Use NativeBridgeLoadLibrary() instead in non-namespace scenario.
-void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns);
+void* NativeBridgeLoadLibraryExt(const char* libpath, int flag,
+                                 struct native_bridge_namespace_t* ns);
 
-// Returns vendor namespace if it is enabled for the device and null otherwise
-native_bridge_namespace_t* NativeBridgeGetVendorNamespace();
+// Returns exported namespace by the name. This is a reflection of
+// android_get_exported_namespace function. Introduced in v5.
+struct native_bridge_namespace_t* NativeBridgeGetExportedNamespace(const char* name);
 
 // Native bridge interfaces to runtime.
 struct NativeBridgeCallbacks {
@@ -177,8 +181,8 @@
   //   runtime_cbs [IN] the pointer to NativeBridgeRuntimeCallbacks.
   // Returns:
   //   true if initialization was successful.
-  bool (*initialize)(const NativeBridgeRuntimeCallbacks* runtime_cbs, const char* private_dir,
-                     const char* instruction_set);
+  bool (*initialize)(const struct NativeBridgeRuntimeCallbacks* runtime_cbs,
+                     const char* private_dir, const char* instruction_set);
 
   // Load a shared library that is supported by the native bridge.
   //
@@ -314,12 +318,12 @@
   //
   // Starting with v3, NativeBridge has two scenarios: with/without namespace.
   // Should not use in non-namespace scenario.
-  native_bridge_namespace_t* (*createNamespace)(const char* name,
-                                                const char* ld_library_path,
-                                                const char* default_library_path,
-                                                uint64_t type,
-                                                const char* permitted_when_isolated_path,
-                                                native_bridge_namespace_t* parent_ns);
+  struct native_bridge_namespace_t* (*createNamespace)(const char* name,
+                                                       const char* ld_library_path,
+                                                       const char* default_library_path,
+                                                       uint64_t type,
+                                                       const char* permitted_when_isolated_path,
+                                                       struct native_bridge_namespace_t* parent_ns);
 
   // Creates a link which shares some libraries from one namespace to another.
   // NativeBridge's peer of android_link_namespaces() of dynamic linker.
@@ -334,8 +338,8 @@
   //
   // Starting with v3, NativeBridge has two scenarios: with/without namespace.
   // Should not use in non-namespace scenario.
-  bool (*linkNamespaces)(native_bridge_namespace_t* from, native_bridge_namespace_t* to,
-                         const char* shared_libs_sonames);
+  bool (*linkNamespaces)(struct native_bridge_namespace_t* from,
+                         struct native_bridge_namespace_t* to, const char* shared_libs_sonames);
 
   // Load a shared library within a namespace.
   // NativeBridge's peer of android_dlopen_ext() of dynamic linker, only supports namespace
@@ -350,7 +354,7 @@
   //
   // Starting with v3, NativeBridge has two scenarios: with/without namespace.
   // Use loadLibrary instead in non-namespace scenario.
-  void* (*loadLibraryExt)(const char* libpath, int flag, native_bridge_namespace_t* ns);
+  void* (*loadLibraryExt)(const char* libpath, int flag, struct native_bridge_namespace_t* ns);
 
   // Get native bridge version of vendor namespace.
   // The vendor namespace is the namespace used to load vendor public libraries.
@@ -359,7 +363,17 @@
   //
   // Returns:
   //   vendor namespace or null if it was not set up for the device
-  native_bridge_namespace_t* (*getVendorNamespace)();
+  //
+  // Starting with v5 (Android Q) this function is no longer used.
+  // Use getExportedNamespace() below.
+  struct native_bridge_namespace_t* (*getVendorNamespace)();
+
+  // Get native bridge version of exported namespace. Peer of
+  // android_get_exported_namespace(const char*) function.
+  //
+  // Returns:
+  //   exported namespace or null if it was not set up for the device
+  struct native_bridge_namespace_t* (*getExportedNamespace)(const char* name);
 };
 
 // Runtime interfaces to native bridge.
@@ -396,6 +410,9 @@
                                uint32_t method_count);
 };
 
-};  // namespace android
+#ifdef __cplusplus
+}  // extern "C"
+}  // namespace android
+#endif  // __cplusplus
 
 #endif  // NATIVE_BRIDGE_H_
diff --git a/libnativebridge/libnativebridge.map.txt b/libnativebridge/libnativebridge.map.txt
new file mode 100644
index 0000000..a6841a3
--- /dev/null
+++ b/libnativebridge/libnativebridge.map.txt
@@ -0,0 +1,45 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# TODO(b/122710865): Most of these uses come from libnativeloader, which should be bundled
+# together with libnativebridge in the APEX. Once this happens, prune this list.
+LIBNATIVEBRIDGE_1 {
+  global:
+    NativeBridgeIsSupported;
+    NativeBridgeLoadLibrary;
+    NativeBridgeUnloadLibrary;
+    NativeBridgeGetError;
+    NativeBridgeIsPathSupported;
+    NativeBridgeCreateNamespace;
+    NativeBridgeGetExportedNamespace;
+    NativeBridgeLinkNamespaces;
+    NativeBridgeLoadLibraryExt;
+    NativeBridgeInitAnonymousNamespace;
+    NativeBridgeInitialized;
+    NativeBridgeGetTrampoline;
+    LoadNativeBridge;
+    PreInitializeNativeBridge;
+    InitializeNativeBridge;
+    NativeBridgeGetVersion;
+    NativeBridgeGetSignalHandler;
+    UnloadNativeBridge;
+    NativeBridgeAvailable;
+    NeedsNativeBridge;
+    NativeBridgeError;
+    NativeBridgeNameAcceptable;
+  local:
+    *;
+};
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index e24307a..9adba9a 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -33,6 +33,13 @@
 
 namespace android {
 
+#ifdef __APPLE__
+template <typename T>
+void UNUSED(const T&) {}
+#endif
+
+extern "C" {
+
 // Environment values required by the apps running with native bridge.
 struct NativeBridgeRuntimeValues {
     const char* os_arch;
@@ -94,6 +101,8 @@
   NAMESPACE_VERSION = 3,
   // The version with vendor namespaces
   VENDOR_NAMESPACE_VERSION = 4,
+  // The version with runtime namespaces
+  RUNTIME_NAMESPACE_VERSION = 5,
 };
 
 // Whether we had an error at some point.
@@ -252,10 +261,6 @@
   return strncmp(instruction_set, ABI_STRING, strlen(ABI_STRING) + 1) != 0;
 }
 
-#ifdef __APPLE__
-template<typename T> void UNUSED(const T&) {}
-#endif
-
 bool PreInitializeNativeBridge(const char* app_data_dir_in, const char* instruction_set) {
   if (state != NativeBridgeState::kOpened) {
     ALOGE("Invalid state: native bridge is expected to be opened.");
@@ -607,12 +612,22 @@
   return false;
 }
 
-native_bridge_namespace_t* NativeBridgeGetVendorNamespace() {
-  if (!NativeBridgeInitialized() || !isCompatibleWith(VENDOR_NAMESPACE_VERSION)) {
+native_bridge_namespace_t* NativeBridgeGetExportedNamespace(const char* name) {
+  if (!NativeBridgeInitialized()) {
     return nullptr;
   }
 
-  return callbacks->getVendorNamespace();
+  if (isCompatibleWith(RUNTIME_NAMESPACE_VERSION)) {
+    return callbacks->getExportedNamespace(name);
+  }
+
+  // sphal is vendor namespace name -> use v4 callback in the case NB callbacks
+  // are not compatible with v5
+  if (isCompatibleWith(VENDOR_NAMESPACE_VERSION) && name != nullptr && strcmp("sphal", name) == 0) {
+    return callbacks->getVendorNamespace();
+  }
+
+  return nullptr;
 }
 
 void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns) {
@@ -626,4 +641,6 @@
   return nullptr;
 }
 
-};  // namespace android
+}  // extern "C"
+
+}  // namespace android
diff --git a/libnativebridge/native_bridge_lazy.cc b/libnativebridge/native_bridge_lazy.cc
new file mode 100644
index 0000000..94c8084
--- /dev/null
+++ b/libnativebridge/native_bridge_lazy.cc
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "nativebridge/native_bridge.h"
+#define LOG_TAG "nativebridge"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+
+#include <log/log.h>
+
+namespace android {
+
+namespace {
+
+void* GetLibHandle() {
+  static void* handle = dlopen("libnativebridge.so", RTLD_NOW);
+  LOG_FATAL_IF(handle == nullptr, "Failed to load libnativebridge.so: %s", dlerror());
+  return handle;
+}
+
+template <typename FuncPtr>
+FuncPtr GetFuncPtr(const char* function_name) {
+  auto f = reinterpret_cast<FuncPtr>(dlsym(GetLibHandle(), function_name));
+  LOG_FATAL_IF(f == nullptr, "Failed to get address of %s: %s", function_name, dlerror());
+  return f;
+}
+
+#define GET_FUNC_PTR(name) GetFuncPtr<decltype(&name)>(#name)
+
+}  // namespace
+
+bool LoadNativeBridge(const char* native_bridge_library_filename,
+                      const struct NativeBridgeRuntimeCallbacks* runtime_callbacks) {
+  static auto f = GET_FUNC_PTR(LoadNativeBridge);
+  return f(native_bridge_library_filename, runtime_callbacks);
+}
+
+bool NeedsNativeBridge(const char* instruction_set) {
+  static auto f = GET_FUNC_PTR(NeedsNativeBridge);
+  return f(instruction_set);
+}
+
+bool PreInitializeNativeBridge(const char* app_data_dir, const char* instruction_set) {
+  static auto f = GET_FUNC_PTR(PreInitializeNativeBridge);
+  return f(app_data_dir, instruction_set);
+}
+
+bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set) {
+  static auto f = GET_FUNC_PTR(InitializeNativeBridge);
+  return f(env, instruction_set);
+}
+
+void UnloadNativeBridge() {
+  static auto f = GET_FUNC_PTR(UnloadNativeBridge);
+  return f();
+}
+
+bool NativeBridgeAvailable() {
+  static auto f = GET_FUNC_PTR(NativeBridgeAvailable);
+  return f();
+}
+
+bool NativeBridgeInitialized() {
+  static auto f = GET_FUNC_PTR(NativeBridgeInitialized);
+  return f();
+}
+
+void* NativeBridgeLoadLibrary(const char* libpath, int flag) {
+  static auto f = GET_FUNC_PTR(NativeBridgeLoadLibrary);
+  return f(libpath, flag);
+}
+
+void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty, uint32_t len) {
+  static auto f = GET_FUNC_PTR(NativeBridgeGetTrampoline);
+  return f(handle, name, shorty, len);
+}
+
+bool NativeBridgeIsSupported(const char* libpath) {
+  static auto f = GET_FUNC_PTR(NativeBridgeIsSupported);
+  return f(libpath);
+}
+
+uint32_t NativeBridgeGetVersion() {
+  static auto f = GET_FUNC_PTR(NativeBridgeGetVersion);
+  return f();
+}
+
+NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal) {
+  static auto f = GET_FUNC_PTR(NativeBridgeGetSignalHandler);
+  return f(signal);
+}
+
+bool NativeBridgeError() {
+  static auto f = GET_FUNC_PTR(NativeBridgeError);
+  return f();
+}
+
+bool NativeBridgeNameAcceptable(const char* native_bridge_library_filename) {
+  static auto f = GET_FUNC_PTR(NativeBridgeNameAcceptable);
+  return f(native_bridge_library_filename);
+}
+
+int NativeBridgeUnloadLibrary(void* handle) {
+  static auto f = GET_FUNC_PTR(NativeBridgeUnloadLibrary);
+  return f(handle);
+}
+
+const char* NativeBridgeGetError() {
+  static auto f = GET_FUNC_PTR(NativeBridgeGetError);
+  return f();
+}
+
+bool NativeBridgeIsPathSupported(const char* path) {
+  static auto f = GET_FUNC_PTR(NativeBridgeIsPathSupported);
+  return f(path);
+}
+
+bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
+                                        const char* anon_ns_library_path) {
+  static auto f = GET_FUNC_PTR(NativeBridgeInitAnonymousNamespace);
+  return f(public_ns_sonames, anon_ns_library_path);
+}
+
+struct native_bridge_namespace_t* NativeBridgeCreateNamespace(
+    const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type,
+    const char* permitted_when_isolated_path, struct native_bridge_namespace_t* parent_ns) {
+  static auto f = GET_FUNC_PTR(NativeBridgeCreateNamespace);
+  return f(name, ld_library_path, default_library_path, type, permitted_when_isolated_path,
+           parent_ns);
+}
+
+bool NativeBridgeLinkNamespaces(struct native_bridge_namespace_t* from,
+                                struct native_bridge_namespace_t* to,
+                                const char* shared_libs_sonames) {
+  static auto f = GET_FUNC_PTR(NativeBridgeLinkNamespaces);
+  return f(from, to, shared_libs_sonames);
+}
+
+void* NativeBridgeLoadLibraryExt(const char* libpath, int flag,
+                                 struct native_bridge_namespace_t* ns) {
+  static auto f = GET_FUNC_PTR(NativeBridgeLoadLibraryExt);
+  return f(libpath, flag, ns);
+}
+
+struct native_bridge_namespace_t* NativeBridgeGetVendorNamespace() {
+  static auto f = GET_FUNC_PTR(NativeBridgeGetVendorNamespace);
+  return f();
+}
+
+#undef GET_FUNC_PTR
+
+}  // namespace android
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
index 9e2e641..2bb8467 100644
--- a/libnativebridge/tests/Android.bp
+++ b/libnativebridge/tests/Android.bp
@@ -23,7 +23,7 @@
         "-Wextra",
         "-Werror",
     ],
-    header_libs: ["libnativebridge-dummy-headers"],
+    header_libs: ["libnativebridge-headers"],
     cppflags: ["-fvisibility=protected"],
 }
 
@@ -44,3 +44,67 @@
     srcs: ["DummyNativeBridge3.cpp"],
     defaults: ["libnativebridge-dummy-defaults"],
 }
+
+// Build the unit tests.
+cc_defaults {
+    name: "libnativebridge-tests-defaults",
+    test_per_src: true,
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    srcs: [
+        "CodeCacheCreate_test.cpp",
+        "CodeCacheExists_test.cpp",
+        "CodeCacheStatFail_test.cpp",
+        "CompleteFlow_test.cpp",
+        "InvalidCharsNativeBridge_test.cpp",
+        "NativeBridge2Signal_test.cpp",
+        "NativeBridgeVersion_test.cpp",
+        "NeedsNativeBridge_test.cpp",
+        "PreInitializeNativeBridge_test.cpp",
+        "PreInitializeNativeBridgeFail1_test.cpp",
+        "PreInitializeNativeBridgeFail2_test.cpp",
+        "ReSetupNativeBridge_test.cpp",
+        "UnavailableNativeBridge_test.cpp",
+        "ValidNameNativeBridge_test.cpp",
+        "NativeBridge3UnloadLibrary_test.cpp",
+        "NativeBridge3GetError_test.cpp",
+        "NativeBridge3IsPathSupported_test.cpp",
+        "NativeBridge3InitAnonymousNamespace_test.cpp",
+        "NativeBridge3CreateNamespace_test.cpp",
+        "NativeBridge3LoadLibraryExt_test.cpp",
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libnativebridge-dummy",
+    ],
+    header_libs: ["libbase_headers"],
+}
+
+cc_test {
+    name: "libnativebridge-tests",
+    defaults: ["libnativebridge-tests-defaults"],
+    host_supported: true,
+    shared_libs: ["libnativebridge"],
+}
+
+cc_test {
+    name: "libnativebridge-lazy-tests",
+    defaults: ["libnativebridge-tests-defaults"],
+    shared_libs: ["libnativebridge_lazy"],
+}
+
+// Build the test for the C API.
+cc_test {
+    name: "libnativebridge-api-tests",
+    host_supported: true,
+    test_per_src: true,
+    srcs: [
+        "NativeBridgeApi.c",
+    ],
+    header_libs: ["libnativebridge-headers"],
+}
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
deleted file mode 100644
index 5b9ba1c..0000000
--- a/libnativebridge/tests/Android.mk
+++ /dev/null
@@ -1,56 +0,0 @@
-# Build the unit tests.
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Build the unit tests.
-test_src_files := \
-    CodeCacheCreate_test.cpp \
-    CodeCacheExists_test.cpp \
-    CodeCacheStatFail_test.cpp \
-    CompleteFlow_test.cpp \
-    InvalidCharsNativeBridge_test.cpp \
-    NativeBridge2Signal_test.cpp \
-    NativeBridgeVersion_test.cpp \
-    NeedsNativeBridge_test.cpp \
-    PreInitializeNativeBridge_test.cpp \
-    PreInitializeNativeBridgeFail1_test.cpp \
-    PreInitializeNativeBridgeFail2_test.cpp \
-    ReSetupNativeBridge_test.cpp \
-    UnavailableNativeBridge_test.cpp \
-    ValidNameNativeBridge_test.cpp \
-    NativeBridge3UnloadLibrary_test.cpp \
-    NativeBridge3GetError_test.cpp \
-    NativeBridge3IsPathSupported_test.cpp \
-    NativeBridge3InitAnonymousNamespace_test.cpp \
-    NativeBridge3CreateNamespace_test.cpp \
-    NativeBridge3LoadLibraryExt_test.cpp
-
-
-shared_libraries := \
-    liblog \
-    libbase \
-    libnativebridge \
-    libnativebridge-dummy
-
-libnativebridge_tests_common_cflags := \
-    -Wall \
-    -Werror \
-
-$(foreach file,$(test_src_files), \
-    $(eval include $(CLEAR_VARS)) \
-    $(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_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/NativeBridgeApi.c b/libnativebridge/tests/NativeBridgeApi.c
new file mode 100644
index 0000000..7ab71fe
--- /dev/null
+++ b/libnativebridge/tests/NativeBridgeApi.c
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* The main purpose of this test is to ensure this C header compiles in C, so
+ * that no C++ features inadvertently leak into the C ABI. */
+#include "nativebridge/native_bridge.h"
+
+int main(int argc, char** argv) {
+  (void)argc;
+  (void)argv;
+  return 0;
+}
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index 17983bc..debc43f 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -1,16 +1,7 @@
 // Shared library for target
 // ========================================================
-cc_library {
-    name: "libnativeloader",
-    host_supported: true,
-    srcs: ["native_loader.cpp"],
-    shared_libs: [
-        "libnativehelper",
-        "liblog",
-        "libcutils",
-        "libnativebridge",
-        "libbase",
-    ],
+cc_defaults {
+    name: "libnativeloader-defaults",
     cflags: [
         "-Werror",
         "-Wall",
@@ -18,9 +9,64 @@
     cppflags: [
         "-fvisibility=hidden",
     ],
-    export_include_dirs: ["include"],
+    header_libs: ["libnativeloader-headers"],
+    export_header_lib_headers: ["libnativeloader-headers"],
+}
+
+cc_library {
+    name: "libnativeloader",
+    defaults: ["libnativeloader-defaults"],
+    host_supported: true,
+    srcs: [
+        "native_loader.cpp",
+    ],
+    shared_libs: [
+        "libnativehelper",
+        "liblog",
+        "libnativebridge",
+        "libbase",
+    ],
+    target: {
+        android: {
+            srcs: [
+                "library_namespaces.cpp",
+                "native_loader_namespace.cpp",
+                "public_libraries.cpp",
+            ],
+            shared_libs: [
+                "libdl_android",
+            ],
+        },
+    },
     required: [
         "llndk.libraries.txt",
         "vndksp.libraries.txt",
     ],
+    stubs: {
+        symbol_file: "libnativeloader.map.txt",
+        versions: ["1"],
+    },
+}
+
+// TODO(b/124250621) eliminate the need for this library
+cc_library {
+    name: "libnativeloader_lazy",
+    defaults: ["libnativeloader-defaults"],
+    host_supported: false,
+    srcs: ["native_loader_lazy.cpp"],
+    required: ["libnativeloader"],
+}
+
+cc_library_headers {
+    name: "libnativeloader-headers",
+    host_supported: true,
+    export_include_dirs: ["include"],
+}
+
+// TODO(jiyong) Remove this when its use in the internal master is
+// switched to libnativeloader-headers
+cc_library_headers {
+    name: "libnativeloader-dummy-headers",
+    host_supported: true,
+    export_include_dirs: ["include"],
 }
diff --git a/libnativeloader/OWNERS b/libnativeloader/OWNERS
index f2cc942..f735653 100644
--- a/libnativeloader/OWNERS
+++ b/libnativeloader/OWNERS
@@ -1 +1,6 @@
 dimitry@google.com
+jiyong@google.com
+ngeoffray@google.com
+oth@google.com
+mast@google.com
+rpl@google.com
diff --git a/libnativeloader/README.md b/libnativeloader/README.md
new file mode 100644
index 0000000..46f6fdd
--- /dev/null
+++ b/libnativeloader/README.md
@@ -0,0 +1,84 @@
+libnativeloader
+===============================================================================
+
+Overview
+-------------------------------------------------------------------------------
+libnativeloader is responsible for loading native shared libraries (`*.so`
+files) inside the Android Runtime (ART). The native shared libraries could be
+app-provided JNI libraries or public native libraries like `libc.so` provided
+by the platform.
+
+The most typical use case of this library is calling `System.loadLibrary(name)`.
+When the method is called, the ART runtime delegates the call to this library
+along with the reference to the classloader where the call was made.  Then this
+library finds the linker namespace (named `classloader-namespace`) that is
+associated with the given classloader, and tries to load the requested library
+from the namespace. The actual searching, loading, and linking of the library
+is performed by the dynamic linker.
+
+The linker namespace is created when an APK is loaded into the process, and is
+associated with the classloader that loaded the APK. The linker namespace is
+configured so that only the JNI libraries embedded in the APK is accessible
+from the namespace, thus preventing an APK from loading JNI libraries of other
+APKs.
+
+The linker namespace is also configured differently depending on other
+characteristics of the APK such as whether or not the APK is bundled with the
+platform. In case of the unbundled, i.e., downloaded or updated APK, only the
+public native libraries that is listed in `/system/etc/public.libraries.txt`
+are available from the platform, whereas in case of the bundled, all libraries
+under `/system/lib` are available (i.e. shared). In case when the unbundled
+app is from `/vendor` or `/product` partition, the app is additionally provided
+with the [VNDK-SP](https://source.android.com/devices/architecture/vndk#sp-hal)
+libraries. As the platform is getting modularized with
+[APEX](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/docs/README.md),
+some libraries are no longer provided from platform, but from the APEXes which
+have their own linker namespaces. For example, ICU libraries `libicuuc.so` and
+`libicui18n.so` are from the runtime APEX.
+
+The list of public native libraries is not static. The default set of libraries
+are defined in AOSP, but partners can extend it to include their own libraries.
+Currently, following extensions are available:
+
+- `/vendor/etc/public.libraries.txt`: libraries in `/vendor/lib` that are
+specific to the underlying SoC, e.g. GPU, DSP, etc.
+- `/{system|product}/etc/public.libraries-<companyname>.txt`: libraries in
+`/{system|system}/lib` that a device manufacturer has newly added. The
+libraries should be named as `lib<name>.<companyname>.so` as in
+`libFoo.acme.so`.
+
+Note that, due to the naming constraint requiring `.<companyname>.so` suffix, it
+is prohibited for a device manufacturer to expose an AOSP-defined private
+library, e.g. libgui.so, libart.so, etc., to APKs.
+
+Lastly, libnativeloader is responsible for abstracting the two types of the
+dynamic linker interface: `libdl.so` and `libnativebridge.so`. The former is
+for non-translated, e.g. ARM-on-ARM, libraries, while the latter is for
+loading libraries in a translated environment such as ARM-on-x86.
+
+Implementation
+-------------------------------------------------------------------------------
+Implementation wise, libnativeloader consists of four parts:
+
+- `native_loader.cpp`
+- `library_namespaces.cpp`
+- `native_loader_namespace.cpp`
+- `public_libraries.cpp`
+
+`native_loader.cpp` implements the public interface of this library. It is just
+a thin wrapper around `library_namespaces.cpp` and `native_loader_namespace.cpp`.
+
+`library_namespaces.cpp` implements the singleton class `LibraryNamespaces` which
+is a manager-like entity that is responsible for creating and configuring
+linker namespaces and finding an already created linker namespace for a given
+classloader.
+
+`native_loader_namesapces.cpp` implements the class `NativeLoaderNamespace` that
+models a linker namespace. It's main job is to abstract the two types of the
+dynamic linker interface so that other parts of this library do not have to know
+the differences of the interfaces.
+
+`public_libraries.cpp` is responsible for reading `*.txt` files for the public
+native libraries from the various partitions. It can be considered as a part of
+`LibraryNamespaces` but is separated from it to hide the details of the parsing
+routines.
diff --git a/libnativeloader/include/nativeloader/dlext_namespaces.h b/libnativeloader/include/nativeloader/dlext_namespaces.h
index 43c9329..2d6ce85 100644
--- a/libnativeloader/include/nativeloader/dlext_namespaces.h
+++ b/libnativeloader/include/nativeloader/dlext_namespaces.h
@@ -18,6 +18,7 @@
 #define __ANDROID_DLEXT_NAMESPACES_H__
 
 #include <android/dlext.h>
+#include <stdbool.h>
 
 __BEGIN_DECLS
 
@@ -84,12 +85,9 @@
  * If a library or any of its dependencies are outside of the permitted_when_isolated_path
  * and search_path, and it is not part of the public namespace dlopen will fail.
  */
-extern struct android_namespace_t* android_create_namespace(const char* name,
-                                                            const char* ld_library_path,
-                                                            const char* default_library_path,
-                                                            uint64_t type,
-                                                            const char* permitted_when_isolated_path,
-                                                            android_namespace_t* parent);
+extern struct android_namespace_t* android_create_namespace(
+    const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type,
+    const char* permitted_when_isolated_path, struct android_namespace_t* parent);
 
 /*
  * Creates a link between namespaces. Every link has list of sonames of
@@ -107,24 +105,11 @@
  *      step will not go deeper into linked namespaces for this library but
  *      will do so for DT_NEEDED libraries.
  */
-extern bool android_link_namespaces(android_namespace_t* from,
-                                    android_namespace_t* to,
+extern bool android_link_namespaces(struct android_namespace_t* from,
+                                    struct android_namespace_t* to,
                                     const char* shared_libs_sonames);
 
-/*
- * Get the default library search path.
- * The path will be copied into buffer, which must have space for at least
- * buffer_size chars. Elements are separated with ':', and the path will always
- * be null-terminated.
- *
- * If buffer_size is too small to hold the entire default search path and the
- * null terminator, this function will abort. There is currently no way to find
- * out what the required buffer size is. At the time of this writing, PATH_MAX
- * is sufficient and used by all callers of this function.
- */
-extern void android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size);
-
-extern android_namespace_t* android_get_exported_namespace(const char* name);
+extern struct android_namespace_t* android_get_exported_namespace(const char* name);
 
 __END_DECLS
 
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index 3563fc1..51fb875 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -17,49 +17,62 @@
 #ifndef NATIVE_LOADER_H_
 #define NATIVE_LOADER_H_
 
-#include "jni.h"
+#include <stdbool.h>
 #include <stdint.h>
-#include <string>
+#include "jni.h"
 #if defined(__ANDROID__)
 #include <android/dlext.h>
 #endif
 
+#ifdef __cplusplus
 namespace android {
+extern "C" {
+#endif  // __cplusplus
+
+// README: the char** error message parameter being passed
+// to the methods below need to be freed through calling NativeLoaderFreeErrorMessage.
+// It's the caller's responsibility to call that method.
 
 __attribute__((visibility("default")))
 void InitializeNativeLoader();
 
-__attribute__((visibility("default")))
-jstring CreateClassLoaderNamespace(JNIEnv* env,
-                                   int32_t target_sdk_version,
-                                   jobject class_loader,
-                                   bool is_shared,
-                                   bool is_for_vendor,
-                                   jstring library_path,
-                                   jstring permitted_path);
+__attribute__((visibility("default"))) jstring CreateClassLoaderNamespace(
+    JNIEnv* env, int32_t target_sdk_version, jobject class_loader, bool is_shared, jstring dex_path,
+    jstring library_path, jstring permitted_path);
 
-__attribute__((visibility("default")))
-void* OpenNativeLibrary(JNIEnv* env,
-                        int32_t target_sdk_version,
-                        const char* path,
-                        jobject class_loader,
-                        jstring library_path,
-                        bool* needs_native_bridge,
-                        std::string* error_msg);
+__attribute__((visibility("default"))) void* OpenNativeLibrary(
+    JNIEnv* env, int32_t target_sdk_version, const char* path, jobject class_loader,
+    const char* caller_location, jstring library_path, bool* needs_native_bridge, char** error_msg);
 
-__attribute__((visibility("default")))
-bool CloseNativeLibrary(void* handle, const bool needs_native_bridge);
+__attribute__((visibility("default"))) bool CloseNativeLibrary(void* handle,
+                                                               const bool needs_native_bridge,
+                                                               char** error_msg);
+
+__attribute__((visibility("default"))) void NativeLoaderFreeErrorMessage(char* msg);
 
 #if defined(__ANDROID__)
 // Look up linker namespace by class_loader. Returns nullptr if
 // there is no namespace associated with the class_loader.
-__attribute__((visibility("default")))
-android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+// TODO(b/79940628): move users to FindNativeLoaderNamespaceByClassLoader and remove this function.
+__attribute__((visibility("default"))) struct android_namespace_t* FindNamespaceByClassLoader(
+    JNIEnv* env, jobject class_loader);
+// That version works with native bridge namespaces, but requires use of OpenNativeLibrary.
+struct NativeLoaderNamespace;
+__attribute__((visibility("default"))) struct 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* OpenNativeLibraryInNamespace(
+    struct NativeLoaderNamespace* ns, const char* path, bool* needs_native_bridge,
+    char** error_msg);
 #endif
 
 __attribute__((visibility("default")))
 void ResetNativeLoader();
 
-};  // namespace android
+#ifdef __cplusplus
+}  // extern "C"
+}  // namespace android
+#endif  // __cplusplus
 
 #endif  // NATIVE_BRIDGE_H_
diff --git a/libnativeloader/libnativeloader.map.txt b/libnativeloader/libnativeloader.map.txt
new file mode 100644
index 0000000..40c30bd
--- /dev/null
+++ b/libnativeloader/libnativeloader.map.txt
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# TODO(b/122710865): Prune these uses once the runtime APEX is complete.
+LIBNATIVELOADER_1 {
+  global:
+    OpenNativeLibrary;
+    InitializeNativeLoader;
+    ResetNativeLoader;
+    CloseNativeLibrary;
+    OpenNativeLibraryInNamespace;
+    FindNamespaceByClassLoader;
+    FindNativeLoaderNamespaceByClassLoader;
+    CreateClassLoaderNamespace;
+    NativeLoaderFreeErrorMessage;
+  local:
+    *;
+};
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
new file mode 100644
index 0000000..f7f972f
--- /dev/null
+++ b/libnativeloader/library_namespaces.cpp
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "library_namespaces.h"
+
+#include <dirent.h>
+#include <dlfcn.h>
+
+#include <regex>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <nativehelper/ScopedUtfChars.h>
+
+#include "nativeloader/dlext_namespaces.h"
+#include "public_libraries.h"
+#include "utils.h"
+
+namespace android::nativeloader {
+
+namespace {
+// 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
+// to use to load vendor libraries to separate namespace with controlled interface between
+// vendor and system namespaces.
+constexpr const char* kVendorNamespaceName = "sphal";
+constexpr const char* kVndkNamespaceName = "vndk";
+constexpr const char* kRuntimeNamespaceName = "runtime";
+
+// classloader-namespace is a linker namespace that is created for the loaded
+// app. To be specific, it is created for the app classloader. When
+// System.load() is called from a Java class that is loaded from the
+// classloader, the classloader-namespace namespace associated with that
+// classloader is selected for dlopen. The namespace is configured so that its
+// search path is set to the app-local JNI directory and it is linked to the
+// platform namespace with the names of libs listed in the public.libraries.txt.
+// This way an app can only load its own JNI libraries along with the public libs.
+constexpr const char* kClassloaderNamespaceName = "classloader-namespace";
+// Same thing for vendor APKs.
+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.
+constexpr const char* kWhitelistedDirectories = "/data:/mnt/expand";
+
+constexpr const char* kVendorLibPath = "/vendor/" LIB;
+constexpr const char* kProductLibPath = "/product/" LIB ":/system/product/" LIB;
+
+const std::regex kVendorDexPathRegex("(^|:)/vendor/");
+const std::regex kProductDexPathRegex("(^|:)(/system)?/product/");
+
+// Define origin of APK if it is from vendor partition or product partition
+typedef enum {
+  APK_ORIGIN_DEFAULT = 0,
+  APK_ORIGIN_VENDOR = 1,
+  APK_ORIGIN_PRODUCT = 2,
+} ApkOrigin;
+
+jobject GetParentClassLoader(JNIEnv* env, jobject class_loader) {
+  jclass class_loader_class = env->FindClass("java/lang/ClassLoader");
+  jmethodID get_parent =
+      env->GetMethodID(class_loader_class, "getParent", "()Ljava/lang/ClassLoader;");
+
+  return env->CallObjectMethod(class_loader, get_parent);
+}
+
+ApkOrigin GetApkOriginFromDexPath(JNIEnv* env, jstring dex_path) {
+  ApkOrigin apk_origin = APK_ORIGIN_DEFAULT;
+
+  if (dex_path != nullptr) {
+    ScopedUtfChars dex_path_utf_chars(env, dex_path);
+
+    if (std::regex_search(dex_path_utf_chars.c_str(), kVendorDexPathRegex)) {
+      apk_origin = APK_ORIGIN_VENDOR;
+    }
+
+    if (std::regex_search(dex_path_utf_chars.c_str(), kProductDexPathRegex)) {
+      LOG_ALWAYS_FATAL_IF(apk_origin == APK_ORIGIN_VENDOR,
+                          "Dex path contains both vendor and product partition : %s",
+                          dex_path_utf_chars.c_str());
+
+      apk_origin = APK_ORIGIN_PRODUCT;
+    }
+  }
+  return apk_origin;
+}
+
+}  // namespace
+
+void LibraryNamespaces::Initialize() {
+  // Once public namespace is initialized there is no
+  // point in running this code - it will have no effect
+  // on the current list of public libraries.
+  if (initialized_) {
+    return;
+  }
+
+  // android_init_namespaces() expects all the public libraries
+  // to be loaded so that they can be found by soname alone.
+  //
+  // 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 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 : android::base::Split(default_public_libraries(), ":")) {
+    LOG_ALWAYS_FATAL_IF(dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr,
+                        "Error preloading public library %s: %s", soname.c_str(), dlerror());
+  }
+}
+
+NativeLoaderNamespace* LibraryNamespaces::Create(JNIEnv* env, uint32_t target_sdk_version,
+                                                 jobject class_loader, bool is_shared,
+                                                 jstring dex_path, 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) {
+    ScopedUtfChars library_path_utf_chars(env, java_library_path);
+    library_path = library_path_utf_chars.c_str();
+  }
+
+  ApkOrigin apk_origin = GetApkOriginFromDexPath(env, dex_path);
+
+  // (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 part effectively allows such a classloader to access anything
+  // under /data and /mnt/expand
+  std::string permitted_path = kWhitelistedDirectories;
+
+  if (java_permitted_path != nullptr) {
+    ScopedUtfChars path(env, java_permitted_path);
+    if (path.c_str() != nullptr && path.size() > 0) {
+      permitted_path = permitted_path + ":" + path.c_str();
+    }
+  }
+
+  // Initialize the anonymous namespace with the first non-empty library path.
+  if (!library_path.empty() && !initialized_ &&
+      !InitPublicNamespace(library_path.c_str(), error_msg)) {
+    return nullptr;
+  }
+
+  bool found = FindNamespaceByClassLoader(env, class_loader);
+
+  LOG_ALWAYS_FATAL_IF(found, "There is already a namespace associated with this classloader");
+
+  std::string system_exposed_libraries = default_public_libraries();
+  const char* namespace_name = kClassloaderNamespaceName;
+  bool unbundled_vendor_or_product_app = false;
+  if ((apk_origin == APK_ORIGIN_VENDOR ||
+       (apk_origin == APK_ORIGIN_PRODUCT && target_sdk_version > 29)) &&
+      !is_shared) {
+    unbundled_vendor_or_product_app = true;
+    // For vendor / product apks, give access to the vendor / product lib even though
+    // they are treated as unbundled; the libs and apks are still bundled
+    // together in the vendor / product partition.
+    const char* origin_partition;
+    const char* origin_lib_path;
+
+    switch (apk_origin) {
+      case APK_ORIGIN_VENDOR:
+        origin_partition = "vendor";
+        origin_lib_path = kVendorLibPath;
+        break;
+      case APK_ORIGIN_PRODUCT:
+        origin_partition = "product";
+        origin_lib_path = kProductLibPath;
+        break;
+      default:
+        origin_partition = "unknown";
+        origin_lib_path = "";
+    }
+    library_path = library_path + ":" + origin_lib_path;
+    permitted_path = permitted_path + ":" + origin_lib_path;
+
+    // Also give access to LLNDK libraries since they are available to vendors
+    system_exposed_libraries = system_exposed_libraries + ":" + llndk_libraries().c_str();
+
+    // Different name is useful for debugging
+    namespace_name = kVendorClassloaderNamespaceName;
+    ALOGD("classloader namespace configured for unbundled %s apk. library_path=%s",
+          origin_partition, library_path.c_str());
+  } else {
+    // extended public libraries are NOT available to vendor apks, otherwise it
+    // would be system->vendor violation.
+    if (!extended_public_libraries().empty()) {
+      system_exposed_libraries = system_exposed_libraries + ':' + extended_public_libraries();
+    }
+  }
+
+  // Create the app namespace
+  NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
+  auto app_ns =
+      NativeLoaderNamespace::Create(namespace_name, library_path, permitted_path, parent_ns,
+                                    is_shared, target_sdk_version < 24 /* is_greylist_enabled */);
+  if (app_ns.IsNil()) {
+    *error_msg = app_ns.GetError();
+    return nullptr;
+  }
+
+  // ... and link to other namespaces to allow access to some public libraries
+  bool is_bridged = app_ns.IsBridged();
+
+  auto platform_ns = NativeLoaderNamespace::GetPlatformNamespace(is_bridged);
+  if (!app_ns.Link(platform_ns, system_exposed_libraries)) {
+    *error_msg = app_ns.GetError();
+    return nullptr;
+  }
+
+  auto runtime_ns = NativeLoaderNamespace::GetExportedNamespace(kRuntimeNamespaceName, is_bridged);
+  // Runtime apex does not exist in host, and under certain build conditions.
+  if (!runtime_ns.IsNil()) {
+    if (!app_ns.Link(runtime_ns, runtime_public_libraries())) {
+      *error_msg = app_ns.GetError();
+      return nullptr;
+    }
+  }
+
+  // Give access to VNDK-SP libraries from the 'vndk' namespace.
+  if (unbundled_vendor_or_product_app && !vndksp_libraries().empty()) {
+    auto vndk_ns = NativeLoaderNamespace::GetExportedNamespace(kVndkNamespaceName, is_bridged);
+    if (!vndk_ns.IsNil() && !app_ns.Link(vndk_ns, vndksp_libraries())) {
+      *error_msg = app_ns.GetError();
+      return nullptr;
+    }
+  }
+
+  // Note that when vendor_ns is not configured, vendor_ns.IsNil() will be true
+  // and it will result in linking to the default namespace which is expected
+  // behavior in this case.
+  if (!vendor_public_libraries().empty()) {
+    auto vendor_ns = NativeLoaderNamespace::GetExportedNamespace(kVendorNamespaceName, is_bridged);
+    if (!app_ns.Link(vendor_ns, vendor_public_libraries())) {
+      *error_msg = dlerror();
+      return nullptr;
+    }
+  }
+
+  namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), app_ns));
+
+  return &(namespaces_.back().second);
+}
+
+NativeLoaderNamespace* LibraryNamespaces::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()) {
+    return &it->second;
+  }
+
+  return nullptr;
+}
+
+bool LibraryNamespaces::InitPublicNamespace(const char* library_path, std::string* error_msg) {
+  // Ask native bride if this apps library path should be handled by it
+  bool is_native_bridge = NativeBridgeIsPathSupported(library_path);
+
+  // (http://b/25844435) - Some apps call dlopen from generated code (mono jited
+  // code is one example) unknown to linker in which  case linker uses anonymous
+  // namespace. The second argument specifies the search path for the anonymous
+  // namespace which is the library_path of the classloader.
+  initialized_ = android_init_anonymous_namespace(default_public_libraries().c_str(),
+                                                  is_native_bridge ? nullptr : library_path);
+  if (!initialized_) {
+    *error_msg = dlerror();
+    return false;
+  }
+
+  // and now initialize native bridge namespaces if necessary.
+  if (NativeBridgeInitialized()) {
+    initialized_ = NativeBridgeInitAnonymousNamespace(default_public_libraries().c_str(),
+                                                      is_native_bridge ? library_path : nullptr);
+    if (!initialized_) {
+      *error_msg = NativeBridgeGetError();
+    }
+  }
+
+  return initialized_;
+}
+
+NativeLoaderNamespace* LibraryNamespaces::FindParentNamespaceByClassLoader(JNIEnv* env,
+                                                                           jobject class_loader) {
+  jobject parent_class_loader = GetParentClassLoader(env, class_loader);
+
+  while (parent_class_loader != nullptr) {
+    NativeLoaderNamespace* ns;
+    if ((ns = FindNamespaceByClassLoader(env, parent_class_loader)) != nullptr) {
+      return ns;
+    }
+
+    parent_class_loader = GetParentClassLoader(env, parent_class_loader);
+  }
+
+  return nullptr;
+}
+
+}  // namespace android::nativeloader
diff --git a/libnativeloader/library_namespaces.h b/libnativeloader/library_namespaces.h
new file mode 100644
index 0000000..fd46cdc
--- /dev/null
+++ b/libnativeloader/library_namespaces.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
+#if !defined(__ANDROID__)
+#error "Not available for host"
+#endif
+
+#define LOG_TAG "nativeloader"
+
+#include "native_loader_namespace.h"
+
+#include <list>
+#include <string>
+
+#include <jni.h>
+
+namespace android::nativeloader {
+
+// LibraryNamespaces is a singleton object that manages NativeLoaderNamespace
+// objects for an app process. Its main job is to create (and configure) a new
+// NativeLoaderNamespace object for a Java ClassLoader, and to find an existing
+// object for a given ClassLoader.
+class LibraryNamespaces {
+ public:
+  LibraryNamespaces() : initialized_(false) {}
+
+  LibraryNamespaces(LibraryNamespaces&&) = default;
+  LibraryNamespaces(const LibraryNamespaces&) = delete;
+  LibraryNamespaces& operator=(const LibraryNamespaces&) = delete;
+
+  void Initialize();
+  void Reset() { namespaces_.clear(); }
+  NativeLoaderNamespace* Create(JNIEnv* env, uint32_t target_sdk_version, jobject class_loader,
+                                bool is_shared, jstring dex_path, jstring java_library_path,
+                                jstring java_permitted_path, std::string* error_msg);
+  NativeLoaderNamespace* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+
+ private:
+  bool InitPublicNamespace(const char* library_path, std::string* error_msg);
+  NativeLoaderNamespace* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+
+  bool initialized_;
+  std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
+};
+
+}  // namespace android::nativeloader
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 7fef106..0c29324 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -14,21 +14,13 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "nativeloader"
+
 #include "nativeloader/native_loader.h"
-#include <nativehelper/ScopedUtfChars.h>
 
 #include <dlfcn.h>
-#ifdef __ANDROID__
-#define LOG_TAG "libnativeloader"
-#include "nativeloader/dlext_namespaces.h"
-#include "cutils/properties.h"
-#include "log/log.h"
-#endif
-#include <dirent.h>
 #include <sys/types.h>
-#include "nativebridge/native_bridge.h"
 
-#include <algorithm>
 #include <memory>
 #include <mutex>
 #include <string>
@@ -37,557 +29,49 @@
 #include <android-base/file.h>
 #include <android-base/macros.h>
 #include <android-base/strings.h>
+#include <nativebridge/native_bridge.h>
+#include <nativehelper/ScopedUtfChars.h>
 
-#ifdef __BIONIC__
-#include <android-base/properties.h>
+#ifdef __ANDROID__
+#include <log/log.h>
+#include "library_namespaces.h"
+#include "nativeloader/dlext_namespaces.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 {
 
+namespace {
 #if defined(__ANDROID__)
-class NativeLoaderNamespace {
- public:
-  NativeLoaderNamespace()
-      : android_ns_(nullptr), native_bridge_ns_(nullptr) { }
+using android::nativeloader::LibraryNamespaces;
 
-  explicit NativeLoaderNamespace(android_namespace_t* ns)
-      : android_ns_(ns), native_bridge_ns_(nullptr) { }
+constexpr const char* kApexPath = "/apex/";
 
-  explicit NativeLoaderNamespace(native_bridge_namespace_t* ns)
-      : android_ns_(nullptr), native_bridge_ns_(ns) { }
+std::mutex g_namespaces_mutex;
+LibraryNamespaces* g_namespaces = new LibraryNamespaces;
 
-  NativeLoaderNamespace(NativeLoaderNamespace&& that) = default;
-  NativeLoaderNamespace(const NativeLoaderNamespace& that) = default;
-
-  NativeLoaderNamespace& operator=(const NativeLoaderNamespace& that) = default;
-
-  android_namespace_t* get_android_ns() const {
-    CHECK(native_bridge_ns_ == nullptr);
-    return android_ns_;
+android_namespace_t* FindExportedNamespace(const char* caller_location) {
+  std::string location = caller_location;
+  // Lots of implicit assumptions here: we expect `caller_location` to be of the form:
+  // /apex/com.android...modulename/...
+  //
+  // And we extract from it 'modulename', which is the name of the linker namespace.
+  if (android::base::StartsWith(location, kApexPath)) {
+    size_t slash_index = location.find_first_of('/', strlen(kApexPath));
+    LOG_ALWAYS_FATAL_IF((slash_index == std::string::npos),
+                        "Error finding namespace of apex: no slash in path %s", caller_location);
+    size_t dot_index = location.find_last_of('.', slash_index);
+    LOG_ALWAYS_FATAL_IF((dot_index == std::string::npos),
+                        "Error finding namespace of apex: no dot in apex name %s", caller_location);
+    std::string name = location.substr(dot_index + 1, slash_index - dot_index - 1);
+    android_namespace_t* boot_namespace = android_get_exported_namespace(name.c_str());
+    LOG_ALWAYS_FATAL_IF((boot_namespace == nullptr),
+                        "Error finding namespace of apex: no namespace called %s", name.c_str());
+    return boot_namespace;
   }
-
-  native_bridge_namespace_t* get_native_bridge_ns() const {
-    CHECK(android_ns_ == nullptr);
-    return native_bridge_ns_;
-  }
-
-  bool is_android_namespace() const {
-    return native_bridge_ns_ == nullptr;
-  }
-
- private:
-  // Only one of them can be not null
-  android_namespace_t* android_ns_;
-  native_bridge_namespace_t* native_bridge_ns_;
-};
-
-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
-// to use to load vendor libraries to separate namespace with controlled interface between
-// 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.
-static constexpr const char* kWhitelistedDirectories = "/data:/mnt/expand";
-
-static bool is_debuggable() {
-  char debuggable[PROP_VALUE_MAX];
-  property_get("ro.debuggable", debuggable, "0");
-  return std::string(debuggable) == "1";
+  return nullptr;
 }
-
-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,
-              bool is_for_vendor,
-              jstring java_library_path,
-              jstring java_permitted_path,
-              NativeLoaderNamespace* ns,
-              std::string* error_msg) {
-    std::string library_path; // empty string by default.
-
-    if (java_library_path != nullptr) {
-      ScopedUtfChars library_path_utf_chars(env, java_library_path);
-      library_path = library_path_utf_chars.c_str();
-    }
-
-    // (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 part effectively allows such a classloader to access anything
-    // under /data and /mnt/expand
-    std::string permitted_path = kWhitelistedDirectories;
-
-    if (java_permitted_path != nullptr) {
-      ScopedUtfChars path(env, java_permitted_path);
-      if (path.c_str() != nullptr && path.size() > 0) {
-        permitted_path = permitted_path + ":" + path.c_str();
-      }
-    }
-
-    if (!initialized_ && !InitPublicNamespace(library_path.c_str(), error_msg)) {
-      return false;
-    }
-
-    bool found = FindNamespaceByClassLoader(env, class_loader, nullptr);
-
-    LOG_ALWAYS_FATAL_IF(found,
-                        "There is already a namespace associated with this classloader");
-
-    uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
-    if (is_shared) {
-      namespace_type |= ANDROID_NAMESPACE_TYPE_SHARED;
-    }
-
-    if (target_sdk_version < 24) {
-      namespace_type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
-    }
-
-    NativeLoaderNamespace parent_ns;
-    bool found_parent_namespace = FindParentNamespaceByClassLoader(env, class_loader, &parent_ns);
-
-    bool is_native_bridge = false;
-
-    if (found_parent_namespace) {
-      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(namespace_name,
-                                                         nullptr,
-                                                         library_path.c_str(),
-                                                         namespace_type,
-                                                         permitted_path.c_str(),
-                                                         parent_ns.get_android_ns());
-      if (ns == nullptr) {
-        *error_msg = dlerror();
-        return false;
-      }
-
-      // Note that when vendor_ns is not configured this function will return nullptr
-      // and it will result in linking vendor_public_libraries_ to the default namespace
-      // which is expected behavior in this case.
-      android_namespace_t* vendor_ns = android_get_exported_namespace(kVendorNamespaceName);
-
-      if (!android_link_namespaces(ns, nullptr, system_exposed_libraries.c_str())) {
-        *error_msg = dlerror();
-        return false;
-      }
-
-      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 false;
-        }
-      }
-
-      if (!vendor_public_libraries_.empty()) {
-        if (!android_link_namespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
-          *error_msg = dlerror();
-          return false;
-        }
-      }
-
-      native_loader_ns = NativeLoaderNamespace(ns);
-    } else {
-      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());
-
-      if (ns == nullptr) {
-        *error_msg = NativeBridgeGetError();
-        return false;
-      }
-
-      native_bridge_namespace_t* vendor_ns = NativeBridgeGetVendorNamespace();
-
-      if (!NativeBridgeLinkNamespaces(ns, nullptr, system_exposed_libraries.c_str())) {
-        *error_msg = NativeBridgeGetError();
-        return false;
-      }
-
-      if (!vendor_public_libraries_.empty()) {
-        if (!NativeBridgeLinkNamespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
-          *error_msg = NativeBridgeGetError();
-          return false;
-        }
-      }
-
-      native_loader_ns = NativeLoaderNamespace(ns);
-    }
-
-    namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), native_loader_ns));
-
-    *ns = native_loader_ns;
-    return true;
-  }
-
-  bool FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader, NativeLoaderNamespace* ns) {
-    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 false;
-  }
-
-  void Initialize() {
-    // Once public namespace is initialized there is no
-    // point in running this code - it will have no effect
-    // on the current list of public libraries.
-    if (initialized_) {
-      return;
-    }
-
-    std::vector<std::string> sonames;
-    const char* android_root_env = getenv("ANDROID_ROOT");
-    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, 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.
-    if (is_debuggable()) {
-      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::back_inserter(sonames));
-      }
-    }
-
-    // android_init_namespaces() expects all the public libraries
-    // to be loaded so that they can be found by soname alone.
-    //
-    // 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 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());
-    }
-
-    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, always_true, nullptr);
-
-    vendor_public_libraries_ = base::Join(sonames, ':');
-  }
-
-  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;
-    if(!base::ReadFileToString(configFile, &file_content)) {
-      if (error_msg) *error_msg = strerror(errno);
-      return false;
-    }
-
-    std::vector<std::string> lines = base::Split(file_content, "\n");
-
-    for (auto& line : lines) {
-      auto trimmed_line = base::Trim(line);
-      if (trimmed_line[0] == '#' || trimmed_line.empty()) {
-        continue;
-      }
-      size_t space_pos = trimmed_line.rfind(' ');
-      if (space_pos != std::string::npos) {
-        std::string type = trimmed_line.substr(space_pos + 1);
-        if (type != "32" && type != "64") {
-          if (error_msg) *error_msg = "Malformed line: " + line;
-          return false;
-        }
-#if defined(__LP64__)
-        // Skip 32 bit public library.
-        if (type == "32") {
-          continue;
-        }
-#else
-        // Skip 64 bit public library.
-        if (type == "64") {
-          continue;
-        }
-#endif
-        trimmed_line.resize(space_pos);
-      }
-
-      if (check_soname(trimmed_line, error_msg)) {
-        sonames->push_back(trimmed_line);
-      } else {
-        return false;
-      }
-    }
-
-    return true;
-  }
-
-  bool InitPublicNamespace(const char* library_path, std::string* error_msg) {
-    // Ask native bride if this apps library path should be handled by it
-    bool is_native_bridge = NativeBridgeIsPathSupported(library_path);
-
-    // (http://b/25844435) - Some apps call dlopen from generated code (mono jited
-    // code is one example) unknown to linker in which  case linker uses anonymous
-    // namespace. The second argument specifies the search path for the anonymous
-    // namespace which is the library_path of the classloader.
-    initialized_ = android_init_anonymous_namespace(system_public_libraries_.c_str(),
-                                                    is_native_bridge ? nullptr : library_path);
-    if (!initialized_) {
-      *error_msg = dlerror();
-      return false;
-    }
-
-    // and now initialize native bridge namespaces if necessary.
-    if (NativeBridgeInitialized()) {
-      initialized_ = NativeBridgeInitAnonymousNamespace(system_public_libraries_.c_str(),
-                                                        is_native_bridge ? library_path : nullptr);
-      if (!initialized_) {
-        *error_msg = NativeBridgeGetError();
-      }
-    }
-
-    return initialized_;
-  }
-
-  jobject GetParentClassLoader(JNIEnv* env, jobject class_loader) {
-    jclass class_loader_class = env->FindClass("java/lang/ClassLoader");
-    jmethodID get_parent = env->GetMethodID(class_loader_class,
-                                            "getParent",
-                                            "()Ljava/lang/ClassLoader;");
-
-    return env->CallObjectMethod(class_loader, get_parent);
-  }
-
-  bool FindParentNamespaceByClassLoader(JNIEnv* env,
-                                        jobject class_loader,
-                                        NativeLoaderNamespace* ns) {
-    jobject parent_class_loader = GetParentClassLoader(env, class_loader);
-
-    while (parent_class_loader != nullptr) {
-      if (FindNamespaceByClassLoader(env, parent_class_loader, ns)) {
-        return true;
-      }
-
-      parent_class_loader = GetParentClassLoader(env, parent_class_loader);
-    }
-
-    return false;
-  }
-
-  bool initialized_;
-  std::vector<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);
-};
-
-static std::mutex g_namespaces_mutex;
-static LibraryNamespaces* g_namespaces = new LibraryNamespaces;
-#endif
+#endif  // #if defined(__ANDROID__)
+}  // namespace
 
 void InitializeNativeLoader() {
 #if defined(__ANDROID__)
@@ -603,91 +87,69 @@
 #endif
 }
 
-jstring CreateClassLoaderNamespace(JNIEnv* env,
-                                   int32_t target_sdk_version,
-                                   jobject class_loader,
-                                   bool is_shared,
-                                   bool is_for_vendor,
-                                   jstring library_path,
+jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader,
+                                   bool is_shared, jstring dex_path, 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);
+  bool success = g_namespaces->Create(env, target_sdk_version, class_loader, is_shared, dex_path,
+                                      library_path, permitted_path, &error_msg) != nullptr;
   if (!success) {
     return env->NewStringUTF(error_msg.c_str());
   }
 #else
-  UNUSED(env, target_sdk_version, class_loader, is_shared, is_for_vendor,
-         library_path, permitted_path);
+  UNUSED(env, target_sdk_version, class_loader, is_shared, dex_path, library_path, permitted_path);
 #endif
   return nullptr;
 }
 
-void* OpenNativeLibrary(JNIEnv* env,
-                        int32_t target_sdk_version,
-                        const char* path,
-                        jobject class_loader,
-                        jstring library_path,
-                        bool* needs_native_bridge,
-                        std::string* error_msg) {
+void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
+                        jobject class_loader, const char* caller_location, jstring library_path,
+                        bool* needs_native_bridge, char** error_msg) {
 #if defined(__ANDROID__)
   UNUSED(target_sdk_version);
   if (class_loader == nullptr) {
     *needs_native_bridge = false;
-    return dlopen(path, RTLD_NOW);
+    if (caller_location != nullptr) {
+      android_namespace_t* boot_namespace = FindExportedNamespace(caller_location);
+      if (boot_namespace != nullptr) {
+        const android_dlextinfo dlextinfo = {
+            .flags = ANDROID_DLEXT_USE_NAMESPACE,
+            .library_namespace = boot_namespace,
+        };
+        void* handle = android_dlopen_ext(path, RTLD_NOW, &dlextinfo);
+        if (handle == nullptr) {
+          *error_msg = strdup(dlerror());
+        }
+        return handle;
+      }
+    }
+    void* handle = dlopen(path, RTLD_NOW);
+    if (handle == nullptr) {
+      *error_msg = strdup(dlerror());
+    }
+    return handle;
   }
 
   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 /* is_shared */,
-                              false /* is_for_vendor */,
-                              library_path,
-                              nullptr,
-                              &ns,
-                              error_msg)) {
+    std::string create_error_msg;
+    if ((ns = g_namespaces->Create(env, target_sdk_version, class_loader, false /* is_shared */,
+                                   nullptr, library_path, nullptr, &create_error_msg)) == nullptr) {
+      *error_msg = strdup(create_error_msg.c_str());
       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 OpenNativeLibraryInNamespace(ns, path, needs_native_bridge, error_msg);
 #else
-  UNUSED(env, target_sdk_version, class_loader);
+  UNUSED(env, target_sdk_version, class_loader, caller_location);
 
   // Do some best effort to emulate library-path support. It will not
   // work for dependencies.
@@ -726,33 +188,64 @@
       if (handle != nullptr) {
         return handle;
       }
-      *error_msg = NativeBridgeGetError();
+      *error_msg = strdup(NativeBridgeGetError());
     } else {
-      *error_msg = dlerror();
+      *error_msg = strdup(dlerror());
     }
   }
   return nullptr;
 #endif
 }
 
-bool CloseNativeLibrary(void* handle, const bool needs_native_bridge) {
-    return needs_native_bridge ? NativeBridgeUnloadLibrary(handle) :
-                                 dlclose(handle);
+bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, char** error_msg) {
+  bool success;
+  if (needs_native_bridge) {
+    success = (NativeBridgeUnloadLibrary(handle) == 0);
+    if (!success) {
+      *error_msg = strdup(NativeBridgeGetError());
+    }
+  } else {
+    success = (dlclose(handle) == 0);
+    if (!success) {
+      *error_msg = strdup(dlerror());
+    }
+  }
+
+  return success;
+}
+
+void NativeLoaderFreeErrorMessage(char* msg) {
+  // The error messages get allocated through strdup, so we must call free on them.
+  free(msg);
 }
 
 #if defined(__ANDROID__)
+void* OpenNativeLibraryInNamespace(NativeLoaderNamespace* ns, const char* path,
+                                   bool* needs_native_bridge, char** error_msg) {
+  void* handle = ns->Load(path);
+  if (handle == nullptr) {
+    *error_msg = ns->GetError();
+  }
+  *needs_native_bridge = ns->IsBridged();
+  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 && !ns->IsBridged()) {
+    return ns->ToRawAndroidNamespace();
   }
-
   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
+};  // namespace android
diff --git a/libnativeloader/native_loader_lazy.cpp b/libnativeloader/native_loader_lazy.cpp
new file mode 100644
index 0000000..2eb1203
--- /dev/null
+++ b/libnativeloader/native_loader_lazy.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "nativeloader/native_loader.h"
+#define LOG_TAG "nativeloader"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+
+#include <log/log.h>
+
+namespace android {
+
+namespace {
+
+void* GetLibHandle() {
+  static void* handle = dlopen("libnativeloader.so", RTLD_NOW);
+  LOG_FATAL_IF(handle == nullptr, "Failed to load libnativeloader.so: %s", dlerror());
+  return handle;
+}
+
+template <typename FuncPtr>
+FuncPtr GetFuncPtr(const char* function_name) {
+  auto f = reinterpret_cast<FuncPtr>(dlsym(GetLibHandle(), function_name));
+  LOG_FATAL_IF(f == nullptr, "Failed to get address of %s: %s", function_name, dlerror());
+  return f;
+}
+
+#define GET_FUNC_PTR(name) GetFuncPtr<decltype(&name)>(#name)
+
+}  // namespace
+
+void InitializeNativeLoader() {
+  static auto f = GET_FUNC_PTR(InitializeNativeLoader);
+  return f();
+}
+
+jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader,
+                                   bool is_shared, jstring dex_path, jstring library_path,
+                                   jstring permitted_path) {
+  static auto f = GET_FUNC_PTR(CreateClassLoaderNamespace);
+  return f(env, target_sdk_version, class_loader, is_shared, dex_path, library_path,
+           permitted_path);
+}
+
+void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
+                        jobject class_loader, const char* caller_location, jstring library_path,
+                        bool* needs_native_bridge, char** error_msg) {
+  static auto f = GET_FUNC_PTR(OpenNativeLibrary);
+  return f(env, target_sdk_version, path, class_loader, caller_location, library_path,
+           needs_native_bridge, error_msg);
+}
+
+bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, char** error_msg) {
+  static auto f = GET_FUNC_PTR(CloseNativeLibrary);
+  return f(handle, needs_native_bridge, error_msg);
+}
+
+void NativeLoaderFreeErrorMessage(char* msg) {
+  static auto f = GET_FUNC_PTR(NativeLoaderFreeErrorMessage);
+  return f(msg);
+}
+
+struct android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+  static auto f = GET_FUNC_PTR(FindNamespaceByClassLoader);
+  return f(env, class_loader);
+}
+
+struct NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env,
+                                                                     jobject class_loader) {
+  static auto f = GET_FUNC_PTR(FindNativeLoaderNamespaceByClassLoader);
+  return f(env, class_loader);
+}
+
+void* OpenNativeLibraryInNamespace(struct NativeLoaderNamespace* ns, const char* path,
+                                   bool* needs_native_bridge, char** error_msg) {
+  static auto f = GET_FUNC_PTR(OpenNativeLibraryInNamespace);
+  return f(ns, path, needs_native_bridge, error_msg);
+}
+
+void ResetNativeLoader() {
+  static auto f = GET_FUNC_PTR(ResetNativeLoader);
+  return f();
+}
+
+#undef GET_FUNC_PTR
+
+}  // namespace android
diff --git a/libnativeloader/native_loader_namespace.cpp b/libnativeloader/native_loader_namespace.cpp
new file mode 100644
index 0000000..58ac686
--- /dev/null
+++ b/libnativeloader/native_loader_namespace.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "nativeloader"
+
+#include "native_loader_namespace.h"
+
+#include <dlfcn.h>
+
+#include <functional>
+
+#include <android-base/strings.h>
+#include <log/log.h>
+#include <nativebridge/native_bridge.h>
+
+#include "nativeloader/dlext_namespaces.h"
+
+namespace android {
+
+namespace {
+
+constexpr const char* kDefaultNamespaceName = "default";
+constexpr const char* kPlatformNamespaceName = "platform";
+
+}  // namespace
+
+NativeLoaderNamespace NativeLoaderNamespace::GetExportedNamespace(const std::string& name,
+                                                                  bool is_bridged) {
+  if (!is_bridged) {
+    return NativeLoaderNamespace(name, android_get_exported_namespace(name.c_str()));
+  } else {
+    return NativeLoaderNamespace(name, NativeBridgeGetExportedNamespace(name.c_str()));
+  }
+}
+
+char* NativeLoaderNamespace::GetError() const {
+  if (!IsBridged()) {
+    return strdup(dlerror());
+  } else {
+    return strdup(NativeBridgeGetError());
+  }
+}
+
+// The platform namespace is called "default" for binaries in /system and
+// "platform" for those in the Runtime APEX. Try "platform" first since
+// "default" always exists.
+NativeLoaderNamespace NativeLoaderNamespace::GetPlatformNamespace(bool is_bridged) {
+  NativeLoaderNamespace ns = GetExportedNamespace(kPlatformNamespaceName, is_bridged);
+  if (ns.IsNil()) {
+    ns = GetExportedNamespace(kDefaultNamespaceName, is_bridged);
+  }
+  return ns;
+}
+
+NativeLoaderNamespace NativeLoaderNamespace::Create(const std::string& name,
+                                                    const std::string& search_paths,
+                                                    const std::string& permitted_paths,
+                                                    const NativeLoaderNamespace* parent,
+                                                    bool is_shared, bool is_greylist_enabled) {
+  bool is_bridged = false;
+  if (parent != nullptr) {
+    is_bridged = parent->IsBridged();
+  } else if (!search_paths.empty()) {
+    is_bridged = NativeBridgeIsPathSupported(search_paths.c_str());
+  }
+
+  // Fall back to the platform namespace if no parent is set.
+  const NativeLoaderNamespace& effective_parent =
+      parent != nullptr ? *parent : GetPlatformNamespace(is_bridged);
+
+  uint64_t type = ANDROID_NAMESPACE_TYPE_ISOLATED;
+  if (is_shared) {
+    type |= ANDROID_NAMESPACE_TYPE_SHARED;
+  }
+  if (is_greylist_enabled) {
+    type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
+  }
+
+  if (!is_bridged) {
+    android_namespace_t* raw =
+        android_create_namespace(name.c_str(), nullptr, search_paths.c_str(), type,
+                                 permitted_paths.c_str(), effective_parent.ToRawAndroidNamespace());
+    return NativeLoaderNamespace(name, raw);
+  } else {
+    native_bridge_namespace_t* raw = NativeBridgeCreateNamespace(
+        name.c_str(), nullptr, search_paths.c_str(), type, permitted_paths.c_str(),
+        effective_parent.ToRawNativeBridgeNamespace());
+    return NativeLoaderNamespace(name, raw);
+  }
+}
+
+bool NativeLoaderNamespace::Link(const NativeLoaderNamespace& target,
+                                 const std::string& shared_libs) const {
+  LOG_ALWAYS_FATAL_IF(shared_libs.empty(), "empty share lib when linking %s to %s",
+                      this->name().c_str(), target.name().c_str());
+  if (!IsBridged()) {
+    return android_link_namespaces(this->ToRawAndroidNamespace(), target.ToRawAndroidNamespace(),
+                                   shared_libs.c_str());
+  } else {
+    return NativeBridgeLinkNamespaces(this->ToRawNativeBridgeNamespace(),
+                                      target.ToRawNativeBridgeNamespace(), shared_libs.c_str());
+  }
+}
+
+void* NativeLoaderNamespace::Load(const char* lib_name) const {
+  if (!IsBridged()) {
+    android_dlextinfo extinfo;
+    extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+    extinfo.library_namespace = this->ToRawAndroidNamespace();
+    return android_dlopen_ext(lib_name, RTLD_NOW, &extinfo);
+  } else {
+    return NativeBridgeLoadLibraryExt(lib_name, RTLD_NOW, this->ToRawNativeBridgeNamespace());
+  }
+}
+
+}  // namespace android
diff --git a/libnativeloader/native_loader_namespace.h b/libnativeloader/native_loader_namespace.h
new file mode 100644
index 0000000..71e4247
--- /dev/null
+++ b/libnativeloader/native_loader_namespace.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
+#if defined(__ANDROID__)
+
+#include <string>
+#include <variant>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android/dlext.h>
+#include <log/log.h>
+#include <nativebridge/native_bridge.h>
+
+namespace android {
+
+// NativeLoaderNamespace abstracts a linker namespace for the native
+// architecture (ex: arm on arm) or the translated architecture (ex: arm on
+// x86). Instances of this class are managed by LibraryNamespaces object.
+struct NativeLoaderNamespace {
+ public:
+  // TODO(return with errors)
+  static NativeLoaderNamespace Create(const std::string& name, const std::string& search_paths,
+                                      const std::string& permitted_paths,
+                                      const NativeLoaderNamespace* parent, bool is_shared,
+                                      bool is_greylist_enabled);
+
+  NativeLoaderNamespace(NativeLoaderNamespace&&) = default;
+  NativeLoaderNamespace(const NativeLoaderNamespace&) = default;
+  NativeLoaderNamespace& operator=(const NativeLoaderNamespace&) = default;
+
+  android_namespace_t* ToRawAndroidNamespace() const { return std::get<0>(raw_); }
+  native_bridge_namespace_t* ToRawNativeBridgeNamespace() const { return std::get<1>(raw_); }
+
+  std::string name() const { return name_; }
+  bool IsBridged() const { return raw_.index() == 1; }
+  bool IsNil() const {
+    return IsBridged() ? std::get<1>(raw_) == nullptr : std::get<0>(raw_) == nullptr;
+  }
+
+  bool Link(const NativeLoaderNamespace& target, const std::string& shared_libs) const;
+  void* Load(const char* lib_name) const;
+  char* GetError() const;
+
+  static NativeLoaderNamespace GetExportedNamespace(const std::string& name, bool is_bridged);
+  static NativeLoaderNamespace GetPlatformNamespace(bool is_bridged);
+
+ private:
+  explicit NativeLoaderNamespace(const std::string& name, android_namespace_t* ns)
+      : name_(name), raw_(ns) {}
+  explicit NativeLoaderNamespace(const std::string& name, native_bridge_namespace_t* ns)
+      : name_(name), raw_(ns) {}
+
+  std::string name_;
+  std::variant<android_namespace_t*, native_bridge_namespace_t*> raw_;
+};
+
+}  // namespace android
+#endif  // #if defined(__ANDROID__)
diff --git a/libnativeloader/public_libraries.cpp b/libnativeloader/public_libraries.cpp
new file mode 100644
index 0000000..c205eb1
--- /dev/null
+++ b/libnativeloader/public_libraries.cpp
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "nativeloader"
+
+#include "public_libraries.h"
+
+#include <dirent.h>
+
+#include <algorithm>
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <log/log.h>
+
+#include "utils.h"
+
+namespace android::nativeloader {
+
+using namespace std::string_literals;
+
+namespace {
+
+constexpr const char* kDefaultPublicLibrariesFile = "/etc/public.libraries.txt";
+constexpr const char* kExtendedPublicLibrariesFilePrefix = "public.libraries-";
+constexpr const char* kExtendedPublicLibrariesFileSuffix = ".txt";
+constexpr const char* kVendorPublicLibrariesFile = "/vendor/etc/public.libraries.txt";
+constexpr const char* kLlndkLibrariesFile = "/system/etc/llndk.libraries.txt";
+constexpr const char* kVndkLibrariesFile = "/system/etc/vndksp.libraries.txt";
+
+const std::vector<const std::string> kRuntimePublicLibraries = {
+    "libicuuc.so",
+    "libicui18n.so",
+};
+
+constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/" LIB;
+
+// TODO(b/130388701): do we need this?
+std::string root_dir() {
+  static const char* android_root_env = getenv("ANDROID_ROOT");
+  return android_root_env != nullptr ? android_root_env : "/system";
+}
+
+bool debuggable() {
+  static bool debuggable = android::base::GetBoolProperty("ro.debuggable", false);
+  return debuggable;
+}
+
+std::string vndk_version_str() {
+  static std::string version = android::base::GetProperty("ro.vndk.version", "");
+  if (version != "" && version != "current") {
+    return "." + version;
+  }
+  return "";
+}
+
+// For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment
+// variable to add libraries to the list. This is intended for platform tests only.
+std::string additional_public_libraries() {
+  if (debuggable()) {
+    const char* val = getenv("ANDROID_ADDITIONAL_PUBLIC_LIBRARIES");
+    return val ? val : "";
+  }
+  return "";
+}
+
+void InsertVndkVersionStr(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());
+}
+
+const std::function<bool(const std::string&, std::string*)> always_true =
+    [](const std::string&, std::string*) { return true; };
+
+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;
+  if (!base::ReadFileToString(configFile, &file_content)) {
+    if (error_msg) *error_msg = strerror(errno);
+    return false;
+  }
+
+  std::vector<std::string> lines = base::Split(file_content, "\n");
+
+  for (auto& line : lines) {
+    auto trimmed_line = base::Trim(line);
+    if (trimmed_line[0] == '#' || trimmed_line.empty()) {
+      continue;
+    }
+    size_t space_pos = trimmed_line.rfind(' ');
+    if (space_pos != std::string::npos) {
+      std::string type = trimmed_line.substr(space_pos + 1);
+      if (type != "32" && type != "64") {
+        if (error_msg) *error_msg = "Malformed line: " + line;
+        return false;
+      }
+#if defined(__LP64__)
+      // Skip 32 bit public library.
+      if (type == "32") {
+        continue;
+      }
+#else
+      // Skip 64 bit public library.
+      if (type == "64") {
+        continue;
+      }
+#endif
+      trimmed_line.resize(space_pos);
+    }
+
+    if (check_soname(trimmed_line, error_msg)) {
+      sonames->push_back(trimmed_line);
+    } else {
+      return false;
+    }
+  }
+  return true;
+}
+
+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);
+      std::string_view fn = filename;
+      if (android::base::ConsumePrefix(&fn, kExtendedPublicLibrariesFilePrefix) &&
+          android::base::ConsumeSuffix(&fn, kExtendedPublicLibrariesFileSuffix)) {
+        const std::string company_name(fn);
+        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());
+      }
+    }
+  }
+}
+
+static std::string InitDefaultPublicLibraries() {
+  std::string config_file = root_dir() + kDefaultPublicLibrariesFile;
+  std::vector<std::string> sonames;
+  std::string error_msg;
+  LOG_ALWAYS_FATAL_IF(!ReadConfig(config_file, &sonames, always_true, &error_msg),
+                      "Error reading public native library list from \"%s\": %s",
+                      config_file.c_str(), error_msg.c_str());
+
+  std::string additional_libs = additional_public_libraries();
+  if (!additional_libs.empty()) {
+    auto vec = base::Split(additional_libs, ":");
+    std::copy(vec.begin(), vec.end(), std::back_inserter(sonames));
+  }
+
+  // Remove the public libs in the runtime namespace.
+  // These libs are listed in public.android.txt, but we don't want the rest of android
+  // in default namespace to dlopen the libs.
+  // For example, libicuuc.so is exposed to classloader namespace from runtime namespace.
+  // Unfortunately, it does not have stable C symbols, and default namespace should only use
+  // stable symbols in libandroidicu.so. http://b/120786417
+  for (const std::string& lib_name : kRuntimePublicLibraries) {
+    std::string path(kRuntimeApexLibPath);
+    path.append("/").append(lib_name);
+
+    struct stat s;
+    // Do nothing if the path in /apex does not exist.
+    // Runtime APEX must be mounted since libnativeloader is in the same APEX
+    if (stat(path.c_str(), &s) != 0) {
+      continue;
+    }
+
+    auto it = std::find(sonames.begin(), sonames.end(), lib_name);
+    if (it != sonames.end()) {
+      sonames.erase(it);
+    }
+  }
+  return android::base::Join(sonames, ':');
+}
+
+static std::string InitRuntimePublicLibraries() {
+  CHECK(sizeof(kRuntimePublicLibraries) > 0);
+  std::string list = android::base::Join(kRuntimePublicLibraries, ":");
+
+  std::string additional_libs = additional_public_libraries();
+  if (!additional_libs.empty()) {
+    list = list + ':' + additional_libs;
+  }
+  return list;
+}
+
+static std::string InitVendorPublicLibraries() {
+  // This file is optional, quietly ignore if the file does not exist.
+  std::vector<std::string> sonames;
+  ReadConfig(kVendorPublicLibrariesFile, &sonames, always_true, nullptr);
+  return android::base::Join(sonames, ':');
+}
+
+// read /system/etc/public.libraries-<companyname>.txt and
+// /product/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.
+static std::string InitExtendedPublicLibraries() {
+  std::vector<std::string> sonames;
+  ReadExtensionLibraries("/system/etc", &sonames);
+  ReadExtensionLibraries("/product/etc", &sonames);
+  return android::base::Join(sonames, ':');
+}
+
+static std::string InitLlndkLibraries() {
+  std::string config_file = kLlndkLibrariesFile;
+  InsertVndkVersionStr(&config_file);
+  std::vector<std::string> sonames;
+  ReadConfig(config_file, &sonames, always_true, nullptr);
+  return android::base::Join(sonames, ':');
+}
+
+static std::string InitVndkspLibraries() {
+  std::string config_file = kVndkLibrariesFile;
+  InsertVndkVersionStr(&config_file);
+  std::vector<std::string> sonames;
+  ReadConfig(config_file, &sonames, always_true, nullptr);
+  return android::base::Join(sonames, ':');
+}
+
+}  // namespace
+
+const std::string& default_public_libraries() {
+  static std::string list = InitDefaultPublicLibraries();
+  return list;
+}
+
+const std::string& runtime_public_libraries() {
+  static std::string list = InitRuntimePublicLibraries();
+  return list;
+}
+
+const std::string& vendor_public_libraries() {
+  static std::string list = InitVendorPublicLibraries();
+  return list;
+}
+
+const std::string& extended_public_libraries() {
+  static std::string list = InitExtendedPublicLibraries();
+  return list;
+}
+
+const std::string& llndk_libraries() {
+  static std::string list = InitLlndkLibraries();
+  return list;
+}
+
+const std::string& vndksp_libraries() {
+  static std::string list = InitVndkspLibraries();
+  return list;
+}
+
+}  // namespace android::nativeloader
diff --git a/libnativeloader/public_libraries.h b/libnativeloader/public_libraries.h
new file mode 100644
index 0000000..9b6dea8
--- /dev/null
+++ b/libnativeloader/public_libraries.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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>
+
+namespace android::nativeloader {
+
+// These provide the list of libraries that are available to the namespace for apps.
+// Not all of the libraries are available to apps. Depending on the context,
+// e.g., if it is a vendor app or not, different set of libraries are made available.
+const std::string& default_public_libraries();
+const std::string& runtime_public_libraries();
+const std::string& vendor_public_libraries();
+const std::string& extended_public_libraries();
+const std::string& llndk_libraries();
+const std::string& vndksp_libraries();
+
+};  // namespace android::nativeloader
diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp
index d528f30..4d5c53d 100644
--- a/libnativeloader/test/Android.bp
+++ b/libnativeloader/test/Android.bp
@@ -69,3 +69,14 @@
         "libbase",
     ],
 }
+
+// Build the test for the C API.
+cc_test {
+    name: "libnativeloader-api-tests",
+    host_supported: true,
+    test_per_src: true,
+    srcs: [
+        "api_test.c",
+    ],
+    header_libs: ["libnativeloader-headers"],
+}
diff --git a/libnativeloader/test/api_test.c b/libnativeloader/test/api_test.c
new file mode 100644
index 0000000..e7025fd
--- /dev/null
+++ b/libnativeloader/test/api_test.c
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* The main purpose of this test is to ensure this C header compiles in C, so
+ * that no C++ features inadvertently leak into the C ABI. */
+#include "nativeloader/native_loader.h"
+
+int main(int argc, char** argv) {
+  (void)argc;
+  (void)argv;
+  return 0;
+}
diff --git a/libnativeloader/utils.h b/libnativeloader/utils.h
new file mode 100644
index 0000000..a1c2be5
--- /dev/null
+++ b/libnativeloader/utils.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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::nativeloader {
+
+#if defined(__LP64__)
+#define LIB "lib64"
+#else
+#define LIB "lib"
+#endif
+
+}  // namespace android::nativeloader
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
index 1d43775..268496f 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -6,6 +6,7 @@
     },
 
     srcs: [
+        "checksum.c",
         "dhcpclient.c",
         "dhcpmsg.c",
         "ifc_utils.c",
diff --git a/libnetutils/OWNERS b/libnetutils/OWNERS
index e3ec950..8321de6 100644
--- a/libnetutils/OWNERS
+++ b/libnetutils/OWNERS
@@ -1,3 +1,2 @@
-# TODO: should this be in system/netd?
-ek@google.com
-lorenzo@google.com
+include platform/system/netd:/OWNERS
+
diff --git a/libnetutils/checksum.c b/libnetutils/checksum.c
new file mode 100644
index 0000000..74b5fdd
--- /dev/null
+++ b/libnetutils/checksum.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2011 Daniel Drown
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * checksum.c - ipv4/ipv6 checksum calculation
+ */
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+
+#include "netutils/checksum.h"
+
+/* function: ip_checksum_add
+ * adds data to a checksum. only known to work on little-endian hosts
+ * current - the current checksum (or 0 to start a new checksum)
+ *   data        - the data to add to the checksum
+ *   len         - length of data
+ */
+uint32_t ip_checksum_add(uint32_t current, const void* data, int len) {
+    uint32_t checksum = current;
+    int left = len;
+    const uint16_t* data_16 = data;
+
+    while (left > 1) {
+        checksum += *data_16;
+        data_16++;
+        left -= 2;
+    }
+    if (left) {
+        checksum += *(uint8_t*)data_16;
+    }
+
+    return checksum;
+}
+
+/* function: ip_checksum_fold
+ * folds a 32-bit partial checksum into 16 bits
+ *   temp_sum - sum from ip_checksum_add
+ *   returns: the folded checksum in network byte order
+ */
+uint16_t ip_checksum_fold(uint32_t temp_sum) {
+    while (temp_sum > 0xffff) {
+        temp_sum = (temp_sum >> 16) + (temp_sum & 0xFFFF);
+    }
+    return temp_sum;
+}
+
+/* function: ip_checksum_finish
+ * folds and closes the checksum
+ *   temp_sum - sum from ip_checksum_add
+ *   returns: a header checksum value in network byte order
+ */
+uint16_t ip_checksum_finish(uint32_t temp_sum) {
+    return ~ip_checksum_fold(temp_sum);
+}
+
+/* function: ip_checksum
+ * combined ip_checksum_add and ip_checksum_finish
+ *   data - data to checksum
+ *   len  - length of data
+ */
+uint16_t ip_checksum(const void* data, int len) {
+    // TODO: consider starting from 0xffff so the checksum of a buffer entirely consisting of zeros
+    // is correctly calculated as 0.
+    uint32_t temp_sum;
+
+    temp_sum = ip_checksum_add(0, data, len);
+    return ip_checksum_finish(temp_sum);
+}
+
+/* function: ipv6_pseudo_header_checksum
+ * calculate the pseudo header checksum for use in tcp/udp/icmp headers
+ *   ip6      - the ipv6 header
+ *   len      - the transport length (transport header + payload)
+ *   protocol - the transport layer protocol, can be different from ip6->ip6_nxt for fragments
+ */
+uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr* ip6, uint32_t len, uint8_t protocol) {
+    uint32_t checksum_len = htonl(len);
+    uint32_t checksum_next = htonl(protocol);
+
+    uint32_t current = 0;
+
+    current = ip_checksum_add(current, &(ip6->ip6_src), sizeof(struct in6_addr));
+    current = ip_checksum_add(current, &(ip6->ip6_dst), sizeof(struct in6_addr));
+    current = ip_checksum_add(current, &checksum_len, sizeof(checksum_len));
+    current = ip_checksum_add(current, &checksum_next, sizeof(checksum_next));
+
+    return current;
+}
+
+/* function: ipv4_pseudo_header_checksum
+ * calculate the pseudo header checksum for use in tcp/udp headers
+ *   ip      - the ipv4 header
+ *   len     - the transport length (transport header + payload)
+ */
+uint32_t ipv4_pseudo_header_checksum(const struct iphdr* ip, uint16_t len) {
+    uint16_t temp_protocol, temp_length;
+
+    temp_protocol = htons(ip->protocol);
+    temp_length = htons(len);
+
+    uint32_t current = 0;
+
+    current = ip_checksum_add(current, &(ip->saddr), sizeof(uint32_t));
+    current = ip_checksum_add(current, &(ip->daddr), sizeof(uint32_t));
+    current = ip_checksum_add(current, &temp_protocol, sizeof(uint16_t));
+    current = ip_checksum_add(current, &temp_length, sizeof(uint16_t));
+
+    return current;
+}
+
+/* function: ip_checksum_adjust
+ * calculates a new checksum given a previous checksum and the old and new pseudo-header checksums
+ *   checksum    - the header checksum in the original packet in network byte order
+ *   old_hdr_sum - the pseudo-header checksum of the original packet
+ *   new_hdr_sum - the pseudo-header checksum of the translated packet
+ *   returns: the new header checksum in network byte order
+ */
+uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum) {
+    // Algorithm suggested in RFC 1624.
+    // http://tools.ietf.org/html/rfc1624#section-3
+    checksum = ~checksum;
+    uint16_t folded_sum = ip_checksum_fold(checksum + new_hdr_sum);
+    uint16_t folded_old = ip_checksum_fold(old_hdr_sum);
+    if (folded_sum > folded_old) {
+        return ~(folded_sum - folded_old);
+    } else {
+        return ~(folded_sum - folded_old - 1);  // end-around borrow
+    }
+}
diff --git a/libnetutils/include/netutils/checksum.h b/libnetutils/include/netutils/checksum.h
new file mode 100644
index 0000000..868217c
--- /dev/null
+++ b/libnetutils/include/netutils/checksum.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2011 Daniel Drown
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * checksum.h - checksum functions
+ */
+#ifndef __CHECKSUM_H__
+#define __CHECKSUM_H__
+
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <stdint.h>
+
+uint32_t ip_checksum_add(uint32_t current, const void* data, int len);
+uint16_t ip_checksum_finish(uint32_t temp_sum);
+uint16_t ip_checksum(const void* data, int len);
+
+uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr* ip6, uint32_t len, uint8_t protocol);
+uint32_t ipv4_pseudo_header_checksum(const struct iphdr* ip, uint16_t len);
+
+uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum);
+
+#endif /* __CHECKSUM_H__ */
diff --git a/libpackagelistparser/Android.bp b/libpackagelistparser/Android.bp
index 27693b3..c38594a 100644
--- a/libpackagelistparser/Android.bp
+++ b/libpackagelistparser/Android.bp
@@ -1,6 +1,7 @@
 cc_library {
 
     name: "libpackagelistparser",
+    recovery_available: true,
     srcs: ["packagelistparser.c"],
     cflags: [
         "-Wall",
diff --git a/libpackagelistparser/include/packagelistparser/packagelistparser.h b/libpackagelistparser/include/packagelistparser/packagelistparser.h
index d602c26..3cb6b9a 100644
--- a/libpackagelistparser/include/packagelistparser/packagelistparser.h
+++ b/libpackagelistparser/include/packagelistparser/packagelistparser.h
@@ -53,6 +53,8 @@
     char *seinfo;
     gid_list gids;
     void *private_data;
+    bool profileable_from_shell;
+    long version_code;
 };
 
 /**
diff --git a/libpackagelistparser/packagelistparser.c b/libpackagelistparser/packagelistparser.c
index 3e1a3d1..edc533c 100644
--- a/libpackagelistparser/packagelistparser.c
+++ b/libpackagelistparser/packagelistparser.c
@@ -223,6 +223,32 @@
             }
         }
 
+        cur = strsep(&next, " \t\r\n");
+        if (cur) {
+            tmp = strtoul(cur, &endptr, 10);
+            if (*endptr != '\0') {
+                errmsg = "Could not convert field \"profileable_from_shell\" to integer value";
+                goto err;
+            }
+
+            /* should be a valid boolean of 1 or 0 */
+            if (!(tmp == 0 || tmp == 1)) {
+                errmsg = "Field \"profileable_from_shell\" is not 0 or 1 boolean value";
+                goto err;
+            }
+
+            pkg_info->profileable_from_shell = (bool)tmp;
+        }
+        cur = strsep(&next, " \t\r\n");
+        if (cur) {
+            tmp = strtoul(cur, &endptr, 10);
+            if (*endptr != '\0') {
+                errmsg = "Could not convert field \"versionCode\" to integer value";
+                goto err;
+            }
+            pkg_info->version_code = tmp;
+        }
+
         rc = callback(pkg_info, userdata);
         if (rc == false) {
             /*
diff --git a/libpixelflinger/Android.bp b/libpixelflinger/Android.bp
new file mode 100644
index 0000000..76d9444
--- /dev/null
+++ b/libpixelflinger/Android.bp
@@ -0,0 +1,115 @@
+cc_defaults {
+    name: "pixelflinger_defaults",
+
+    cflags: [
+        "-fstrict-aliasing",
+        "-fomit-frame-pointer",
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-function",
+    ],
+    export_include_dirs: ["include"],
+    header_libs: ["libbase_headers"],
+    shared_libs: [
+        "libcutils",
+        "liblog",
+        "libutils",
+    ],
+
+    arch: {
+        arm: {
+            neon: {
+                cflags: ["-D__ARM_HAVE_NEON"],
+            },
+        },
+    },
+}
+
+cc_library_static {
+    name: "libpixelflinger-arm",
+    defaults: ["pixelflinger_defaults"],
+
+    srcs: [
+        "fixed.cpp",
+        "picker.cpp",
+        "pixelflinger.cpp",
+        "trap.cpp",
+        "scanline.cpp",
+    ],
+
+    arch: {
+        arm: {
+            instruction_set: "arm",
+        },
+    },
+}
+
+// For the tests to use
+cc_library_headers {
+    name: "libpixelflinger_internal",
+    export_include_dirs: [
+        "include",
+        ".",
+    ],
+}
+
+cc_library {
+    name: "libpixelflinger",
+    defaults: ["pixelflinger_defaults"],
+
+    srcs: [
+        "codeflinger/ARMAssemblerInterface.cpp",
+        "codeflinger/ARMAssemblerProxy.cpp",
+        "codeflinger/CodeCache.cpp",
+        "codeflinger/GGLAssembler.cpp",
+        "codeflinger/load_store.cpp",
+        "codeflinger/blending.cpp",
+        "codeflinger/texturing.cpp",
+        "format.cpp",
+        "clear.cpp",
+        "raster.cpp",
+        "buffer.cpp",
+    ],
+    whole_static_libs: ["libpixelflinger-arm"],
+
+    arch: {
+        arm: {
+            srcs: [
+                "codeflinger/ARMAssembler.cpp",
+                "codeflinger/disassem.c",
+                "col32cb16blend.S",
+                "t32cb16blend.S",
+            ],
+
+            neon: {
+                srcs: ["col32cb16blend_neon.S"],
+            },
+        },
+        arm64: {
+            srcs: [
+                "codeflinger/Arm64Assembler.cpp",
+                "codeflinger/Arm64Disassembler.cpp",
+                "arch-arm64/col32cb16blend.S",
+                "arch-arm64/t32cb16blend.S",
+            ],
+        },
+        mips: {
+            mips32r6: {
+                srcs: [
+                    "codeflinger/MIPSAssembler.cpp",
+                    "codeflinger/mips_disassem.c",
+                    "arch-mips/t32cb16blend.S",
+                ],
+            },
+        },
+        mips64: {
+            srcs: [
+                "codeflinger/MIPSAssembler.cpp",
+                "codeflinger/MIPS64Assembler.cpp",
+                "codeflinger/mips64_disassem.c",
+                "arch-mips64/col32cb16blend.S",
+                "arch-mips64/t32cb16blend.S",
+            ],
+        },
+    },
+}
diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
deleted file mode 100644
index c7306cd..0000000
--- a/libpixelflinger/Android.mk
+++ /dev/null
@@ -1,81 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-#
-# C/C++ and ARMv5 objects
-#
-
-include $(CLEAR_VARS)
-PIXELFLINGER_SRC_FILES:= \
-	codeflinger/ARMAssemblerInterface.cpp \
-	codeflinger/ARMAssemblerProxy.cpp \
-	codeflinger/CodeCache.cpp \
-	codeflinger/GGLAssembler.cpp \
-	codeflinger/load_store.cpp \
-	codeflinger/blending.cpp \
-	codeflinger/texturing.cpp \
-	fixed.cpp.arm \
-	picker.cpp.arm \
-	pixelflinger.cpp.arm \
-	trap.cpp.arm \
-	scanline.cpp.arm \
-	format.cpp \
-	clear.cpp \
-	raster.cpp \
-	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 \
-	codeflinger/disassem.c \
-	col32cb16blend.S \
-	t32cb16blend.S \
-
-ifeq ($(ARCH_ARM_HAVE_NEON),true)
-PIXELFLINGER_SRC_FILES_arm += col32cb16blend_neon.S
-PIXELFLINGER_CFLAGS_arm += -D__ARM_HAVE_NEON
-endif
-
-PIXELFLINGER_SRC_FILES_arm64 := \
-	codeflinger/Arm64Assembler.cpp \
-	codeflinger/Arm64Disassembler.cpp \
-	arch-arm64/col32cb16blend.S \
-	arch-arm64/t32cb16blend.S \
-
-ifndef ARCH_MIPS_REV6
-PIXELFLINGER_SRC_FILES_mips := \
-	codeflinger/MIPSAssembler.cpp \
-	codeflinger/mips_disassem.c \
-	arch-mips/t32cb16blend.S \
-
-endif
-
-PIXELFLINGER_SRC_FILES_mips64 := \
-        codeflinger/MIPSAssembler.cpp \
-	codeflinger/MIPS64Assembler.cpp \
-	codeflinger/mips64_disassem.c \
-	arch-mips64/col32cb16blend.S \
-	arch-mips64/t32cb16blend.S \
-
-#
-# Shared library
-#
-
-LOCAL_MODULE:= libpixelflinger
-LOCAL_SRC_FILES := $(PIXELFLINGER_SRC_FILES)
-LOCAL_SRC_FILES_arm := $(PIXELFLINGER_SRC_FILES_arm)
-LOCAL_SRC_FILES_arm64 := $(PIXELFLINGER_SRC_FILES_arm64)
-LOCAL_SRC_FILES_mips := $(PIXELFLINGER_SRC_FILES_mips)
-LOCAL_SRC_FILES_mips64 := $(PIXELFLINGER_SRC_FILES_mips64)
-LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS) \
-		    external/safe-iop/include
-LOCAL_SHARED_LIBRARIES := libcutils liblog libutils
-
-include $(BUILD_SHARED_LIBRARY)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libpixelflinger/arch-arm64/t32cb16blend.S b/libpixelflinger/arch-arm64/t32cb16blend.S
index b1a950d..a9733c0 100644
--- a/libpixelflinger/arch-arm64/t32cb16blend.S
+++ b/libpixelflinger/arch-arm64/t32cb16blend.S
@@ -49,7 +49,7 @@
  *      upper 16-bit pixels in DREG into FB
  *
  *
- * clobbered: w6, w7, w16, w17, w18
+ * clobbered: w6, w7, w15, w16, w17
  *
  */
 
@@ -73,8 +73,8 @@
     add     w16, w6, w16, lsr #8
     cmp     w16, #0x1F
     orr     w17, \FB, #(0x1F<<(16 + 11))
-    orr     w18, \FB, w16, lsl #(16 + 11)
-    csel    \FB, w17, w18, hi
+    orr     w15, \FB, w16, lsl #(16 + 11)
+    csel    \FB, w17, w15, hi
         // green
         and     w6, \DREG, #(0x3F<<(16 + 5))
         lsr     w17,w6,#(16+5)
@@ -84,8 +84,8 @@
         add     w6, w16, w6, lsr #8
         cmp     w6, #0x3F
         orr     w17, \FB, #(0x3F<<(16 + 5))
-        orr     w18, \FB, w6, lsl #(16 + 5)
-        csel    \FB, w17, w18, hi
+        orr     w15, \FB, w6, lsl #(16 + 5)
+        csel    \FB, w17, w15, hi
             // blue
             and     w16, \DREG, #(0x1F << 16)
             lsr     w17,w16,#16
@@ -95,8 +95,8 @@
             add     w16, w6, w16, lsr #8
             cmp     w16, #0x1F
             orr     w17, \FB, #(0x1F << 16)
-            orr     w18, \FB, w16, lsl #16
-            csel    \FB, w17, w18, hi
+            orr     w15, \FB, w16, lsl #16
+            csel    \FB, w17, w15, hi
 
 .else //Blending even pixel present in bottom 16 bits of DREG register
 
@@ -109,8 +109,8 @@
     add     w16, w6, w16, lsr #8
     cmp     w16, #0x1F
     mov     w17, #(0x1F<<11)
-    lsl     w18, w16, #11
-    csel    \FB, w17, w18, hi
+    lsl     w15, w16, #11
+    csel    \FB, w17, w15, hi
 
 
         // green
@@ -121,8 +121,8 @@
         add     w6, w16, w6, lsr #(5+8)
         cmp     w6, #0x3F
         orr     w17, \FB, #(0x3F<<5)
-        orr     w18, \FB, w6, lsl #5
-        csel    \FB, w17, w18, hi
+        orr     w15, \FB, w6, lsl #5
+        csel    \FB, w17, w15, hi
 
             // blue
             and     w16, \DREG, #0x1F
@@ -132,8 +132,8 @@
             add     w16, w6, w16, lsr #8
             cmp     w16, #0x1F
             orr     w17, \FB, #0x1F
-            orr     w18, \FB, w16
-            csel    \FB, w17, w18, hi
+            orr     w15, \FB, w16
+            csel    \FB, w17, w15, hi
 
 .endif // End of blending even pixel
 
diff --git a/libpixelflinger/buffer.cpp b/libpixelflinger/buffer.cpp
index dcb95c5..ea9514c 100644
--- a/libpixelflinger/buffer.cpp
+++ b/libpixelflinger/buffer.cpp
@@ -18,6 +18,8 @@
 
 #include <assert.h>
 
+#include <android-base/macros.h>
+
 #include "buffer.h"
 
 namespace android {
@@ -266,8 +268,11 @@
     p = downshift_component(p, b,   hbits, lbits,  f->bh, f->bl, 0, 1, -1);
     p = downshift_component(p, a,   hbits, lbits,  f->ah, f->al, 0, 1, -1);
     switch (f->size) {
-    case 1: p |= p << 8;    // fallthrough
-    case 2: p |= p << 16;
+        case 1:
+            p |= p << 8;
+            FALLTHROUGH_INTENDED;
+        case 2:
+            p |= p << 16;
     }
     return p;
 }
diff --git a/libpixelflinger/codeflinger/ARMAssembler.cpp b/libpixelflinger/codeflinger/ARMAssembler.cpp
index ac009a9..f47b6e4 100644
--- a/libpixelflinger/codeflinger/ARMAssembler.cpp
+++ b/libpixelflinger/codeflinger/ARMAssembler.cpp
@@ -171,7 +171,7 @@
     }
 
     mAssembly->resize( int(pc()-base())*4 );
-    
+
     // the instruction cache is 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";
@@ -183,8 +183,8 @@
         printf(format, name, int(pc()-base()), base(), pc(), duration);
         disassemble(name);
     }
-    
-    return NO_ERROR;
+
+    return OK;
 }
 
 uint32_t* ARMAssembler::pcForLabel(const char* label)
@@ -213,14 +213,14 @@
 // multiply...
 void ARMAssembler::MLA(int cc, int s,
         int Rd, int Rm, int Rs, int Rn) {
-    if (Rd == Rm) { int t = Rm; Rm=Rs; Rs=t; } 
+    if (Rd == Rm) { int t = Rm; Rm=Rs; Rs=t; }
     LOG_FATAL_IF(Rd==Rm, "MLA(r%u,r%u,r%u,r%u)", Rd,Rm,Rs,Rn);
     *mPC++ =    (cc<<28) | (1<<21) | (s<<20) |
                 (Rd<<16) | (Rn<<12) | (Rs<<8) | 0x90 | Rm;
 }
 void ARMAssembler::MUL(int cc, int s,
         int Rd, int Rm, int Rs) {
-    if (Rd == Rm) { int t = Rm; Rm=Rs; Rs=t; } 
+    if (Rd == Rm) { int t = Rm; Rm=Rs; Rs=t; }
     LOG_FATAL_IF(Rd==Rm, "MUL(r%u,r%u,r%u)", Rd,Rm,Rs);
     *mPC++ = (cc<<28) | (s<<20) | (Rd<<16) | (Rs<<8) | 0x90 | Rm;
 }
@@ -577,4 +577,3 @@
 }
 
 }; // namespace android
-
diff --git a/libpixelflinger/codeflinger/Arm64Assembler.cpp b/libpixelflinger/codeflinger/Arm64Assembler.cpp
index aebc129..8926776 100644
--- a/libpixelflinger/codeflinger/Arm64Assembler.cpp
+++ b/libpixelflinger/codeflinger/Arm64Assembler.cpp
@@ -325,7 +325,7 @@
         printf(format, name, int(pc()-base()), base(), pc(), duration);
         disassemble(name);
     }
-    return NO_ERROR;
+    return OK;
 }
 
 uint32_t* ArmToArm64Assembler::pcForLabel(const char* label)
@@ -1238,4 +1238,3 @@
 }
 
 }; // namespace android
-
diff --git a/libpixelflinger/codeflinger/CodeCache.cpp b/libpixelflinger/codeflinger/CodeCache.cpp
index 85166407..32691a3 100644
--- a/libpixelflinger/codeflinger/CodeCache.cpp
+++ b/libpixelflinger/codeflinger/CodeCache.cpp
@@ -61,7 +61,11 @@
 #define USAGE_ERROR_ACTION(m,p) \
     heap_error("ARGUMENT IS INVALID HEAP ADDRESS", __FUNCTION__, p)
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wexpansion-to-defined"
+#pragma GCC diagnostic ignored "-Wnull-pointer-arithmetic"
 #include "../../../../external/dlmalloc/malloc.c"
+#pragma GCC diagnostic pop
 
 static void heap_error(const char* msg, const char* function, void* p) {
     ALOG(LOG_FATAL, LOG_TAG, "@@@ ABORTING: CODE FLINGER: %s IN %s addr=%p",
diff --git a/libpixelflinger/codeflinger/MIPSAssembler.cpp b/libpixelflinger/codeflinger/MIPSAssembler.cpp
index 039a725..7de8cc1 100644
--- a/libpixelflinger/codeflinger/MIPSAssembler.cpp
+++ b/libpixelflinger/codeflinger/MIPSAssembler.cpp
@@ -1421,7 +1421,7 @@
         disassemble(name);
     }
 
-    return NO_ERROR;
+    return OK;
 }
 
 uint32_t* MIPSAssembler::pcForLabel(const char* label)
@@ -1953,5 +1953,3 @@
 
 
 }; // namespace android:
-
-
diff --git a/libpixelflinger/codeflinger/blending.cpp b/libpixelflinger/codeflinger/blending.cpp
index a55dfe3..2cbb00f 100644
--- a/libpixelflinger/codeflinger/blending.cpp
+++ b/libpixelflinger/codeflinger/blending.cpp
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include <sys/types.h>
 
+#include <android-base/macros.h>
 #include <log/log.h>
 
 #include "GGLAssembler.h"
@@ -301,7 +302,7 @@
                 return;
             }                
         }
-        // fall-through...
+        FALLTHROUGH_INTENDED;
     case GGL_ONE_MINUS_DST_COLOR:
     case GGL_DST_COLOR:
     case GGL_ONE_MINUS_SRC_COLOR:
diff --git a/libpixelflinger/fixed.cpp b/libpixelflinger/fixed.cpp
index 5094537..de6b479 100644
--- a/libpixelflinger/fixed.cpp
+++ b/libpixelflinger/fixed.cpp
@@ -70,17 +70,6 @@
 
 // ------------------------------------------------------------------------
 
-GGLfixed gglFastDivx(GGLfixed n, GGLfixed d)
-{
-    if ((d>>24) && ((d>>24)+1)) {
-        n >>= 8;
-        d >>= 8;
-    }
-    return gglMulx(n, gglRecip(d));
-}
-
-// ------------------------------------------------------------------------
-
 static const GGLfixed ggl_sqrt_reciproc_approx_tab[8] = {
     // 1/sqrt(x) with x = 1-N/16, N=[8...1]
     0x16A09, 0x15555, 0x143D1, 0x134BF, 0x1279A, 0x11C01, 0x111AC, 0x10865
diff --git a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
index 51e9e26..4217a89 100644
--- a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
+++ b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
@@ -86,7 +86,6 @@
 GGLfixed gglPowx(GGLfixed x, GGLfixed y) CONST;
 GGLfixed gglSqrtx(GGLfixed a) CONST;
 GGLfixed gglSqrtRecipx(GGLfixed x) CONST;
-GGLfixed gglFastDivx(GGLfixed n, GGLfixed d) CONST;
 int32_t gglMulDivi(int32_t a, int32_t b, int32_t c);
 
 int32_t gglRecipQNormalized(int32_t x, int* exponent);
@@ -108,7 +107,7 @@
 
 // inline ARM implementations
 inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) CONST;
-inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) {
+__attribute__((always_inline)) inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) {
     GGLfixed result, t;
     if (__builtin_constant_p(shift)) {
     asm("smull  %[lo], %[hi], %[x], %[y]            \n"
@@ -131,7 +130,8 @@
 }
 
 inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) CONST;
-inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) {
+__attribute__((always_inline)) inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a,
+                                                          int shift) {
     GGLfixed result, t;
     if (__builtin_constant_p(shift)) {
     asm("smull  %[lo], %[hi], %[x], %[y]            \n"
diff --git a/libpixelflinger/tests/Android.bp b/libpixelflinger/tests/Android.bp
new file mode 100644
index 0000000..e20dd93
--- /dev/null
+++ b/libpixelflinger/tests/Android.bp
@@ -0,0 +1,17 @@
+cc_defaults {
+    name: "pixelflinger-tests",
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    header_libs: ["libpixelflinger_internal"],
+    static_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+        "libpixelflinger",
+        "libutils",
+    ],
+}
diff --git a/libpixelflinger/tests/Android.mk b/libpixelflinger/tests/Android.mk
deleted file mode 100644
index 6571161..0000000
--- a/libpixelflinger/tests/Android.mk
+++ /dev/null
@@ -1 +0,0 @@
-include $(all-subdir-makefiles)
diff --git a/libpixelflinger/tests/arch-arm64/Android.bp b/libpixelflinger/tests/arch-arm64/Android.bp
new file mode 100644
index 0000000..2f5586a
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/Android.bp
@@ -0,0 +1,11 @@
+cc_defaults {
+    name: "pixelflinger-tests-arm64",
+    defaults: ["pixelflinger-tests"],
+
+    enabled: false,
+    arch: {
+        arm64: {
+            enabled: true,
+        },
+    },
+}
diff --git a/libpixelflinger/tests/arch-arm64/Android.mk b/libpixelflinger/tests/arch-arm64/Android.mk
deleted file mode 100644
index ca58b4b..0000000
--- a/libpixelflinger/tests/arch-arm64/Android.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-ifeq ($(TARGET_ARCH),arm64)
-include $(all-subdir-makefiles)
-endif
diff --git a/libpixelflinger/tests/arch-arm64/assembler/Android.bp b/libpixelflinger/tests/arch-arm64/assembler/Android.bp
new file mode 100644
index 0000000..003f485
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/assembler/Android.bp
@@ -0,0 +1,9 @@
+cc_test {
+    name: "test-pixelflinger-arm64-assembler-test",
+    defaults: ["pixelflinger-tests-arm64"],
+
+    srcs: [
+        "arm64_assembler_test.cpp",
+        "asm_test_jacket.S",
+    ],
+}
diff --git a/libpixelflinger/tests/arch-arm64/assembler/Android.mk b/libpixelflinger/tests/arch-arm64/assembler/Android.mk
deleted file mode 100644
index db5dc4d..0000000
--- a/libpixelflinger/tests/arch-arm64/assembler/Android.mk
+++ /dev/null
@@ -1,23 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-    arm64_assembler_test.cpp\
-    asm_test_jacket.S
-
-LOCAL_SHARED_LIBRARIES := \
-    libcutils \
-    libpixelflinger
-
-LOCAL_C_INCLUDES := \
-    $(LOCAL_PATH)/../../..
-
-LOCAL_MODULE:= test-pixelflinger-arm64-assembler-test
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.bp b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.bp
new file mode 100644
index 0000000..e640aeb
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+    name: "test-pixelflinger-arm64-col32cb16blend",
+    defaults: ["pixelflinger-tests-arm64"],
+
+    srcs: ["col32cb16blend_test.c"],
+}
diff --git a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
deleted file mode 100644
index 3096232..0000000
--- a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-    col32cb16blend_test.c \
-    ../../../arch-arm64/col32cb16blend.S
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_C_INCLUDES :=
-
-LOCAL_MODULE:= test-pixelflinger-arm64-col32cb16blend
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-arm64/disassembler/Android.bp b/libpixelflinger/tests/arch-arm64/disassembler/Android.bp
new file mode 100644
index 0000000..38dc99a
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/disassembler/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+    name: "test-pixelflinger-arm64-disassembler-test",
+    defaults: ["pixelflinger-tests-arm64"],
+
+    srcs: ["arm64_diassembler_test.cpp"],
+}
diff --git a/libpixelflinger/tests/arch-arm64/disassembler/Android.mk b/libpixelflinger/tests/arch-arm64/disassembler/Android.mk
deleted file mode 100644
index 78f12af..0000000
--- a/libpixelflinger/tests/arch-arm64/disassembler/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-    arm64_diassembler_test.cpp \
-    ../../../codeflinger/Arm64Disassembler.cpp
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_MODULE:= test-pixelflinger-arm64-disassembler-test
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.bp b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.bp
new file mode 100644
index 0000000..9d060d1
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+    name: "test-pixelflinger-arm64-t32cb16blend",
+    defaults: ["pixelflinger-tests-arm64"],
+
+    srcs: ["t32cb16blend_test.c"],
+}
diff --git a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
deleted file mode 100644
index 664347f..0000000
--- a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-    t32cb16blend_test.c \
-    ../../../arch-arm64/t32cb16blend.S
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_C_INCLUDES :=
-
-LOCAL_MODULE:= test-pixelflinger-arm64-t32cb16blend
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips/Android.bp b/libpixelflinger/tests/arch-mips/Android.bp
new file mode 100644
index 0000000..2ca2721
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/Android.bp
@@ -0,0 +1,11 @@
+cc_defaults {
+    name: "pixelflinger-tests-mips",
+    defaults: ["pixelflinger-tests"],
+
+    enabled: false,
+    arch: {
+        mips: {
+            enabled: true,
+        },
+    },
+}
diff --git a/libpixelflinger/tests/arch-mips/Android.mk b/libpixelflinger/tests/arch-mips/Android.mk
deleted file mode 100644
index fe6979e..0000000
--- a/libpixelflinger/tests/arch-mips/Android.mk
+++ /dev/null
@@ -1,6 +0,0 @@
-ifeq ($(TARGET_ARCH),mips)
-include $(all-subdir-makefiles)
-endif
-ifeq ($(TARGET_ARCH),mipsel)
-include $(all-subdir-makefiles)
-endif
diff --git a/libpixelflinger/tests/arch-mips/col32cb16blend/Android.bp b/libpixelflinger/tests/arch-mips/col32cb16blend/Android.bp
new file mode 100644
index 0000000..45bfe29
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/col32cb16blend/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+    name: "test-pixelflinger-mips-col32cb16blend",
+    defaults: ["pixelflinger-tests-mips"],
+
+    srcs: ["col32cb16blend_test.c"],
+}
diff --git a/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk
deleted file mode 100644
index 40f197f..0000000
--- a/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-    col32cb16blend_test.c \
-    ../../../arch-mips/col32cb16blend.S
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_C_INCLUDES :=
-
-LOCAL_MODULE:= test-pixelflinger-mips-col32cb16blend
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 32
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips/t32cb16blend/Android.bp b/libpixelflinger/tests/arch-mips/t32cb16blend/Android.bp
new file mode 100644
index 0000000..069e97c
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/t32cb16blend/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+    name: "test-pixelflinger-mips-t32cb16blend",
+    defaults: ["pixelflinger-tests-mips"],
+
+    srcs: ["t32cb16blend_test.c"],
+}
diff --git a/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk
deleted file mode 100644
index d0c0ae4..0000000
--- a/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-    t32cb16blend_test.c \
-    ../../../arch-mips/t32cb16blend.S
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_C_INCLUDES :=
-
-LOCAL_MODULE:= test-pixelflinger-mips-t32cb16blend
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 32
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/Android.bp b/libpixelflinger/tests/arch-mips64/Android.bp
new file mode 100644
index 0000000..ba55d62
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/Android.bp
@@ -0,0 +1,11 @@
+cc_defaults {
+    name: "pixelflinger-tests-mips64",
+    defaults: ["pixelflinger-tests"],
+
+    enabled: false,
+    arch: {
+        mips64: {
+            enabled: true,
+        },
+    },
+}
diff --git a/libpixelflinger/tests/arch-mips64/Android.mk b/libpixelflinger/tests/arch-mips64/Android.mk
deleted file mode 100644
index 3b1c64e..0000000
--- a/libpixelflinger/tests/arch-mips64/Android.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-ifeq ($(TARGET_ARCH),mips64)
-include $(all-subdir-makefiles)
-endif
diff --git a/libpixelflinger/tests/arch-mips64/assembler/Android.bp b/libpixelflinger/tests/arch-mips64/assembler/Android.bp
new file mode 100644
index 0000000..b672053
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/assembler/Android.bp
@@ -0,0 +1,9 @@
+cc_test {
+    name: "test-pixelflinger-mips64-assembler-test",
+    defaults: ["pixelflinger-tests-mips64"],
+
+    srcs: [
+        "mips64_assembler_test.cpp",
+        "asm_mips_test_jacket.S",
+    ],
+}
diff --git a/libpixelflinger/tests/arch-mips64/assembler/Android.mk b/libpixelflinger/tests/arch-mips64/assembler/Android.mk
deleted file mode 100644
index 4699961..0000000
--- a/libpixelflinger/tests/arch-mips64/assembler/Android.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-    mips64_assembler_test.cpp\
-    asm_mips_test_jacket.S
-
-LOCAL_SHARED_LIBRARIES := \
-    libcutils \
-    libpixelflinger
-
-LOCAL_C_INCLUDES := \
-    $(LOCAL_PATH)/../../..
-
-LOCAL_MODULE:= test-pixelflinger-mips64-assembler-test
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.bp b/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.bp
new file mode 100644
index 0000000..bfc6ae9
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+    name: "test-pixelflinger-mips64-col32cb16blend",
+    defaults: ["pixelflinger-tests-mips64"],
+
+    srcs: ["col32cb16blend_test.c"],
+}
diff --git a/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk
deleted file mode 100644
index 7d4177e..0000000
--- a/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-    col32cb16blend_test.c \
-    ../../../arch-mips64/col32cb16blend.S
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_C_INCLUDES :=
-
-LOCAL_MODULE:= test-pixelflinger-mips64-col32cb16blend
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/disassembler/Android.bp b/libpixelflinger/tests/arch-mips64/disassembler/Android.bp
new file mode 100644
index 0000000..96bf9e9
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/disassembler/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+    name: "test-pixelflinger-mips64-disassembler-test",
+    defaults: ["pixelflinger-tests-mips64"],
+
+    srcs: ["mips64_disassembler_test.cpp"],
+}
diff --git a/libpixelflinger/tests/arch-mips64/disassembler/Android.mk b/libpixelflinger/tests/arch-mips64/disassembler/Android.mk
deleted file mode 100644
index 4e72b57..0000000
--- a/libpixelflinger/tests/arch-mips64/disassembler/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-    mips64_disassembler_test.cpp \
-    ../../../codeflinger/mips64_disassem.c
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_MODULE:= test-pixelflinger-mips64-disassembler-test
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/codegen/Android.bp b/libpixelflinger/tests/codegen/Android.bp
new file mode 100644
index 0000000..7e4bcfb3
--- /dev/null
+++ b/libpixelflinger/tests/codegen/Android.bp
@@ -0,0 +1,12 @@
+cc_test {
+    name: "test-opengl-codegen",
+    defaults: ["pixelflinger-tests"],
+
+    srcs: ["codegen.cpp"],
+
+    arch: {
+        arm: {
+            instruction_set: "arm",
+        },
+    },
+}
diff --git a/libpixelflinger/tests/codegen/Android.mk b/libpixelflinger/tests/codegen/Android.mk
deleted file mode 100644
index 72d71ef..0000000
--- a/libpixelflinger/tests/codegen/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	codegen.cpp.arm
-
-LOCAL_SHARED_LIBRARIES := \
-	libcutils \
-    libpixelflinger
-
-LOCAL_C_INCLUDES := \
-	$(LOCAL_PATH)/../..
-
-LOCAL_MODULE:= test-opengl-codegen
-
-LOCAL_CFLAGS:= -Wall -Werror
-
-LOCAL_MODULE_TAGS := tests
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/gglmul/Android.bp b/libpixelflinger/tests/gglmul/Android.bp
new file mode 100644
index 0000000..288337b
--- /dev/null
+++ b/libpixelflinger/tests/gglmul/Android.bp
@@ -0,0 +1,12 @@
+cc_test {
+    name: "test-pixelflinger-gglmul",
+
+    srcs: ["gglmul_test.cpp"],
+
+    header_libs: ["libpixelflinger_internal"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/libpixelflinger/tests/gglmul/Android.mk b/libpixelflinger/tests/gglmul/Android.mk
deleted file mode 100644
index 67f358f..0000000
--- a/libpixelflinger/tests/gglmul/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	gglmul_test.cpp
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_C_INCLUDES := \
-	$(LOCAL_PATH)/../../include
-
-LOCAL_MODULE:= test-pixelflinger-gglmul
-
-LOCAL_CFLAGS:= -Wall -Werror
-
-LOCAL_MODULE_TAGS := tests
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index b0bc497..618a5c5 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -1,11 +1,55 @@
+cc_library_headers {
+    name: "libprocessgroup_headers",
+    vendor_available: true,
+    recovery_available: true,
+    host_supported: true,
+    native_bridge_supported: true,
+    export_include_dirs: ["include"],
+    target: {
+        linux_bionic: {
+            enabled: true,
+        },
+        windows: {
+            enabled: true,
+        },
+    },
+}
+
 cc_library {
-    srcs: ["processgroup.cpp"],
+    srcs: [
+        "cgroup_map.cpp",
+        "processgroup.cpp",
+        "sched_policy.cpp",
+        "task_profiles.cpp",
+    ],
     name: "libprocessgroup",
     host_supported: true,
-    shared_libs: ["libbase"],
+    native_bridge_supported: true,
+    recovery_available: true,
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
+    shared_libs: [
+        "libbase",
+        "libcgrouprc",
+    ],
+    static_libs: [
+        "libjsoncpp",
+    ],
+    // for cutils/android_filesystem_config.h
+    header_libs: [
+        "libcutils_headers",
+        "libprocessgroup_headers",
+    ],
     export_include_dirs: ["include"],
+    export_header_lib_headers: [
+        "libprocessgroup_headers",
+    ],
     cflags: [
         "-Wall",
         "-Werror",
+        "-Wexit-time-destructors",
     ],
 }
diff --git a/libprocessgroup/OWNERS b/libprocessgroup/OWNERS
new file mode 100644
index 0000000..bfa730a
--- /dev/null
+++ b/libprocessgroup/OWNERS
@@ -0,0 +1,2 @@
+ccross@google.com
+tomcherry@google.com
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
new file mode 100644
index 0000000..20ae2be
--- /dev/null
+++ b/libprocessgroup/cgroup_map.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "libprocessgroup"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <regex>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <cgroup_map.h>
+#include <json/reader.h>
+#include <json/value.h>
+#include <processgroup/processgroup.h>
+
+using android::base::GetBoolProperty;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+static constexpr const char* CGROUP_PROCS_FILE = "/cgroup.procs";
+static constexpr const char* CGROUP_TASKS_FILE = "/tasks";
+static constexpr const char* CGROUP_TASKS_FILE_V2 = "/cgroup.tasks";
+
+uint32_t CgroupController::version() const {
+    CHECK(HasValue());
+    return ACgroupController_getVersion(controller_);
+}
+
+const char* CgroupController::name() const {
+    CHECK(HasValue());
+    return ACgroupController_getName(controller_);
+}
+
+const char* CgroupController::path() const {
+    CHECK(HasValue());
+    return ACgroupController_getPath(controller_);
+}
+
+bool CgroupController::HasValue() const {
+    return controller_ != nullptr;
+}
+
+bool CgroupController::IsUsable() {
+    if (!HasValue()) return false;
+
+    if (state_ == UNKNOWN) {
+        state_ = access(GetProcsFilePath("", 0, 0).c_str(), F_OK) == 0 ? USABLE : MISSING;
+    }
+
+    return state_ == USABLE;
+}
+
+std::string CgroupController::GetTasksFilePath(const std::string& rel_path) const {
+    std::string tasks_path = path();
+
+    if (!rel_path.empty()) {
+        tasks_path += "/" + rel_path;
+    }
+    return (version() == 1) ? tasks_path + CGROUP_TASKS_FILE : tasks_path + CGROUP_TASKS_FILE_V2;
+}
+
+std::string CgroupController::GetProcsFilePath(const std::string& rel_path, uid_t uid,
+                                               pid_t pid) const {
+    std::string proc_path(path());
+    proc_path.append("/").append(rel_path);
+    proc_path = regex_replace(proc_path, std::regex("<uid>"), std::to_string(uid));
+    proc_path = regex_replace(proc_path, std::regex("<pid>"), std::to_string(pid));
+
+    return proc_path.append(CGROUP_PROCS_FILE);
+}
+
+bool CgroupController::GetTaskGroup(int tid, std::string* group) const {
+    std::string file_name = StringPrintf("/proc/%d/cgroup", tid);
+    std::string content;
+    if (!android::base::ReadFileToString(file_name, &content)) {
+        PLOG(ERROR) << "Failed to read " << file_name;
+        return false;
+    }
+
+    // if group is null and tid exists return early because
+    // user is not interested in cgroup membership
+    if (group == nullptr) {
+        return true;
+    }
+
+    std::string cg_tag = StringPrintf(":%s:", name());
+    size_t start_pos = content.find(cg_tag);
+    if (start_pos == std::string::npos) {
+        return false;
+    }
+
+    start_pos += cg_tag.length() + 1;  // skip '/'
+    size_t end_pos = content.find('\n', start_pos);
+    if (end_pos == std::string::npos) {
+        *group = content.substr(start_pos, std::string::npos);
+    } else {
+        *group = content.substr(start_pos, end_pos - start_pos);
+    }
+
+    return true;
+}
+
+CgroupMap::CgroupMap() {
+    if (!LoadRcFile()) {
+        LOG(ERROR) << "CgroupMap::LoadRcFile called for [" << getpid() << "] failed";
+    }
+}
+
+CgroupMap& CgroupMap::GetInstance() {
+    // Deliberately leak this object to avoid a race between destruction on
+    // process exit and concurrent access from another thread.
+    static auto* instance = new CgroupMap;
+    return *instance;
+}
+
+bool CgroupMap::LoadRcFile() {
+    if (!loaded_) {
+        loaded_ = (ACgroupFile_getVersion() != 0);
+    }
+    return loaded_;
+}
+
+void CgroupMap::Print() const {
+    if (!loaded_) {
+        LOG(ERROR) << "CgroupMap::Print called for [" << getpid()
+                   << "] failed, RC file was not initialized properly";
+        return;
+    }
+    LOG(INFO) << "File version = " << ACgroupFile_getVersion();
+    LOG(INFO) << "File controller count = " << ACgroupFile_getControllerCount();
+
+    LOG(INFO) << "Mounted cgroups:";
+
+    auto controller_count = ACgroupFile_getControllerCount();
+    for (uint32_t i = 0; i < controller_count; ++i) {
+        const ACgroupController* controller = ACgroupFile_getController(i);
+        LOG(INFO) << "\t" << ACgroupController_getName(controller) << " ver "
+                  << ACgroupController_getVersion(controller) << " path "
+                  << ACgroupController_getPath(controller);
+    }
+}
+
+CgroupController CgroupMap::FindController(const std::string& name) const {
+    if (!loaded_) {
+        LOG(ERROR) << "CgroupMap::FindController called for [" << getpid()
+                   << "] failed, RC file was not initialized properly";
+        return CgroupController(nullptr);
+    }
+
+    auto controller_count = ACgroupFile_getControllerCount();
+    for (uint32_t i = 0; i < controller_count; ++i) {
+        const ACgroupController* controller = ACgroupFile_getController(i);
+        if (name == ACgroupController_getName(controller)) {
+            return CgroupController(controller);
+        }
+    }
+
+    return CgroupController(nullptr);
+}
diff --git a/libprocessgroup/cgroup_map.h b/libprocessgroup/cgroup_map.h
new file mode 100644
index 0000000..427d71b
--- /dev/null
+++ b/libprocessgroup/cgroup_map.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/cdefs.h>
+#include <sys/types.h>
+
+#include <map>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include <android/cgrouprc.h>
+
+// Convenient wrapper of an ACgroupController pointer.
+class CgroupController {
+  public:
+    // Does not own controller
+    explicit CgroupController(const ACgroupController* controller)
+        : controller_(controller), state_(UNKNOWN) {}
+
+    uint32_t version() const;
+    const char* name() const;
+    const char* path() const;
+
+    bool HasValue() const;
+    bool IsUsable();
+
+    std::string GetTasksFilePath(const std::string& path) const;
+    std::string GetProcsFilePath(const std::string& path, uid_t uid, pid_t pid) const;
+    bool GetTaskGroup(int tid, std::string* group) const;
+  private:
+    enum ControllerState {
+        UNKNOWN = 0,
+        USABLE = 1,
+        MISSING = 2,
+    };
+
+    const ACgroupController* controller_ = nullptr;
+    ControllerState state_;
+};
+
+class CgroupMap {
+  public:
+    // Selinux policy ensures only init process can successfully use this function
+    static bool SetupCgroups();
+
+    static CgroupMap& GetInstance();
+    CgroupController FindController(const std::string& name) const;
+
+  private:
+    bool loaded_ = false;
+    CgroupMap();
+    bool LoadRcFile();
+    void Print() const;
+};
diff --git a/libprocessgroup/cgrouprc/Android.bp b/libprocessgroup/cgrouprc/Android.bp
new file mode 100644
index 0000000..0af75bb
--- /dev/null
+++ b/libprocessgroup/cgrouprc/Android.bp
@@ -0,0 +1,63 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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: "libcgrouprc",
+    host_supported: true,
+    recovery_available: true,
+    // Do not ever mark this as vendor_available; otherwise, vendor modules
+    // that links to the static library will behave unexpectedly. All on-device
+    // modules should use libprocessgroup which links to the LL-NDK library
+    // defined below. The static library is built for tests.
+    vendor_available: false,
+    native_bridge_supported: true,
+    srcs: [
+        "cgroup_controller.cpp",
+        "cgroup_file.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    header_libs: [
+        "libprocessgroup_headers",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+    static_libs: [
+        "libcgrouprc_format",
+    ],
+    stubs: {
+        symbol_file: "libcgrouprc.llndk.txt",
+        versions: ["29"],
+    },
+    target: {
+        linux: {
+            version_script: "libcgrouprc.llndk.txt",
+        },
+    },
+}
+
+llndk_library {
+    name: "libcgrouprc",
+    symbol_file: "libcgrouprc.llndk.txt",
+    native_bridge_supported: true,
+    export_include_dirs: [
+        "include",
+    ],
+}
diff --git a/libprocessgroup/cgrouprc/cgroup_controller.cpp b/libprocessgroup/cgrouprc/cgroup_controller.cpp
new file mode 100644
index 0000000..d064d31
--- /dev/null
+++ b/libprocessgroup/cgrouprc/cgroup_controller.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/logging.h>
+#include <android/cgrouprc.h>
+
+#include "cgrouprc_internal.h"
+
+// All ACgroupController_* functions implicitly convert the pointer back
+// to the original CgroupController pointer before invoking the member functions.
+
+uint32_t ACgroupController_getVersion(const ACgroupController* controller) {
+    CHECK(controller != nullptr);
+    return controller->version();
+}
+
+const char* ACgroupController_getName(const ACgroupController* controller) {
+    CHECK(controller != nullptr);
+    return controller->name();
+}
+
+const char* ACgroupController_getPath(const ACgroupController* controller) {
+    CHECK(controller != nullptr);
+    return controller->path();
+}
diff --git a/libprocessgroup/cgrouprc/cgroup_file.cpp b/libprocessgroup/cgrouprc/cgroup_file.cpp
new file mode 100644
index 0000000..e26d841
--- /dev/null
+++ b/libprocessgroup/cgrouprc/cgroup_file.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/mman.h>
+#include <sys/stat.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <android/cgrouprc.h>
+#include <processgroup/processgroup.h>
+
+#include "cgrouprc_internal.h"
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+using android::cgrouprc::format::CgroupController;
+using android::cgrouprc::format::CgroupFile;
+
+static CgroupFile* LoadRcFile() {
+    struct stat sb;
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(CGROUPS_RC_PATH, O_RDONLY | O_CLOEXEC)));
+    if (fd < 0) {
+        PLOG(ERROR) << "open() failed for " << CGROUPS_RC_PATH;
+        return nullptr;
+    }
+
+    if (fstat(fd, &sb) < 0) {
+        PLOG(ERROR) << "fstat() failed for " << CGROUPS_RC_PATH;
+        return nullptr;
+    }
+
+    size_t file_size = sb.st_size;
+    if (file_size < sizeof(CgroupFile)) {
+        LOG(ERROR) << "Invalid file format " << CGROUPS_RC_PATH;
+        return nullptr;
+    }
+
+    CgroupFile* file_data = (CgroupFile*)mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
+    if (file_data == MAP_FAILED) {
+        PLOG(ERROR) << "Failed to mmap " << CGROUPS_RC_PATH;
+        return nullptr;
+    }
+
+    if (file_data->version_ != CgroupFile::FILE_CURR_VERSION) {
+        LOG(ERROR) << CGROUPS_RC_PATH << " file version mismatch";
+        munmap(file_data, file_size);
+        return nullptr;
+    }
+
+    auto expected = sizeof(CgroupFile) + file_data->controller_count_ * sizeof(CgroupController);
+    if (file_size != expected) {
+        LOG(ERROR) << CGROUPS_RC_PATH << " file has invalid size, expected " << expected
+                   << ", actual " << file_size;
+        munmap(file_data, file_size);
+        return nullptr;
+    }
+
+    return file_data;
+}
+
+static CgroupFile* GetInstance() {
+    // Deliberately leak this object (not munmap) to avoid a race between destruction on
+    // process exit and concurrent access from another thread.
+    static auto* file = LoadRcFile();
+    return file;
+}
+
+uint32_t ACgroupFile_getVersion() {
+    auto file = GetInstance();
+    if (file == nullptr) return 0;
+    return file->version_;
+}
+
+uint32_t ACgroupFile_getControllerCount() {
+    auto file = GetInstance();
+    if (file == nullptr) return 0;
+    return file->controller_count_;
+}
+
+const ACgroupController* ACgroupFile_getController(uint32_t index) {
+    auto file = GetInstance();
+    if (file == nullptr) return nullptr;
+    CHECK(index < file->controller_count_);
+    // Although the object is not actually an ACgroupController object, all ACgroupController_*
+    // functions implicitly convert ACgroupController* back to CgroupController* before invoking
+    // member functions.
+    return static_cast<ACgroupController*>(&file->controllers_[index]);
+}
diff --git a/libprocessgroup/cgrouprc/cgrouprc_internal.h b/libprocessgroup/cgrouprc/cgrouprc_internal.h
new file mode 100644
index 0000000..cd02f03
--- /dev/null
+++ b/libprocessgroup/cgrouprc/cgrouprc_internal.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <android/cgrouprc.h>
+
+#include <processgroup/format/cgroup_controller.h>
+#include <processgroup/format/cgroup_file.h>
+
+struct ACgroupController : android::cgrouprc::format::CgroupController {};
diff --git a/libprocessgroup/cgrouprc/include/android/cgrouprc.h b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
new file mode 100644
index 0000000..0f6a9cd
--- /dev/null
+++ b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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>
+
+__BEGIN_DECLS
+
+// For host builds, __INTRODUCED_IN is not defined.
+#ifndef __INTRODUCED_IN
+#define __INTRODUCED_IN(x)
+#endif
+
+struct ACgroupController;
+typedef struct ACgroupController ACgroupController;
+
+#if __ANDROID_API__ >= __ANDROID_API_Q__
+
+// ACgroupFile
+
+/**
+ * Returns file version. See android::cgrouprc::format::CgroupFile for a list of valid versions
+ * for the file.
+ * If ACgroupFile_init() isn't called, initialization will be done first.
+ * If initialization failed, return 0.
+ */
+__attribute__((warn_unused_result)) uint32_t ACgroupFile_getVersion() __INTRODUCED_IN(29);
+
+/**
+ * Returns the number of controllers.
+ * If ACgroupFile_init() isn't called, initialization will be done first.
+ * If initialization failed, return 0.
+ */
+__attribute__((warn_unused_result)) uint32_t ACgroupFile_getControllerCount() __INTRODUCED_IN(29);
+
+/**
+ * Returns the controller at the given index.
+ * Returnss nullptr if the given index exceeds getControllerCount().
+ * If ACgroupFile_init() isn't called, initialization will be done first.
+ * If initialization failed, return 0.
+ */
+__attribute__((warn_unused_result)) const ACgroupController* ACgroupFile_getController(
+        uint32_t index) __INTRODUCED_IN(29);
+
+// ACgroupController
+
+/**
+ * Returns the version of the given controller.
+ * If the given controller is null, return 0.
+ */
+__attribute__((warn_unused_result)) uint32_t ACgroupController_getVersion(const ACgroupController*)
+        __INTRODUCED_IN(29);
+
+/**
+ * Flag bitmask to be used when ACgroupController_getFlags can be exported
+ */
+#define CGROUPRC_CONTROLLER_FLAG_MOUNTED 0x1
+
+/**
+ * Returns the name of the given controller.
+ * If the given controller is null, return nullptr.
+ */
+__attribute__((warn_unused_result)) const char* ACgroupController_getName(const ACgroupController*)
+        __INTRODUCED_IN(29);
+
+/**
+ * Returns the path of the given controller.
+ * If the given controller is null, return nullptr.
+ */
+__attribute__((warn_unused_result)) const char* ACgroupController_getPath(const ACgroupController*)
+        __INTRODUCED_IN(29);
+
+__END_DECLS
+
+#endif
diff --git a/libprocessgroup/cgrouprc/libcgrouprc.llndk.txt b/libprocessgroup/cgrouprc/libcgrouprc.llndk.txt
new file mode 100644
index 0000000..91df392
--- /dev/null
+++ b/libprocessgroup/cgrouprc/libcgrouprc.llndk.txt
@@ -0,0 +1,11 @@
+LIBCGROUPRC { # introduced=29
+  global:
+    ACgroupFile_getVersion;
+    ACgroupFile_getControllerCount;
+    ACgroupFile_getController;
+    ACgroupController_getVersion;
+    ACgroupController_getName;
+    ACgroupController_getPath;
+  local:
+    *;
+};
diff --git a/libprocessgroup/cgrouprc_format/Android.bp b/libprocessgroup/cgrouprc_format/Android.bp
new file mode 100644
index 0000000..559a869
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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: "libcgrouprc_format",
+    host_supported: true,
+    recovery_available: true,
+    native_bridge_supported: true,
+    srcs: [
+        "cgroup_controller.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+}
diff --git a/libprocessgroup/cgrouprc_format/cgroup_controller.cpp b/libprocessgroup/cgrouprc_format/cgroup_controller.cpp
new file mode 100644
index 0000000..202b23e
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/cgroup_controller.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <processgroup/format/cgroup_controller.h>
+
+namespace android {
+namespace cgrouprc {
+namespace format {
+
+CgroupController::CgroupController() : version_(0), flags_(0) {
+    memset(name_, 0, sizeof(name_));
+    memset(path_, 0, sizeof(path_));
+}
+
+CgroupController::CgroupController(uint32_t version, uint32_t flags, const std::string& name,
+                                   const std::string& path)
+    : CgroupController() {
+    // strlcpy isn't available on host. Although there is an implementation
+    // in licutils, libcutils itself depends on libcgrouprc_format, causing
+    // a circular dependency.
+    version_ = version;
+    flags_ = flags;
+    strncpy(name_, name.c_str(), sizeof(name_) - 1);
+    name_[sizeof(name_) - 1] = '\0';
+    strncpy(path_, path.c_str(), sizeof(path_) - 1);
+    path_[sizeof(path_) - 1] = '\0';
+}
+
+uint32_t CgroupController::version() const {
+    return version_;
+}
+
+uint32_t CgroupController::flags() const {
+    return flags_;
+}
+
+const char* CgroupController::name() const {
+    return name_;
+}
+
+const char* CgroupController::path() const {
+    return path_;
+}
+
+void CgroupController::set_flags(uint32_t flags) {
+    flags_ = flags;
+}
+
+}  // namespace format
+}  // namespace cgrouprc
+}  // namespace android
diff --git a/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h
new file mode 100644
index 0000000..40d8548
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <string>
+
+namespace android {
+namespace cgrouprc {
+namespace format {
+
+// Minimal controller description to be mmapped into process address space
+struct CgroupController {
+  public:
+    CgroupController();
+    CgroupController(uint32_t version, uint32_t flags, const std::string& name,
+                     const std::string& path);
+
+    uint32_t version() const;
+    uint32_t flags() const;
+    const char* name() const;
+    const char* path() const;
+
+    void set_flags(uint32_t flags);
+
+  private:
+    static constexpr size_t CGROUP_NAME_BUF_SZ = 16;
+    static constexpr size_t CGROUP_PATH_BUF_SZ = 32;
+
+    uint32_t version_;
+    uint32_t flags_;
+    char name_[CGROUP_NAME_BUF_SZ];
+    char path_[CGROUP_PATH_BUF_SZ];
+};
+
+}  // namespace format
+}  // namespace cgrouprc
+}  // namespace android
diff --git a/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h
new file mode 100644
index 0000000..f1678a1
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <processgroup/format/cgroup_controller.h>
+
+namespace android {
+namespace cgrouprc {
+namespace format {
+
+struct CgroupFile {
+    uint32_t version_;
+    uint32_t controller_count_;
+    CgroupController controllers_[];
+
+    static constexpr uint32_t FILE_VERSION_1 = 1;
+    static constexpr uint32_t FILE_CURR_VERSION = FILE_VERSION_1;
+};
+
+}  // namespace format
+}  // namespace cgrouprc
+}  // namespace android
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 9fa4154..f73ec2d 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -14,14 +14,36 @@
  *  limitations under the License.
  */
 
-#ifndef _PROCESSGROUP_H_
-#define _PROCESSGROUP_H_
+#pragma once
 
 #include <sys/cdefs.h>
 #include <sys/types.h>
+#include <string>
+#include <vector>
 
 __BEGIN_DECLS
 
+static constexpr const char* CGROUPV2_CONTROLLER_NAME = "cgroup2";
+
+bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path);
+bool CgroupGetAttributePath(const std::string& attr_name, std::string* path);
+bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path);
+
+bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache = false);
+bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
+                        bool use_fd_cache = false);
+
+#ifndef __ANDROID_VNDK__
+
+static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc";
+
+bool UsePerAppMemcg();
+
+// Drop the fd cache of cgroup path. It is used for when resource caching is enabled and a process
+// loses the access to the path, the access checking (See SetCgroupAction::EnableResourceCaching)
+// should be active again. E.g. Zygote specialization for child process.
+void DropTaskProfilesResourceCaching();
+
 // Return 0 and removes the cgroup if there are no longer any processes in it.
 // Returns -1 in the case of an error occurring or if there are processes still running
 // even after retrying for up to 200ms.
@@ -31,14 +53,16 @@
 // that it only returns 0 in the case that the cgroup exists and it contains no processes.
 int killProcessGroupOnce(uid_t uid, int initialPid, int signal);
 
-int createProcessGroup(uid_t uid, int initialPid);
+int createProcessGroup(uid_t uid, int initialPid, bool memControl = false);
 
+// Set various properties of a process group. For these functions to work, the process group must
+// have been created by passing memControl=true to createProcessGroup.
 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
+#endif // __ANDROID_VNDK__
 
-#endif
+__END_DECLS
diff --git a/libprocessgroup/include/processgroup/sched_policy.h b/libprocessgroup/include/processgroup/sched_policy.h
new file mode 100644
index 0000000..3c498da
--- /dev/null
+++ b/libprocessgroup/include/processgroup/sched_policy.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Check if Linux kernel enables CPUSETS feature.
+ *
+ * Return value: 1 if Linux kernel CONFIG_CPUSETS=y; 0 otherwise.
+ */
+extern bool cpusets_enabled();
+
+/*
+ * Check if Linux kernel enables SCHEDTUNE feature (only available in Android
+ * common kernel or Linaro LSK, not in mainline Linux as of v4.9)
+ *
+ * Return value: 1 if Linux kernel CONFIG_CGROUP_SCHEDTUNE=y; 0 otherwise.
+ */
+extern bool schedboost_enabled();
+
+/* Keep in sync with THREAD_GROUP_* in frameworks/base/core/java/android/os/Process.java */
+typedef enum {
+    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_RT_APP = 6,
+    SP_RESTRICTED = 7,
+    SP_CNT,
+    SP_MAX = SP_CNT - 1,
+    SP_SYSTEM_DEFAULT = SP_FOREGROUND,
+} SchedPolicy;
+
+extern int set_cpuset_policy(int tid, SchedPolicy policy);
+
+/* Assign thread tid to the cgroup associated with the specified policy.
+ * If the thread is a thread group leader, that is it's gettid() == getpid(),
+ * then the other threads in the same thread group are _not_ affected.
+ * On platforms which support gettid(), zero tid means current thread.
+ * Return value: 0 for success, or -errno for error.
+ */
+extern int set_sched_policy(int tid, SchedPolicy policy);
+
+/* Return the policy associated with the cgroup of thread tid via policy pointer.
+ * On platforms which support gettid(), zero tid means current thread.
+ * Return value: 0 for success, or -1 for error and set errno.
+ */
+extern int get_sched_policy(int tid, SchedPolicy* policy);
+
+/* Return a displayable string corresponding to policy.
+ * Return value: non-NULL NUL-terminated name of unspecified length;
+ * the caller is responsible for displaying the useful part of the string.
+ */
+extern const char* get_sched_policy_name(SchedPolicy policy);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 58295fa..7c191be 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -25,12 +25,12 @@
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include <chrono>
+#include <map>
 #include <memory>
 #include <mutex>
 #include <set>
@@ -39,79 +39,146 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
-#ifdef __ANDROID__
 #include <android-base/properties.h>
-#endif
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <private/android_filesystem_config.h>
-
+#include <cutils/android_filesystem_config.h>
 #include <processgroup/processgroup.h>
+#include <task_profiles.h>
 
-#ifdef __ANDROID__
 using android::base::GetBoolProperty;
-#endif
 using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
 
 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_CGROUP_PROCS_FILE "/cgroup.procs"
 
-std::once_flag init_path_flag;
+bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path) {
+    auto controller = CgroupMap::GetInstance().FindController(cgroup_name);
 
-static const std::string& GetCgroupRootPath() {
-    static std::string cgroup_root_path;
-    std::call_once(init_path_flag, [&]() {
-#ifdef __ANDROID__
-        // 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);
-#else
-        // host does not support Android properties
-        bool per_app_memcg = false;
-#endif
-        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);
+    if (!controller.HasValue()) {
+        return false;
+    }
+
+    if (path) {
+        *path = controller.path();
+    }
+
+    return true;
+}
+
+bool CgroupGetAttributePath(const std::string& attr_name, std::string* path) {
+    const TaskProfiles& tp = TaskProfiles::GetInstance();
+    const ProfileAttribute* attr = tp.GetAttribute(attr_name);
+
+    if (attr == nullptr) {
+        return false;
+    }
+
+    if (path) {
+        *path = StringPrintf("%s/%s", attr->controller()->path(), attr->file_name().c_str());
+    }
+
+    return true;
+}
+
+bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path) {
+    const TaskProfiles& tp = TaskProfiles::GetInstance();
+    const ProfileAttribute* attr = tp.GetAttribute(attr_name);
+
+    if (attr == nullptr) {
+        return false;
+    }
+
+    if (!attr->GetPathForTask(tid, path)) {
+        PLOG(ERROR) << "Failed to find cgroup for tid " << tid;
+        return false;
+    }
+
+    return true;
+}
+
+bool UsePerAppMemcg() {
+    bool low_ram_device = GetBoolProperty("ro.config.low_ram", false);
+    return GetBoolProperty("ro.config.per_app_memcg", low_ram_device);
+}
+
+static bool isMemoryCgroupSupported() {
+    static bool memcg_supported = CgroupMap::GetInstance().FindController("memory").IsUsable();
+
+    return memcg_supported;
+}
+
+void DropTaskProfilesResourceCaching() {
+    TaskProfiles::GetInstance().DropResourceCaching();
+}
+
+bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
+                        bool use_fd_cache) {
+    const TaskProfiles& tp = TaskProfiles::GetInstance();
+
+    for (const auto& name : profiles) {
+        TaskProfile* profile = tp.GetProfile(name);
+        if (profile != nullptr) {
+            if (use_fd_cache) {
+                profile->EnableResourceCaching();
+            }
+            if (!profile->ExecuteForProcess(uid, pid)) {
+                PLOG(WARNING) << "Failed to apply " << name << " process profile";
+            }
         } else {
-            cgroup_root_path = ACCT_CGROUP_PATH;
+            PLOG(WARNING) << "Failed to find " << name << "process profile";
         }
-    });
-    return cgroup_root_path;
+    }
+
+    return true;
 }
 
-static std::string ConvertUidToPath(uid_t uid) {
-    return StringPrintf("%s/uid_%d", GetCgroupRootPath().c_str(), uid);
+bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) {
+    const TaskProfiles& tp = TaskProfiles::GetInstance();
+
+    for (const auto& name : profiles) {
+        TaskProfile* profile = tp.GetProfile(name);
+        if (profile != nullptr) {
+            if (use_fd_cache) {
+                profile->EnableResourceCaching();
+            }
+            if (!profile->ExecuteForTask(tid)) {
+                PLOG(WARNING) << "Failed to apply " << name << " task profile";
+            }
+        } else {
+            PLOG(WARNING) << "Failed to find " << name << "task profile";
+        }
+    }
+
+    return true;
 }
 
-static std::string ConvertUidPidToPath(uid_t uid, int pid) {
-    return StringPrintf("%s/uid_%d/pid_%d", GetCgroupRootPath().c_str(), uid, pid);
+static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
+    return StringPrintf("%s/uid_%d", cgroup, uid);
 }
 
-static int RemoveProcessGroup(uid_t uid, int pid) {
+static std::string ConvertUidPidToPath(const char* cgroup, uid_t uid, int pid) {
+    return StringPrintf("%s/uid_%d/pid_%d", cgroup, uid, pid);
+}
+
+static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid) {
     int ret;
 
-    auto uid_pid_path = ConvertUidPidToPath(uid, pid);
+    auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, pid);
     ret = rmdir(uid_pid_path.c_str());
 
-    auto uid_path = ConvertUidToPath(uid);
+    auto uid_path = ConvertUidToPath(cgroup, uid);
     rmdir(uid_path.c_str());
 
     return ret;
 }
 
-static void RemoveUidProcessGroups(const std::string& uid_path) {
+static bool RemoveUidProcessGroups(const std::string& uid_path) {
     std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path.c_str()), closedir);
+    bool empty = true;
     if (uid != NULL) {
         dirent* dir;
         while ((dir = readdir(uid.get())) != nullptr) {
@@ -125,44 +192,85 @@
 
             auto path = StringPrintf("%s/%s", uid_path.c_str(), dir->d_name);
             LOG(VERBOSE) << "Removing " << path;
-            if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
+            if (rmdir(path.c_str()) == -1) {
+                if (errno != EBUSY) {
+                    PLOG(WARNING) << "Failed to remove " << path;
+                }
+                empty = false;
+            }
+        }
+    }
+    return empty;
+}
+
+void removeAllProcessGroups() {
+    LOG(VERBOSE) << "removeAllProcessGroups()";
+
+    std::vector<std::string> cgroups;
+    std::string path;
+
+    if (CgroupGetControllerPath("cpuacct", &path)) {
+        cgroups.push_back(path);
+    }
+    if (CgroupGetControllerPath("memory", &path)) {
+        cgroups.push_back(path + "/apps");
+    }
+
+    for (std::string cgroup_root_path : cgroups) {
+        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) {
+                if (dir->d_type != DT_DIR) {
+                    continue;
+                }
+
+                if (!StartsWith(dir->d_name, "uid_")) {
+                    continue;
+                }
+
+                auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
+                if (!RemoveUidProcessGroups(path)) {
+                    LOG(VERBOSE) << "Skip removing " << path;
+                    continue;
+                }
+                LOG(VERBOSE) << "Removing " << path;
+                if (rmdir(path.c_str()) == -1 && errno != EBUSY) {
+                    PLOG(WARNING) << "Failed to remove " << path;
+                }
+            }
         }
     }
 }
 
-void removeAllProcessGroups()
-{
-    LOG(VERBOSE) << "removeAllProcessGroups()";
-    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) {
-            if (dir->d_type != DT_DIR) {
-                continue;
-            }
-
-            if (!StartsWith(dir->d_name, "uid_")) {
-                continue;
-            }
-
-            auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
-            RemoveUidProcessGroups(path);
-            LOG(VERBOSE) << "Removing " << path;
-            if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
-        }
+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.c_str(), uid, gid) == -1) {
+        int saved_errno = errno;
+        rmdir(path.c_str());
+        errno = saved_errno;
+        return false;
+    }
+
+    return true;
 }
 
 // Returns number of processes killed on success
 // Returns 0 if there are no processes in the process cgroup left to kill
 // Returns -1 on error
-static int DoKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
-    auto path = ConvertUidPidToPath(uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE;
+static int DoKillProcessGroupOnce(const char* cgroup, uid_t uid, int initialPid, int signal) {
+    auto path = ConvertUidPidToPath(cgroup, uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE;
     std::unique_ptr<FILE, decltype(&fclose)> fd(fopen(path.c_str(), "re"), fclose);
     if (!fd) {
+        if (errno == ENOENT) {
+            // This happens when process is already dead
+            return 0;
+        }
         PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
         return -1;
     }
@@ -194,7 +302,7 @@
 
     // Erase all pids that will be killed when we kill the process groups.
     for (auto it = pids.begin(); it != pids.end();) {
-        pid_t pgid = getpgid(pid);
+        pid_t pgid = getpgid(*it);
         if (pgids.count(pgid) == 1) {
             it = pids.erase(it);
         } else {
@@ -207,7 +315,7 @@
         LOG(VERBOSE) << "Killing process group " << -pgid << " in uid " << uid
                      << " as part of process cgroup " << initialPid;
 
-        if (kill(-pgid, signal) == -1) {
+        if (kill(-pgid, signal) == -1 && errno != ESRCH) {
             PLOG(WARNING) << "kill(" << -pgid << ", " << signal << ") failed";
         }
     }
@@ -217,7 +325,7 @@
         LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid << " as part of process cgroup "
                      << initialPid;
 
-        if (kill(pid, signal) == -1) {
+        if (kill(pid, signal) == -1 && errno != ESRCH) {
             PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed";
         }
     }
@@ -226,11 +334,23 @@
 }
 
 static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
+    std::string cpuacct_path;
+    std::string memory_path;
+
+    CgroupGetControllerPath("cpuacct", &cpuacct_path);
+    CgroupGetControllerPath("memory", &memory_path);
+    memory_path += "/apps";
+
+    const char* cgroup =
+            (!access(ConvertUidPidToPath(cpuacct_path.c_str(), uid, initialPid).c_str(), F_OK))
+                    ? cpuacct_path.c_str()
+                    : memory_path.c_str();
+
     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(cgroup, uid, initialPid, signal)) > 0) {
         LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid;
         if (retry > 0) {
             std::this_thread::sleep_for(5ms);
@@ -260,7 +380,7 @@
             LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid
                       << " in " << static_cast<int>(ms) << "ms";
         }
-        return RemoveProcessGroup(uid, initialPid);
+        return RemoveProcessGroup(cgroup, uid, initialPid);
     } else {
         if (retries > 0) {
             LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid
@@ -279,31 +399,23 @@
     return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/);
 }
 
-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;
+int createProcessGroup(uid_t uid, int initialPid, bool memControl) {
+    std::string cgroup;
+    if (isMemoryCgroupSupported() && (memControl || UsePerAppMemcg())) {
+        CgroupGetControllerPath("memory", &cgroup);
+        cgroup += "/apps";
+    } else {
+        CgroupGetControllerPath("cpuacct", &cgroup);
     }
 
-    if (chown(path.c_str(), uid, gid) == -1) {
-        int saved_errno = errno;
-        rmdir(path.c_str());
-        errno = saved_errno;
-        return false;
-    }
-
-    return true;
-}
-
-int createProcessGroup(uid_t uid, int initialPid)
-{
-    auto uid_path = ConvertUidToPath(uid);
+    auto uid_path = ConvertUidToPath(cgroup.c_str(), uid);
 
     if (!MkdirAndChown(uid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
         PLOG(ERROR) << "Failed to make and chown " << uid_path;
         return -errno;
     }
 
-    auto uid_pid_path = ConvertUidPidToPath(uid, initialPid);
+    auto uid_pid_path = ConvertUidPidToPath(cgroup.c_str(), uid, initialPid);
 
     if (!MkdirAndChown(uid_pid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
         PLOG(ERROR) << "Failed to make and chown " << uid_pid_path;
@@ -321,13 +433,17 @@
     return ret;
 }
 
-static bool SetProcessGroupValue(uid_t uid, int pid, const std::string& file_name, int64_t value) {
-    if (GetCgroupRootPath() != MEM_CGROUP_PATH) {
+static bool SetProcessGroupValue(int tid, const std::string& attr_name, int64_t value) {
+    if (!isMemoryCgroupSupported()) {
         PLOG(ERROR) << "Memcg is not mounted.";
         return false;
     }
 
-    auto path = ConvertUidPidToPath(uid, pid) + file_name;
+    std::string path;
+    if (!CgroupGetAttributePathForTask(attr_name, tid, &path)) {
+        PLOG(ERROR) << "Failed to find attribute '" << attr_name << "'";
+        return false;
+    }
 
     if (!WriteStringToFile(std::to_string(value), path)) {
         PLOG(ERROR) << "Failed to write '" << value << "' to " << path;
@@ -336,14 +452,14 @@
     return true;
 }
 
-bool setProcessGroupSwappiness(uid_t uid, int pid, int swappiness) {
-    return SetProcessGroupValue(uid, pid, "/memory.swappiness", swappiness);
+bool setProcessGroupSwappiness(uid_t, int pid, int swappiness) {
+    return SetProcessGroupValue(pid, "MemSwappiness", 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 setProcessGroupSoftLimit(uid_t, int pid, int64_t soft_limit_in_bytes) {
+    return SetProcessGroupValue(pid, "MemSoftLimit", 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);
+bool setProcessGroupLimit(uid_t, int pid, int64_t limit_in_bytes) {
+    return SetProcessGroupValue(pid, "MemLimit", limit_in_bytes);
 }
diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
new file mode 100644
index 0000000..e05a690
--- /dev/null
+++ b/libprocessgroup/profiles/Android.bp
@@ -0,0 +1,108 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+prebuilt_etc {
+    name: "cgroups.json",
+    src: "cgroups.json",
+}
+
+prebuilt_etc {
+    name: "cgroups.recovery.json",
+    filename: "cgroups.json",
+    recovery: true,
+    src: "cgroups.recovery.json",
+}
+
+prebuilt_etc {
+    name: "task_profiles.json",
+    src: "task_profiles.json",
+}
+
+cc_defaults {
+    name: "libprocessgroup_test_defaults",
+    cflags: [
+        "-Wall",
+        "-Werror",
+
+        // Needed for headers from libprotobuf.
+        "-Wno-unused-parameter",
+    ],
+}
+
+cc_library_static {
+    name: "libprocessgroup_proto",
+    host_supported: true,
+    defaults: ["libprocessgroup_test_defaults"],
+    srcs: [
+        "cgroups.proto",
+        "task_profiles.proto",
+    ],
+    proto: {
+        type: "full",
+        export_proto_headers: true,
+    },
+}
+
+cc_test_host {
+    name: "libprocessgroup_proto_test",
+    defaults: ["libprocessgroup_test_defaults"],
+    srcs: [
+        "test.cpp",
+    ],
+    static_libs: [
+        "libbase",
+        "libgmock",
+        "liblog",
+        "libjsoncpp",
+        "libjsonpbverify",
+        "libjsonpbparse",
+        "libprocessgroup_proto",
+    ],
+    shared_libs: [
+        "libprotobuf-cpp-full",
+    ],
+    data: [
+        "cgroups.json",
+        "cgroups.recovery.json",
+        "task_profiles.json",
+    ],
+    test_suites: [
+        "general-tests",
+    ],
+}
+
+cc_test {
+    name: "vts_processgroup_validate_test",
+    defaults: ["libprocessgroup_test_defaults"],
+    srcs: [
+        "test_vendor.cpp",
+    ],
+    static_libs: [
+        "libgmock",
+        "libjsonpbverify",
+        "libjsonpbparse",
+        "libprocessgroup_proto",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libjsoncpp",
+        "libprotobuf-cpp-full",
+    ],
+    target: {
+        android: {
+            test_config: "vts_processgroup_validate_test.xml",
+        },
+    },
+}
diff --git a/libprocessgroup/profiles/Android.mk b/libprocessgroup/profiles/Android.mk
new file mode 100644
index 0000000..eab96d4
--- /dev/null
+++ b/libprocessgroup/profiles/Android.mk
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 := VtsProcessgroupValidateTest
+-include test/vts/tools/build/Android.host_config.mk
diff --git a/libprocessgroup/profiles/TEST_MAPPING b/libprocessgroup/profiles/TEST_MAPPING
new file mode 100644
index 0000000..5ff4112
--- /dev/null
+++ b/libprocessgroup/profiles/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+  "presubmit": [
+    {
+      "name": "libprocessgroup_proto_test",
+      "host": true
+    }
+  ]
+}
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
new file mode 100644
index 0000000..5871a63
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups.json
@@ -0,0 +1,50 @@
+{
+  "Cgroups": [
+    {
+      "Controller": "blkio",
+      "Path": "/dev/blkio",
+      "Mode": "0755",
+      "UID": "system",
+      "GID": "system"
+    },
+    {
+      "Controller": "cpu",
+      "Path": "/dev/cpuctl",
+      "Mode": "0755",
+      "UID": "system",
+      "GID": "system"
+    },
+    {
+      "Controller": "cpuacct",
+      "Path": "/acct",
+      "Mode": "0555"
+    },
+    {
+      "Controller": "cpuset",
+      "Path": "/dev/cpuset",
+      "Mode": "0755",
+      "UID": "system",
+      "GID": "system"
+    },
+    {
+      "Controller": "memory",
+      "Path": "/dev/memcg",
+      "Mode": "0700",
+      "UID": "root",
+      "GID": "system"
+    },
+    {
+      "Controller": "schedtune",
+      "Path": "/dev/stune",
+      "Mode": "0755",
+      "UID": "system",
+      "GID": "system"
+    }
+  ],
+  "Cgroups2": {
+    "Path": "/dev/cg2_bpf",
+    "Mode": "0600",
+    "UID": "root",
+    "GID": "root"
+  }
+}
diff --git a/libprocessgroup/profiles/cgroups.proto b/libprocessgroup/profiles/cgroups.proto
new file mode 100644
index 0000000..f4070c5
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups.proto
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 = "proto3";
+
+package android.profiles;
+
+// Next: 3
+message Cgroups {
+    repeated Cgroup cgroups = 1 [json_name = "Cgroups"];
+    Cgroups2 cgroups2 = 2 [json_name = "Cgroups2"];
+}
+
+// Next: 6
+message Cgroup {
+    string controller = 1 [json_name = "Controller"];
+    string path = 2 [json_name = "Path"];
+    string mode = 3 [json_name = "Mode"];
+    string uid = 4 [json_name = "UID"];
+    string gid = 5 [json_name = "GID"];
+}
+
+// Next: 5
+message Cgroups2 {
+    string path = 1 [json_name = "Path"];
+    string mode = 2 [json_name = "Mode"];
+    string uid = 3 [json_name = "UID"];
+    string gid = 4 [json_name = "GID"];
+}
diff --git a/libprocessgroup/profiles/cgroups.recovery.json b/libprocessgroup/profiles/cgroups.recovery.json
new file mode 100644
index 0000000..f0bf5fd
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups.recovery.json
@@ -0,0 +1,9 @@
+{
+  "Cgroups": [
+    {
+      "Controller": "cpuacct",
+      "Path": "/acct",
+      "Mode": "0555"
+    }
+  ]
+}
diff --git a/libprocessgroup/profiles/cgroups_test.h b/libprocessgroup/profiles/cgroups_test.h
new file mode 100644
index 0000000..1309957
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups_test.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <gmock/gmock.h>
+#include <jsonpb/json_schema_test.h>
+
+#include "cgroups.pb.h"
+
+using ::testing::MatchesRegex;
+
+namespace android {
+namespace profiles {
+
+class CgroupsTest : public jsonpb::JsonSchemaTest {
+  public:
+    void SetUp() override {
+        JsonSchemaTest::SetUp();
+        cgroups_ = static_cast<Cgroups*>(message());
+    }
+    Cgroups* cgroups_;
+};
+
+TEST_P(CgroupsTest, CgroupRequiredFields) {
+    for (int i = 0; i < cgroups_->cgroups_size(); ++i) {
+        auto&& cgroup = cgroups_->cgroups(i);
+        EXPECT_FALSE(cgroup.controller().empty())
+                << "No controller name for cgroup #" << i << " in " << file_path_;
+        EXPECT_FALSE(cgroup.path().empty()) << "No path for cgroup #" << i << " in " << file_path_;
+    }
+}
+
+TEST_P(CgroupsTest, Cgroup2RequiredFields) {
+    if (cgroups_->has_cgroups2()) {
+        EXPECT_FALSE(cgroups_->cgroups2().path().empty())
+                << "No path for cgroup2 in " << file_path_;
+    }
+}
+
+// "Mode" field must be in the format of "0xxx".
+static inline constexpr const char* REGEX_MODE = "(0[0-7]{3})?";
+TEST_P(CgroupsTest, CgroupMode) {
+    for (int i = 0; i < cgroups_->cgroups_size(); ++i) {
+        EXPECT_THAT(cgroups_->cgroups(i).mode(), MatchesRegex(REGEX_MODE))
+                << "For cgroup controller #" << i << " in " << file_path_;
+    }
+}
+
+TEST_P(CgroupsTest, Cgroup2Mode) {
+    EXPECT_THAT(cgroups_->cgroups2().mode(), MatchesRegex(REGEX_MODE))
+            << "For cgroups2 in " << file_path_;
+}
+
+}  // namespace profiles
+}  // namespace android
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
new file mode 100644
index 0000000..74a39cd
--- /dev/null
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -0,0 +1,498 @@
+{
+  "Attributes": [
+    {
+      "Name": "LowCapacityCPUs",
+      "Controller": "cpuset",
+      "File": "background/cpus"
+    },
+    {
+      "Name": "HighCapacityCPUs",
+      "Controller": "cpuset",
+      "File": "foreground/cpus"
+    },
+    {
+      "Name": "MaxCapacityCPUs",
+      "Controller": "cpuset",
+      "File": "top-app/cpus"
+    },
+
+    {
+      "Name": "MemLimit",
+      "Controller": "memory",
+      "File": "memory.limit_in_bytes"
+    },
+    {
+      "Name": "MemSoftLimit",
+      "Controller": "memory",
+      "File": "memory.soft_limit_in_bytes"
+    },
+    {
+      "Name": "MemSwappiness",
+      "Controller": "memory",
+      "File": "memory.swappiness"
+    },
+    {
+      "Name": "STuneBoost",
+      "Controller": "schedtune",
+      "File": "schedtune.boost"
+    },
+    {
+      "Name": "STunePreferIdle",
+      "Controller": "schedtune",
+      "File": "schedtune.prefer_idle"
+    },
+    {
+      "Name": "UClampMin",
+      "Controller": "cpu",
+      "File": "cpu.util.min"
+    },
+    {
+      "Name": "UClampMax",
+      "Controller": "cpu",
+      "File": "cpu.util.max"
+    }
+  ],
+
+  "Profiles": [
+    {
+      "Name": "HighEnergySaving",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "background"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "NormalPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": ""
+          }
+        }
+      ]
+    },
+    {
+      "Name": "HighPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "foreground"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "MaxPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "top-app"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "RealtimePerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "rt"
+          }
+        }
+      ]
+    },
+
+    {
+      "Name": "CpuPolicySpread",
+      "Actions": [
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "STunePreferIdle",
+            "Value": "1"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "CpuPolicyPack",
+      "Actions": [
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "STunePreferIdle",
+            "Value": "0"
+          }
+        }
+      ]
+    },
+
+    {
+      "Name": "VrKernelCapacity",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "cpuset",
+            "Path": ""
+          }
+        }
+      ]
+    },
+    {
+      "Name": "VrServiceCapacityLow",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "cpuset",
+            "Path": "system/background"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "VrServiceCapacityNormal",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "cpuset",
+            "Path": "system"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "VrServiceCapacityHigh",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "cpuset",
+            "Path": "system/performance"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "VrProcessCapacityLow",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "cpuset",
+            "Path": "application/background"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "VrProcessCapacityNormal",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "cpuset",
+            "Path": "application"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "VrProcessCapacityHigh",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "cpuset",
+            "Path": "application/performance"
+          }
+        }
+      ]
+    },
+
+    {
+      "Name": "ProcessCapacityLow",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "cpuset",
+            "Path": "background"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "ProcessCapacityNormal",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "cpuset",
+            "Path": ""
+          }
+        }
+      ]
+    },
+    {
+      "Name": "ProcessCapacityHigh",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "cpuset",
+            "Path": "foreground"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "ProcessCapacityMax",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "cpuset",
+            "Path": "top-app"
+          }
+        }
+      ]
+    },
+
+    {
+      "Name": "ServiceCapacityLow",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "cpuset",
+            "Path": "system-background"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "ServiceCapacityRestricted",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "cpuset",
+            "Path": "restricted"
+          }
+        }
+      ]
+    },
+
+    {
+      "Name": "CameraServiceCapacity",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "cpuset",
+            "Path": "camera-daemon"
+          }
+        }
+      ]
+    },
+
+    {
+      "Name": "LowIoPriority",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "blkio",
+            "Path": "background"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "NormalIoPriority",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "blkio",
+            "Path": ""
+          }
+        }
+      ]
+    },
+    {
+      "Name": "HighIoPriority",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "blkio",
+            "Path": ""
+          }
+        }
+      ]
+    },
+    {
+      "Name": "MaxIoPriority",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "blkio",
+            "Path": ""
+          }
+        }
+      ]
+    },
+
+    {
+      "Name": "TimerSlackHigh",
+      "Actions": [
+        {
+          "Name": "SetTimerSlack",
+          "Params":
+          {
+            "Slack": "40000000"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "TimerSlackNormal",
+      "Actions": [
+        {
+          "Name": "SetTimerSlack",
+          "Params":
+          {
+            "Slack": "50000"
+          }
+        }
+      ]
+    },
+
+    {
+      "Name": "PerfBoost",
+      "Actions": [
+        {
+          "Name": "SetClamps",
+          "Params":
+          {
+            "Boost": "50%",
+            "Clamp": "0"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "PerfClamp",
+      "Actions": [
+        {
+          "Name": "SetClamps",
+          "Params":
+          {
+            "Boost": "0",
+            "Clamp": "30%"
+          }
+        }
+      ]
+    },
+
+    {
+      "Name": "LowMemoryUsage",
+      "Actions": [
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "MemSoftLimit",
+            "Value": "16MB"
+          }
+        },
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "MemSwappiness",
+            "Value": "150"
+
+          }
+        }
+      ]
+    },
+    {
+      "Name": "HighMemoryUsage",
+      "Actions": [
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "MemSoftLimit",
+            "Value": "512MB"
+          }
+        },
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "MemSwappiness",
+            "Value": "100"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "SystemMemoryProcess",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "memory",
+            "Path": "system"
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/libprocessgroup/profiles/task_profiles.proto b/libprocessgroup/profiles/task_profiles.proto
new file mode 100644
index 0000000..578f0d3
--- /dev/null
+++ b/libprocessgroup/profiles/task_profiles.proto
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 = "proto3";
+
+package android.profiles;
+
+// Next: 3
+message TaskProfiles {
+    repeated Attribute attributes = 1 [json_name = "Attributes"];
+    repeated Profile profiles = 2 [json_name = "Profiles"];
+}
+
+// Next: 4
+message Attribute {
+    string name = 1 [json_name = "Name"];
+    string controller = 2 [json_name = "Controller"];
+    string file = 3 [json_name = "File"];
+}
+
+// Next: 3
+message Profile {
+    string name = 1 [json_name = "Name"];
+    repeated Action actions = 2 [json_name = "Actions"];
+}
+
+// Next: 3
+message Action {
+    string name = 1 [json_name = "Name"];
+    map<string, string> params = 2 [json_name = "Params"];
+}
diff --git a/libprocessgroup/profiles/task_profiles_test.h b/libprocessgroup/profiles/task_profiles_test.h
new file mode 100644
index 0000000..32f122d
--- /dev/null
+++ b/libprocessgroup/profiles/task_profiles_test.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <gmock/gmock.h>
+#include <jsonpb/json_schema_test.h>
+
+#include "task_profiles.pb.h"
+
+namespace android {
+namespace profiles {
+
+class TaskProfilesTest : public jsonpb::JsonSchemaTest {
+  public:
+    void SetUp() override {
+        JsonSchemaTest::SetUp();
+        task_profiles_ = static_cast<TaskProfiles*>(message());
+    }
+    TaskProfiles* task_profiles_;
+};
+
+TEST_P(TaskProfilesTest, AttributeRequiredFields) {
+    for (int i = 0; i < task_profiles_->attributes_size(); ++i) {
+        auto&& attribute = task_profiles_->attributes(i);
+        EXPECT_FALSE(attribute.name().empty())
+                << "No name for attribute #" << i << " in " << file_path_;
+        EXPECT_FALSE(attribute.controller().empty())
+                << "No controller for attribute #" << i << " in " << file_path_;
+        EXPECT_FALSE(attribute.file().empty())
+                << "No file for attribute #" << i << " in " << file_path_;
+    }
+}
+
+TEST_P(TaskProfilesTest, ProfileRequiredFields) {
+    for (int profile_idx = 0; profile_idx < task_profiles_->profiles_size(); ++profile_idx) {
+        auto&& profile = task_profiles_->profiles(profile_idx);
+        EXPECT_FALSE(profile.name().empty())
+                << "No name for profile #" << profile_idx << " in " << file_path_;
+        for (int action_idx = 0; action_idx < profile.actions_size(); ++action_idx) {
+            auto&& action = profile.actions(action_idx);
+            EXPECT_FALSE(action.name().empty())
+                    << "No name for profiles[" << profile_idx << "].actions[" << action_idx
+                    << "] in " << file_path_;
+        }
+    }
+}
+
+}  // namespace profiles
+}  // namespace android
diff --git a/libprocessgroup/profiles/test.cpp b/libprocessgroup/profiles/test.cpp
new file mode 100644
index 0000000..bc9aade
--- /dev/null
+++ b/libprocessgroup/profiles/test.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/file.h>
+#include <gtest/gtest.h>
+#include <jsonpb/json_schema_test.h>
+
+#include "cgroups_test.h"
+#include "task_profiles_test.h"
+
+using namespace ::android::jsonpb;
+using ::android::base::GetExecutableDirectory;
+
+namespace android {
+namespace profiles {
+
+template <typename T>
+JsonSchemaTestConfigFactory MakeTestParam(const std::string& path) {
+    return jsonpb::MakeTestParam<T>(GetExecutableDirectory() + path);
+}
+
+// Test suite instantiations
+INSTANTIATE_TEST_SUITE_P(, JsonSchemaTest,
+                         ::testing::Values(MakeTestParam<Cgroups>("/cgroups.json"),
+                                           MakeTestParam<Cgroups>("/cgroups.recovery.json"),
+                                           MakeTestParam<TaskProfiles>("/task_profiles.json")));
+INSTANTIATE_TEST_SUITE_P(, CgroupsTest,
+                         ::testing::Values(MakeTestParam<Cgroups>("/cgroups.json"),
+                                           MakeTestParam<Cgroups>("/cgroups.recovery.json")));
+INSTANTIATE_TEST_SUITE_P(, TaskProfilesTest,
+                         ::testing::Values(MakeTestParam<TaskProfiles>("/task_profiles.json")));
+
+}  // namespace profiles
+}  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/libprocessgroup/profiles/test_vendor.cpp b/libprocessgroup/profiles/test_vendor.cpp
new file mode 100644
index 0000000..3ec7fcf
--- /dev/null
+++ b/libprocessgroup/profiles/test_vendor.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/file.h>
+#include <gtest/gtest.h>
+#include <jsonpb/json_schema_test.h>
+
+#include "cgroups_test.h"
+#include "task_profiles_test.h"
+
+using ::android::base::GetExecutableDirectory;
+using namespace ::android::jsonpb;
+
+namespace android {
+namespace profiles {
+
+static constexpr const char* kVendorCgroups = "/vendor/etc/cgroups.json";
+static constexpr const char* kVendorTaskProfiles = "/vendor/etc/task_profiles.json";
+
+template <typename T>
+class TestConfig : public JsonSchemaTestConfig {
+  public:
+    TestConfig(const std::string& path) : file_path_(path){};
+    std::unique_ptr<google::protobuf::Message> CreateMessage() const override {
+        return std::make_unique<T>();
+    }
+    std::string file_path() const override { return file_path_; }
+    bool optional() const override {
+        // Ignore when vendor JSON files are missing.
+        return true;
+    }
+
+  private:
+    std::string file_path_;
+};
+
+template <typename T>
+JsonSchemaTestConfigFactory MakeTestParam(const std::string& path) {
+    return [path]() { return std::make_unique<TestConfig<T>>(path); };
+}
+
+INSTANTIATE_TEST_SUITE_P(VendorCgroups, JsonSchemaTest,
+                         ::testing::Values(MakeTestParam<Cgroups>(kVendorCgroups)));
+INSTANTIATE_TEST_SUITE_P(VendorCgroups, CgroupsTest,
+                         ::testing::Values(MakeTestParam<Cgroups>(kVendorCgroups)));
+
+INSTANTIATE_TEST_SUITE_P(VendorTaskProfiles, JsonSchemaTest,
+                         ::testing::Values(MakeTestParam<TaskProfiles>(kVendorTaskProfiles)));
+INSTANTIATE_TEST_SUITE_P(VendorTaskProfiles, TaskProfilesTest,
+                         ::testing::Values(MakeTestParam<TaskProfiles>(kVendorTaskProfiles)));
+
+}  // namespace profiles
+}  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/libprocessgroup/profiles/vts_processgroup_validate_test.xml b/libprocessgroup/profiles/vts_processgroup_validate_test.xml
new file mode 100644
index 0000000..21d29cd
--- /dev/null
+++ b/libprocessgroup/profiles/vts_processgroup_validate_test.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+          http://www.apache.org/licenses/LICENSE-2.0
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for VtsProcessgroupValidateTest">
+    <option name="config-descriptor:metadata" key="plan" value="vts-treble" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+        <option name="abort-on-push-failure" value="false"/>
+        <option name="push-group" value="HostDrivenTest.push"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+        <option name="test-module-name" value="VtsProcessgroupValidateTest"/>
+        <option name="binary-test-working-directory" value="_32bit::/data/nativetest/" />
+        <option name="binary-test-working-directory" value="_64bit::/data/nativetest64/" />
+        <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_processgroup_validate_test/vts_processgroup_validate_test" />
+        <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_processgroup_validate_test/vts_processgroup_validate_test" />
+        <option name="binary-test-type" value="gtest"/>
+        <option name="binary-test-disable-framework" value="false"/>
+        <option name="test-timeout" value="30s"/>
+    </test>
+</configuration>
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
new file mode 100644
index 0000000..15f8139
--- /dev/null
+++ b/libprocessgroup/sched_policy.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <processgroup/sched_policy.h>
+
+#define LOG_TAG "SchedPolicy"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/threads.h>
+#include <cgroup_map.h>
+#include <processgroup/processgroup.h>
+
+using android::base::GetThreadId;
+
+/* 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.
+ * Returns the possibly re-mapped policy.
+ */
+static inline SchedPolicy _policy(SchedPolicy p) {
+    return p == SP_DEFAULT ? SP_SYSTEM_DEFAULT : p;
+}
+
+#if defined(__ANDROID__)
+
+int set_cpuset_policy(int tid, SchedPolicy policy) {
+    if (tid == 0) {
+        tid = GetThreadId();
+    }
+    policy = _policy(policy);
+
+    switch (policy) {
+        case SP_BACKGROUND:
+            return SetTaskProfiles(tid,
+                                   {"HighEnergySaving", "ProcessCapacityLow", "LowIoPriority",
+                                    "TimerSlackHigh"},
+                                   true)
+                           ? 0
+                           : -1;
+        case SP_FOREGROUND:
+        case SP_AUDIO_APP:
+        case SP_AUDIO_SYS:
+            return SetTaskProfiles(tid,
+                                   {"HighPerformance", "ProcessCapacityHigh", "HighIoPriority",
+                                    "TimerSlackNormal"},
+                                   true)
+                           ? 0
+                           : -1;
+        case SP_TOP_APP:
+            return SetTaskProfiles(tid,
+                                   {"MaxPerformance", "ProcessCapacityMax", "MaxIoPriority",
+                                    "TimerSlackNormal"},
+                                   true)
+                           ? 0
+                           : -1;
+        case SP_SYSTEM:
+            return SetTaskProfiles(tid, {"ServiceCapacityLow", "TimerSlackNormal"}, true) ? 0 : -1;
+        case SP_RESTRICTED:
+            return SetTaskProfiles(tid, {"ServiceCapacityRestricted", "TimerSlackNormal"}, true)
+                           ? 0
+                           : -1;
+        default:
+            break;
+    }
+
+    return 0;
+}
+
+int set_sched_policy(int tid, SchedPolicy policy) {
+    if (tid == 0) {
+        tid = GetThreadId();
+    }
+    policy = _policy(policy);
+
+#if POLICY_DEBUG
+    char statfile[64];
+    char statline[1024];
+    char thread_name[255];
+
+    snprintf(statfile, sizeof(statfile), "/proc/%d/stat", tid);
+    memset(thread_name, 0, sizeof(thread_name));
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(statfile, O_RDONLY | O_CLOEXEC)));
+    if (fd >= 0) {
+        int rc = read(fd, statline, 1023);
+        statline[rc] = 0;
+        char* p = statline;
+        char* q;
+
+        for (p = statline; *p != '('; p++)
+            ;
+        p++;
+        for (q = p; *q != ')'; q++)
+            ;
+
+        strncpy(thread_name, p, (q - p));
+    }
+    switch (policy) {
+        case SP_BACKGROUND:
+            SLOGD("vvv tid %d (%s)", tid, thread_name);
+            break;
+        case SP_FOREGROUND:
+        case SP_AUDIO_APP:
+        case SP_AUDIO_SYS:
+        case SP_TOP_APP:
+            SLOGD("^^^ tid %d (%s)", tid, thread_name);
+            break;
+        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;
+    }
+#endif
+
+    switch (policy) {
+        case SP_BACKGROUND:
+            return SetTaskProfiles(tid, {"HighEnergySaving", "TimerSlackHigh"}, true) ? 0 : -1;
+        case SP_FOREGROUND:
+        case SP_AUDIO_APP:
+        case SP_AUDIO_SYS:
+            return SetTaskProfiles(tid, {"HighPerformance", "TimerSlackNormal"}, true) ? 0 : -1;
+        case SP_TOP_APP:
+            return SetTaskProfiles(tid, {"MaxPerformance", "TimerSlackNormal"}, true) ? 0 : -1;
+        case SP_RT_APP:
+            return SetTaskProfiles(tid, {"RealtimePerformance", "TimerSlackNormal"}, true) ? 0 : -1;
+        default:
+            return SetTaskProfiles(tid, {"TimerSlackNormal"}, true) ? 0 : -1;
+    }
+
+    return 0;
+}
+
+bool cpusets_enabled() {
+    static bool enabled = (CgroupMap::GetInstance().FindController("cpuset").IsUsable());
+    return enabled;
+}
+
+bool schedboost_enabled() {
+    static bool enabled = (CgroupMap::GetInstance().FindController("schedtune").IsUsable());
+    return enabled;
+}
+
+static int getCGroupSubsys(int tid, const char* subsys, std::string& subgroup) {
+    auto controller = CgroupMap::GetInstance().FindController(subsys);
+
+    if (!controller.IsUsable()) return -1;
+
+    if (!controller.GetTaskGroup(tid, &subgroup)) {
+        LOG(ERROR) << "Failed to find cgroup for tid " << tid;
+        return -1;
+    }
+    return 0;
+}
+
+int get_sched_policy(int tid, SchedPolicy* policy) {
+    if (tid == 0) {
+        tid = GetThreadId();
+    }
+
+    std::string group;
+    if (schedboost_enabled()) {
+        if (getCGroupSubsys(tid, "schedtune", group) < 0) return -1;
+    }
+    if (group.empty() && cpusets_enabled()) {
+        if (getCGroupSubsys(tid, "cpuset", group) < 0) return -1;
+    }
+
+    // TODO: replace hardcoded directories
+    if (group.empty()) {
+        *policy = SP_FOREGROUND;
+    } else if (group == "foreground") {
+        *policy = SP_FOREGROUND;
+    } else if (group == "system-background") {
+        *policy = SP_SYSTEM;
+    } else if (group == "background") {
+        *policy = SP_BACKGROUND;
+    } else if (group == "top-app") {
+        *policy = SP_TOP_APP;
+    } else if (group == "restricted") {
+        *policy = SP_RESTRICTED;
+    } else {
+        errno = ERANGE;
+        return -1;
+    }
+    return 0;
+}
+
+#else
+
+/* Stubs for non-Android targets. */
+
+int set_sched_policy(int, SchedPolicy) {
+    return 0;
+}
+
+int get_sched_policy(int, SchedPolicy* policy) {
+    *policy = SP_SYSTEM_DEFAULT;
+    return 0;
+}
+
+#endif
+
+const char* get_sched_policy_name(SchedPolicy policy) {
+    policy = _policy(policy);
+    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",
+    };
+    static_assert(arraysize(kSchedPolicyNames) == SP_CNT, "missing name");
+    if (policy < SP_BACKGROUND || policy >= SP_CNT) {
+        return "error";
+    }
+    return kSchedPolicyNames[policy];
+}
diff --git a/libprocessgroup/setup/Android.bp b/libprocessgroup/setup/Android.bp
new file mode 100644
index 0000000..f6fc066
--- /dev/null
+++ b/libprocessgroup/setup/Android.bp
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_shared {
+    name: "libprocessgroup_setup",
+    recovery_available: true,
+    srcs: [
+        "cgroup_map_write.cpp",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcgrouprc",
+        "libjsoncpp",
+    ],
+    static_libs: [
+        "libcgrouprc_format",
+    ],
+    header_libs: [
+        "libprocessgroup_headers",
+    ],
+    export_header_lib_headers: [
+        "libprocessgroup_headers",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/libprocessgroup/setup/cgroup_descriptor.h b/libprocessgroup/setup/cgroup_descriptor.h
new file mode 100644
index 0000000..f029c4f
--- /dev/null
+++ b/libprocessgroup/setup/cgroup_descriptor.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <processgroup/format/cgroup_controller.h>
+
+namespace android {
+namespace cgrouprc {
+
+// Complete controller description for mounting cgroups
+class CgroupDescriptor {
+  public:
+    CgroupDescriptor(uint32_t version, const std::string& name, const std::string& path,
+                     mode_t mode, const std::string& uid, const std::string& gid);
+
+    const format::CgroupController* controller() const { return &controller_; }
+    mode_t mode() const { return mode_; }
+    std::string uid() const { return uid_; }
+    std::string gid() const { return gid_; }
+
+    void set_mounted(bool mounted);
+
+  private:
+    format::CgroupController controller_;
+    mode_t mode_ = 0;
+    std::string uid_;
+    std::string gid_;
+};
+
+}  // namespace cgrouprc
+}  // namespace android
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
new file mode 100644
index 0000000..17ea06e
--- /dev/null
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "libprocessgroup"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <regex>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <android/cgrouprc.h>
+#include <json/reader.h>
+#include <json/value.h>
+#include <processgroup/format/cgroup_file.h>
+#include <processgroup/processgroup.h>
+#include <processgroup/setup.h>
+
+#include "cgroup_descriptor.h"
+
+using android::base::GetBoolProperty;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+namespace android {
+namespace cgrouprc {
+
+static constexpr const char* CGROUPS_DESC_FILE = "/etc/cgroups.json";
+static constexpr const char* CGROUPS_DESC_VENDOR_FILE = "/vendor/etc/cgroups.json";
+
+static bool Mkdir(const std::string& path, mode_t mode, const std::string& uid,
+                  const std::string& gid) {
+    if (mode == 0) {
+        mode = 0755;
+    }
+
+    if (mkdir(path.c_str(), mode) != 0) {
+        /* chmod in case the directory already exists */
+        if (errno == EEXIST) {
+            if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
+                // /acct is a special case when the directory already exists
+                // TODO: check if file mode is already what we want instead of using EROFS
+                if (errno != EROFS) {
+                    PLOG(ERROR) << "fchmodat() failed for " << path;
+                    return false;
+                }
+            }
+        } else {
+            PLOG(ERROR) << "mkdir() failed for " << path;
+            return false;
+        }
+    }
+
+    if (uid.empty()) {
+        return true;
+    }
+
+    passwd* uid_pwd = getpwnam(uid.c_str());
+    if (!uid_pwd) {
+        PLOG(ERROR) << "Unable to decode UID for '" << uid << "'";
+        return false;
+    }
+
+    uid_t pw_uid = uid_pwd->pw_uid;
+    gid_t gr_gid = -1;
+    if (!gid.empty()) {
+        group* gid_pwd = getgrnam(gid.c_str());
+        if (!gid_pwd) {
+            PLOG(ERROR) << "Unable to decode GID for '" << gid << "'";
+            return false;
+        }
+        gr_gid = gid_pwd->gr_gid;
+    }
+
+    if (lchown(path.c_str(), pw_uid, gr_gid) < 0) {
+        PLOG(ERROR) << "lchown() failed for " << path;
+        return false;
+    }
+
+    /* chown may have cleared S_ISUID and S_ISGID, chmod again */
+    if (mode & (S_ISUID | S_ISGID)) {
+        if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
+            PLOG(ERROR) << "fchmodat() failed for " << path;
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static bool ReadDescriptorsFromFile(const std::string& file_name,
+                                    std::map<std::string, CgroupDescriptor>* descriptors) {
+    std::vector<CgroupDescriptor> result;
+    std::string json_doc;
+
+    if (!android::base::ReadFileToString(file_name, &json_doc)) {
+        PLOG(ERROR) << "Failed to read task profiles from " << file_name;
+        return false;
+    }
+
+    Json::Reader reader;
+    Json::Value root;
+    if (!reader.parse(json_doc, root)) {
+        LOG(ERROR) << "Failed to parse cgroups description: " << reader.getFormattedErrorMessages();
+        return false;
+    }
+
+    if (root.isMember("Cgroups")) {
+        const Json::Value& cgroups = root["Cgroups"];
+        for (Json::Value::ArrayIndex i = 0; i < cgroups.size(); ++i) {
+            std::string name = cgroups[i]["Controller"].asString();
+            auto iter = descriptors->find(name);
+            if (iter == descriptors->end()) {
+                descriptors->emplace(
+                        name, CgroupDescriptor(
+                                      1, name, cgroups[i]["Path"].asString(),
+                                      std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
+                                      cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString()));
+            } else {
+                iter->second = CgroupDescriptor(
+                        1, name, cgroups[i]["Path"].asString(),
+                        std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
+                        cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString());
+            }
+        }
+    }
+
+    if (root.isMember("Cgroups2")) {
+        const Json::Value& cgroups2 = root["Cgroups2"];
+        auto iter = descriptors->find(CGROUPV2_CONTROLLER_NAME);
+        if (iter == descriptors->end()) {
+            descriptors->emplace(
+                    CGROUPV2_CONTROLLER_NAME,
+                    CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
+                                     std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
+                                     cgroups2["UID"].asString(), cgroups2["GID"].asString()));
+        } else {
+            iter->second =
+                    CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
+                                     std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
+                                     cgroups2["UID"].asString(), cgroups2["GID"].asString());
+        }
+    }
+
+    return true;
+}
+
+static bool ReadDescriptors(std::map<std::string, CgroupDescriptor>* descriptors) {
+    // load system cgroup descriptors
+    if (!ReadDescriptorsFromFile(CGROUPS_DESC_FILE, descriptors)) {
+        return false;
+    }
+
+    // load vendor cgroup descriptors if the file exists
+    if (!access(CGROUPS_DESC_VENDOR_FILE, F_OK) &&
+        !ReadDescriptorsFromFile(CGROUPS_DESC_VENDOR_FILE, descriptors)) {
+        return false;
+    }
+
+    return true;
+}
+
+// To avoid issues in sdk_mac build
+#if defined(__ANDROID__)
+
+static bool SetupCgroup(const CgroupDescriptor& descriptor) {
+    const format::CgroupController* controller = descriptor.controller();
+
+    // mkdir <path> [mode] [owner] [group]
+    if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
+        LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
+        return false;
+    }
+
+    int result;
+    if (controller->version() == 2) {
+        result = mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+                       nullptr);
+    } else {
+        // Unfortunately historically cpuset controller was mounted using a mount command
+        // different from all other controllers. This results in controller attributes not
+        // to be prepended with controller name. For example this way instead of
+        // /dev/cpuset/cpuset.cpus the attribute becomes /dev/cpuset/cpus which is what
+        // the system currently expects.
+        if (!strcmp(controller->name(), "cpuset")) {
+            // mount cpuset none /dev/cpuset nodev noexec nosuid
+            result = mount("none", controller->path(), controller->name(),
+                           MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr);
+        } else {
+            // mount cgroup none <path> nodev noexec nosuid <controller>
+            result = mount("none", controller->path(), "cgroup", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+                           controller->name());
+        }
+    }
+
+    if (result < 0) {
+        PLOG(ERROR) << "Failed to mount " << controller->name() << " cgroup";
+        return false;
+    }
+
+    return true;
+}
+
+#else
+
+// Stubs for non-Android targets.
+static bool SetupCgroup(const CgroupDescriptor&) {
+    return false;
+}
+
+#endif
+
+static bool WriteRcFile(const std::map<std::string, CgroupDescriptor>& descriptors) {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(CGROUPS_RC_PATH, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
+                                         S_IRUSR | S_IRGRP | S_IROTH)));
+    if (fd < 0) {
+        PLOG(ERROR) << "open() failed for " << CGROUPS_RC_PATH;
+        return false;
+    }
+
+    format::CgroupFile fl;
+    fl.version_ = format::CgroupFile::FILE_CURR_VERSION;
+    fl.controller_count_ = descriptors.size();
+    int ret = TEMP_FAILURE_RETRY(write(fd, &fl, sizeof(fl)));
+    if (ret < 0) {
+        PLOG(ERROR) << "write() failed for " << CGROUPS_RC_PATH;
+        return false;
+    }
+
+    for (const auto& [name, descriptor] : descriptors) {
+        ret = TEMP_FAILURE_RETRY(
+                write(fd, descriptor.controller(), sizeof(format::CgroupController)));
+        if (ret < 0) {
+            PLOG(ERROR) << "write() failed for " << CGROUPS_RC_PATH;
+            return false;
+        }
+    }
+
+    return true;
+}
+
+CgroupDescriptor::CgroupDescriptor(uint32_t version, const std::string& name,
+                                   const std::string& path, mode_t mode, const std::string& uid,
+                                   const std::string& gid)
+    : controller_(version, 0, name, path), mode_(mode), uid_(uid), gid_(gid) {}
+
+void CgroupDescriptor::set_mounted(bool mounted) {
+    uint32_t flags = controller_.flags();
+    if (mounted) {
+        flags |= CGROUPRC_CONTROLLER_FLAG_MOUNTED;
+    } else {
+        flags &= ~CGROUPRC_CONTROLLER_FLAG_MOUNTED;
+    }
+    controller_.set_flags(flags);
+}
+
+}  // namespace cgrouprc
+}  // namespace android
+
+bool CgroupSetup() {
+    using namespace android::cgrouprc;
+
+    std::map<std::string, CgroupDescriptor> descriptors;
+
+    if (getpid() != 1) {
+        LOG(ERROR) << "Cgroup setup can be done only by init process";
+        return false;
+    }
+
+    // Make sure we do this only one time. No need for std::call_once because
+    // init is a single-threaded process
+    if (access(CGROUPS_RC_PATH, F_OK) == 0) {
+        LOG(WARNING) << "Attempt to call SetupCgroups more than once";
+        return true;
+    }
+
+    // load cgroups.json file
+    if (!ReadDescriptors(&descriptors)) {
+        LOG(ERROR) << "Failed to load cgroup description file";
+        return false;
+    }
+
+    // setup cgroups
+    for (auto& [name, descriptor] : descriptors) {
+        if (SetupCgroup(descriptor)) {
+            descriptor.set_mounted(true);
+        } else {
+            // issue a warning and proceed with the next cgroup
+            LOG(WARNING) << "Failed to setup " << name << " cgroup";
+        }
+    }
+
+    // mkdir <CGROUPS_RC_DIR> 0711 system system
+    if (!Mkdir(android::base::Dirname(CGROUPS_RC_PATH), 0711, "system", "system")) {
+        LOG(ERROR) << "Failed to create directory for " << CGROUPS_RC_PATH << " file";
+        return false;
+    }
+
+    // Generate <CGROUPS_RC_FILE> file which can be directly mmapped into
+    // process memory. This optimizes performance, memory usage
+    // and limits infrormation shared with unprivileged processes
+    // to the minimum subset of information from cgroups.json
+    if (!WriteRcFile(descriptors)) {
+        LOG(ERROR) << "Failed to write " << CGROUPS_RC_PATH << " file";
+        return false;
+    }
+
+    // chmod 0644 <CGROUPS_RC_PATH>
+    if (fchmodat(AT_FDCWD, CGROUPS_RC_PATH, 0644, AT_SYMLINK_NOFOLLOW) < 0) {
+        PLOG(ERROR) << "fchmodat() failed";
+        return false;
+    }
+
+    return true;
+}
diff --git a/libprocessgroup/setup/include/processgroup/setup.h b/libprocessgroup/setup/include/processgroup/setup.h
new file mode 100644
index 0000000..6ea1979
--- /dev/null
+++ b/libprocessgroup/setup/include/processgroup/setup.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
+
+bool CgroupSetup();
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
new file mode 100644
index 0000000..aee5f0c
--- /dev/null
+++ b/libprocessgroup/task_profiles.cpp
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "libprocessgroup"
+
+#include <fcntl.h>
+#include <task_profiles.h>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/threads.h>
+
+#include <cutils/android_filesystem_config.h>
+
+#include <json/reader.h>
+#include <json/value.h>
+
+// To avoid issues in sdk_mac build
+#if defined(__ANDROID__)
+#include <sys/prctl.h>
+#endif
+
+using android::base::GetThreadId;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::base::WriteStringToFile;
+
+#define TASK_PROFILE_DB_FILE "/etc/task_profiles.json"
+#define TASK_PROFILE_DB_VENDOR_FILE "/vendor/etc/task_profiles.json"
+
+bool ProfileAttribute::GetPathForTask(int tid, std::string* path) const {
+    std::string subgroup;
+    if (!controller()->GetTaskGroup(tid, &subgroup)) {
+        return false;
+    }
+
+    if (path == nullptr) {
+        return true;
+    }
+
+    if (subgroup.empty()) {
+        *path = StringPrintf("%s/%s", controller()->path(), file_name_.c_str());
+    } else {
+        *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(),
+                             file_name_.c_str());
+    }
+    return true;
+}
+
+bool SetClampsAction::ExecuteForProcess(uid_t, pid_t) const {
+    // TODO: add support when kernel supports util_clamp
+    LOG(WARNING) << "SetClampsAction::ExecuteForProcess is not supported";
+    return false;
+}
+
+bool SetClampsAction::ExecuteForTask(int) const {
+    // TODO: add support when kernel supports util_clamp
+    LOG(WARNING) << "SetClampsAction::ExecuteForTask is not supported";
+    return false;
+}
+
+// To avoid issues in sdk_mac build
+#if defined(__ANDROID__)
+
+bool SetTimerSlackAction::IsTimerSlackSupported(int tid) {
+    auto file = StringPrintf("/proc/%d/timerslack_ns", tid);
+
+    return (access(file.c_str(), W_OK) == 0);
+}
+
+bool SetTimerSlackAction::ExecuteForTask(int tid) const {
+    static bool sys_supports_timerslack = IsTimerSlackSupported(tid);
+
+    // v4.6+ kernels support the /proc/<tid>/timerslack_ns interface.
+    // TODO: once we've backported this, log if the open(2) fails.
+    if (sys_supports_timerslack) {
+        auto file = StringPrintf("/proc/%d/timerslack_ns", tid);
+        if (!WriteStringToFile(std::to_string(slack_), file)) {
+            if (errno == ENOENT) {
+                // This happens when process is already dead
+                return true;
+            }
+            PLOG(ERROR) << "set_timerslack_ns write failed";
+        }
+    }
+
+    // TODO: Remove when /proc/<tid>/timerslack_ns interface is backported.
+    if (tid == 0 || tid == GetThreadId()) {
+        if (prctl(PR_SET_TIMERSLACK, slack_) == -1) {
+            PLOG(ERROR) << "set_timerslack_ns prctl failed";
+        }
+    }
+
+    return true;
+}
+
+#endif
+
+bool SetAttributeAction::ExecuteForProcess(uid_t, pid_t pid) const {
+    return ExecuteForTask(pid);
+}
+
+bool SetAttributeAction::ExecuteForTask(int tid) const {
+    std::string path;
+
+    if (!attribute_->GetPathForTask(tid, &path)) {
+        LOG(ERROR) << "Failed to find cgroup for tid " << tid;
+        return false;
+    }
+
+    if (!WriteStringToFile(value_, path)) {
+        PLOG(ERROR) << "Failed to write '" << value_ << "' to " << path;
+        return false;
+    }
+
+    return true;
+}
+
+bool SetCgroupAction::IsAppDependentPath(const std::string& path) {
+    return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
+}
+
+SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
+    : controller_(c), path_(p) {
+    // file descriptors for app-dependent paths can't be cached
+    if (IsAppDependentPath(path_)) {
+        // file descriptor is not cached
+        fd_.reset(FDS_APP_DEPENDENT);
+        return;
+    }
+
+    // file descriptor can be cached later on request
+    fd_.reset(FDS_NOT_CACHED);
+}
+
+void SetCgroupAction::EnableResourceCaching() {
+    std::lock_guard<std::mutex> lock(fd_mutex_);
+    if (fd_ != FDS_NOT_CACHED) {
+        return;
+    }
+
+    std::string tasks_path = controller_.GetTasksFilePath(path_);
+
+    if (access(tasks_path.c_str(), W_OK) != 0) {
+        // file is not accessible
+        fd_.reset(FDS_INACCESSIBLE);
+        return;
+    }
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
+    if (fd < 0) {
+        PLOG(ERROR) << "Failed to cache fd '" << tasks_path << "'";
+        fd_.reset(FDS_INACCESSIBLE);
+        return;
+    }
+
+    fd_ = std::move(fd);
+}
+
+void SetCgroupAction::DropResourceCaching() {
+    std::lock_guard<std::mutex> lock(fd_mutex_);
+    if (fd_ == FDS_NOT_CACHED) {
+        return;
+    }
+
+    fd_.reset(FDS_NOT_CACHED);
+}
+
+bool SetCgroupAction::AddTidToCgroup(int tid, int fd) {
+    if (tid <= 0) {
+        return true;
+    }
+
+    std::string value = std::to_string(tid);
+
+    if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) < 0) {
+        // If the thread is in the process of exiting, don't flag an error
+        if (errno != ESRCH) {
+            PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd;
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
+    std::lock_guard<std::mutex> lock(fd_mutex_);
+    if (IsFdValid()) {
+        // fd is cached, reuse it
+        if (!AddTidToCgroup(pid, fd_)) {
+            LOG(ERROR) << "Failed to add task into cgroup";
+            return false;
+        }
+        return true;
+    }
+
+    if (fd_ == FDS_INACCESSIBLE) {
+        // no permissions to access the file, ignore
+        return true;
+    }
+
+    // this is app-dependent path and fd is not cached or cached fd can't be used
+    std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
+    unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
+    if (tmp_fd < 0) {
+        PLOG(WARNING) << "Failed to open " << procs_path;
+        return false;
+    }
+    if (!AddTidToCgroup(pid, tmp_fd)) {
+        LOG(ERROR) << "Failed to add task into cgroup";
+        return false;
+    }
+
+    return true;
+}
+
+bool SetCgroupAction::ExecuteForTask(int tid) const {
+    std::lock_guard<std::mutex> lock(fd_mutex_);
+    if (IsFdValid()) {
+        // fd is cached, reuse it
+        if (!AddTidToCgroup(tid, fd_)) {
+            LOG(ERROR) << "Failed to add task into cgroup";
+            return false;
+        }
+        return true;
+    }
+
+    if (fd_ == FDS_INACCESSIBLE) {
+        // no permissions to access the file, ignore
+        return true;
+    }
+
+    if (fd_ == FDS_APP_DEPENDENT) {
+        // application-dependent path can't be used with tid
+        PLOG(ERROR) << "Application profile can't be applied to a thread";
+        return false;
+    }
+
+    // fd was not cached because cached fd can't be used
+    std::string tasks_path = controller()->GetTasksFilePath(path_);
+    unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
+    if (tmp_fd < 0) {
+        PLOG(WARNING) << "Failed to open " << tasks_path << ": " << strerror(errno);
+        return false;
+    }
+    if (!AddTidToCgroup(tid, tmp_fd)) {
+        LOG(ERROR) << "Failed to add task into cgroup";
+        return false;
+    }
+
+    return true;
+}
+
+bool TaskProfile::ExecuteForProcess(uid_t uid, pid_t pid) const {
+    for (const auto& element : elements_) {
+        if (!element->ExecuteForProcess(uid, pid)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool TaskProfile::ExecuteForTask(int tid) const {
+    if (tid == 0) {
+        tid = GetThreadId();
+    }
+    for (const auto& element : elements_) {
+        if (!element->ExecuteForTask(tid)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void TaskProfile::EnableResourceCaching() {
+    if (res_cached_) {
+        return;
+    }
+
+    for (auto& element : elements_) {
+        element->EnableResourceCaching();
+    }
+
+    res_cached_ = true;
+}
+
+void TaskProfile::DropResourceCaching() {
+    if (!res_cached_) {
+        return;
+    }
+
+    for (auto& element : elements_) {
+        element->DropResourceCaching();
+    }
+
+    res_cached_ = false;
+}
+
+void TaskProfiles::DropResourceCaching() const {
+    for (auto& iter : profiles_) {
+        iter.second->DropResourceCaching();
+    }
+}
+
+TaskProfiles& TaskProfiles::GetInstance() {
+    // Deliberately leak this object to avoid a race between destruction on
+    // process exit and concurrent access from another thread.
+    static auto* instance = new TaskProfiles;
+    return *instance;
+}
+
+TaskProfiles::TaskProfiles() {
+    // load system task profiles
+    if (!Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_FILE)) {
+        LOG(ERROR) << "Loading " << TASK_PROFILE_DB_FILE << " for [" << getpid() << "] failed";
+    }
+
+    // load vendor task profiles if the file exists
+    if (!access(TASK_PROFILE_DB_VENDOR_FILE, F_OK) &&
+        !Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_VENDOR_FILE)) {
+        LOG(ERROR) << "Loading " << TASK_PROFILE_DB_VENDOR_FILE << " for [" << getpid()
+                   << "] failed";
+    }
+}
+
+bool TaskProfiles::Load(const CgroupMap& cg_map, const std::string& file_name) {
+    std::string json_doc;
+
+    if (!android::base::ReadFileToString(file_name, &json_doc)) {
+        LOG(ERROR) << "Failed to read task profiles from " << file_name;
+        return false;
+    }
+
+    Json::Reader reader;
+    Json::Value root;
+    if (!reader.parse(json_doc, root)) {
+        LOG(ERROR) << "Failed to parse task profiles: " << reader.getFormattedErrorMessages();
+        return false;
+    }
+
+    const Json::Value& attr = root["Attributes"];
+    for (Json::Value::ArrayIndex i = 0; i < attr.size(); ++i) {
+        std::string name = attr[i]["Name"].asString();
+        std::string controller_name = attr[i]["Controller"].asString();
+        std::string file_attr = attr[i]["File"].asString();
+
+        if (attributes_.find(name) == attributes_.end()) {
+            auto controller = cg_map.FindController(controller_name);
+            if (controller.HasValue()) {
+                attributes_[name] = std::make_unique<ProfileAttribute>(controller, file_attr);
+            } else {
+                LOG(WARNING) << "Controller " << controller_name << " is not found";
+            }
+        } else {
+            LOG(WARNING) << "Attribute " << name << " is already defined";
+        }
+    }
+
+    std::map<std::string, std::string> params;
+
+    const Json::Value& profiles_val = root["Profiles"];
+    for (Json::Value::ArrayIndex i = 0; i < profiles_val.size(); ++i) {
+        const Json::Value& profile_val = profiles_val[i];
+
+        std::string profile_name = profile_val["Name"].asString();
+        const Json::Value& actions = profile_val["Actions"];
+        auto profile = std::make_unique<TaskProfile>();
+
+        for (Json::Value::ArrayIndex act_idx = 0; act_idx < actions.size(); ++act_idx) {
+            const Json::Value& action_val = actions[act_idx];
+            std::string action_name = action_val["Name"].asString();
+            const Json::Value& params_val = action_val["Params"];
+            if (action_name == "JoinCgroup") {
+                std::string controller_name = params_val["Controller"].asString();
+                std::string path = params_val["Path"].asString();
+
+                auto controller = cg_map.FindController(controller_name);
+                if (controller.HasValue()) {
+                    profile->Add(std::make_unique<SetCgroupAction>(controller, path));
+                } else {
+                    LOG(WARNING) << "JoinCgroup: controller " << controller_name << " is not found";
+                }
+            } else if (action_name == "SetTimerSlack") {
+                std::string slack_value = params_val["Slack"].asString();
+                char* end;
+                unsigned long slack;
+
+                slack = strtoul(slack_value.c_str(), &end, 10);
+                if (end > slack_value.c_str()) {
+                    profile->Add(std::make_unique<SetTimerSlackAction>(slack));
+                } else {
+                    LOG(WARNING) << "SetTimerSlack: invalid parameter: " << slack_value;
+                }
+            } else if (action_name == "SetAttribute") {
+                std::string attr_name = params_val["Name"].asString();
+                std::string attr_value = params_val["Value"].asString();
+
+                auto iter = attributes_.find(attr_name);
+                if (iter != attributes_.end()) {
+                    profile->Add(
+                            std::make_unique<SetAttributeAction>(iter->second.get(), attr_value));
+                } else {
+                    LOG(WARNING) << "SetAttribute: unknown attribute: " << attr_name;
+                }
+            } else if (action_name == "SetClamps") {
+                std::string boost_value = params_val["Boost"].asString();
+                std::string clamp_value = params_val["Clamp"].asString();
+                char* end;
+                unsigned long boost;
+
+                boost = strtoul(boost_value.c_str(), &end, 10);
+                if (end > boost_value.c_str()) {
+                    unsigned long clamp = strtoul(clamp_value.c_str(), &end, 10);
+                    if (end > clamp_value.c_str()) {
+                        profile->Add(std::make_unique<SetClampsAction>(boost, clamp));
+                    } else {
+                        LOG(WARNING) << "SetClamps: invalid parameter " << clamp_value;
+                    }
+                } else {
+                    LOG(WARNING) << "SetClamps: invalid parameter: " << boost_value;
+                }
+            } else {
+                LOG(WARNING) << "Unknown profile action: " << action_name;
+            }
+        }
+        profiles_[profile_name] = std::move(profile);
+    }
+
+    return true;
+}
+
+TaskProfile* TaskProfiles::GetProfile(const std::string& name) const {
+    auto iter = profiles_.find(name);
+
+    if (iter != profiles_.end()) {
+        return iter->second.get();
+    }
+    return nullptr;
+}
+
+const ProfileAttribute* TaskProfiles::GetAttribute(const std::string& name) const {
+    auto iter = attributes_.find(name);
+
+    if (iter != attributes_.end()) {
+        return iter->second.get();
+    }
+    return nullptr;
+}
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
new file mode 100644
index 0000000..891d5b5
--- /dev/null
+++ b/libprocessgroup/task_profiles.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/cdefs.h>
+#include <sys/types.h>
+#include <map>
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <cgroup_map.h>
+
+class ProfileAttribute {
+  public:
+    ProfileAttribute(const CgroupController& controller, const std::string& file_name)
+        : controller_(controller), file_name_(file_name) {}
+
+    const CgroupController* controller() const { return &controller_; }
+    const std::string& file_name() const { return file_name_; }
+
+    bool GetPathForTask(int tid, std::string* path) const;
+
+  private:
+    CgroupController controller_;
+    std::string file_name_;
+};
+
+// Abstract profile element
+class ProfileAction {
+  public:
+    virtual ~ProfileAction() {}
+
+    // Default implementations will fail
+    virtual bool ExecuteForProcess(uid_t, pid_t) const { return false; };
+    virtual bool ExecuteForTask(int) const { return false; };
+
+    virtual void EnableResourceCaching() {}
+    virtual void DropResourceCaching() {}
+};
+
+// Profile actions
+class SetClampsAction : public ProfileAction {
+  public:
+    SetClampsAction(int boost, int clamp) noexcept : boost_(boost), clamp_(clamp) {}
+
+    virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
+    virtual bool ExecuteForTask(int tid) const;
+
+  protected:
+    int boost_;
+    int clamp_;
+};
+
+// To avoid issues in sdk_mac build
+#if defined(__ANDROID__)
+
+class SetTimerSlackAction : public ProfileAction {
+  public:
+    SetTimerSlackAction(unsigned long slack) noexcept : slack_(slack) {}
+
+    virtual bool ExecuteForTask(int tid) const;
+
+  private:
+    unsigned long slack_;
+
+    static bool IsTimerSlackSupported(int tid);
+};
+
+#else
+
+class SetTimerSlackAction : public ProfileAction {
+  public:
+    SetTimerSlackAction(unsigned long) noexcept {}
+
+    virtual bool ExecuteForTask(int) const { return true; }
+};
+
+#endif
+
+// Set attribute profile element
+class SetAttributeAction : public ProfileAction {
+  public:
+    SetAttributeAction(const ProfileAttribute* attribute, const std::string& value)
+        : attribute_(attribute), value_(value) {}
+
+    virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
+    virtual bool ExecuteForTask(int tid) const;
+
+  private:
+    const ProfileAttribute* attribute_;
+    std::string value_;
+};
+
+// Set cgroup profile element
+class SetCgroupAction : public ProfileAction {
+  public:
+    SetCgroupAction(const CgroupController& c, const std::string& p);
+
+    virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
+    virtual bool ExecuteForTask(int tid) const;
+    virtual void EnableResourceCaching();
+    virtual void DropResourceCaching();
+
+    const CgroupController* controller() const { return &controller_; }
+    std::string path() const { return path_; }
+
+  private:
+    enum FdState {
+        FDS_INACCESSIBLE = -1,
+        FDS_APP_DEPENDENT = -2,
+        FDS_NOT_CACHED = -3,
+    };
+
+    CgroupController controller_;
+    std::string path_;
+    android::base::unique_fd fd_;
+    mutable std::mutex fd_mutex_;
+
+    static bool IsAppDependentPath(const std::string& path);
+    static bool AddTidToCgroup(int tid, int fd);
+
+    bool IsFdValid() const { return fd_ > FDS_INACCESSIBLE; }
+};
+
+class TaskProfile {
+  public:
+    TaskProfile() : res_cached_(false) {}
+
+    void Add(std::unique_ptr<ProfileAction> e) { elements_.push_back(std::move(e)); }
+
+    bool ExecuteForProcess(uid_t uid, pid_t pid) const;
+    bool ExecuteForTask(int tid) const;
+    void EnableResourceCaching();
+    void DropResourceCaching();
+
+  private:
+    bool res_cached_;
+    std::vector<std::unique_ptr<ProfileAction>> elements_;
+};
+
+class TaskProfiles {
+  public:
+    // Should be used by all users
+    static TaskProfiles& GetInstance();
+
+    TaskProfile* GetProfile(const std::string& name) const;
+    const ProfileAttribute* GetAttribute(const std::string& name) const;
+    void DropResourceCaching() const;
+
+  private:
+    std::map<std::string, std::unique_ptr<TaskProfile>> profiles_;
+    std::map<std::string, std::unique_ptr<ProfileAttribute>> attributes_;
+
+    TaskProfiles();
+
+    bool Load(const CgroupMap& cg_map, const std::string& file_name);
+};
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index 83b0a7f..15f03d0 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -27,6 +27,7 @@
     name: "libprocinfo",
     defaults: ["libprocinfo_defaults"],
     vendor_available: true,
+    recovery_available: true,
     vndk: {
         enabled: true,
     },
@@ -59,6 +60,7 @@
     host_supported: true,
     srcs: [
         "process_test.cpp",
+        "process_map_test.cpp",
     ],
     target: {
         darwin: {
@@ -83,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/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..b6ec3cb
--- /dev/null
+++ b/libprocinfo/include/procinfo/process_map.h
@@ -0,0 +1,180 @@
+/*
+ * 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 <string.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#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;
+  ino_t inode;
+  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
+    inode = strtoull(p, &end, 10);
+    if (end == p) {
+      return false;
+    }
+    p = end;
+
+    if (*p != '\0' && !pass_space()) {
+      return false;
+    }
+
+    // filename
+    callback(start_addr, end_addr, flags, pgoff, inode, p);
+  }
+  return true;
+}
+
+inline bool ReadMapFile(const std::string& map_file,
+                        const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_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, ino_t,
+                                                     const char*)>& callback) {
+  return ReadMapFile("/proc/" + std::to_string(pid) + "/maps", callback);
+}
+
+struct MapInfo {
+  uint64_t start;
+  uint64_t end;
+  uint16_t flags;
+  uint64_t pgoff;
+  ino_t inode;
+  std::string name;
+
+  MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+          const char* name)
+      : start(start), end(end), flags(flags), pgoff(pgoff), inode(inode), name(name) {}
+};
+
+inline bool ReadProcessMaps(pid_t pid, std::vector<MapInfo>* maps) {
+  return ReadProcessMaps(
+      pid, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+               const char* name) { maps->emplace_back(start, end, flags, pgoff, inode, name); });
+}
+
+} /* 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..eba4fd0
--- /dev/null
+++ b/libprocinfo/process_map_benchmark.cpp
@@ -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.
+ */
+
+#include <procinfo/process_map.h>
+
+#include <string.h>
+#include <sys/types.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>
+
+static void BM_ReadMapFile(benchmark::State& state) {
+  std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
+  for (auto _ : state) {
+    std::vector<android::procinfo::MapInfo> maps;
+    android::procinfo::ReadMapFile(map_file, [&](uint64_t start, uint64_t end, uint16_t flags,
+                                                 uint64_t pgoff, ino_t inode, const char* name) {
+      maps.emplace_back(start, end, flags, pgoff, inode, 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..562d864
--- /dev/null
+++ b/libprocinfo/process_map_test.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 <procinfo/process_map.h>
+
+#include <string>
+
+#include <android-base/file.h>
+
+#include <gtest/gtest.h>
+
+TEST(process_map, ReadMapFile) {
+  std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
+  std::vector<android::procinfo::MapInfo> maps;
+  ASSERT_TRUE(android::procinfo::ReadMapFile(
+      map_file,
+      [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+          const char* name) { maps.emplace_back(start, end, flags, pgoff, inode, 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].inode, 10267643UL);
+  ASSERT_EQ(maps[0].name, "[anon:dalvik-main space (region space)]");
+  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].inode, 2407UL);
+  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].inode, 10266154UL);
+  ASSERT_EQ(maps[1260].name,
+            "[anon:dalvik-classes.dex extracted in memory from "
+            "/data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk]");
+}
+
+TEST(process_map, ReadProcessMaps) {
+  std::vector<android::procinfo::MapInfo> maps;
+  ASSERT_TRUE(android::procinfo::ReadProcessMaps(
+      getpid(),
+      [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+          const char* name) { maps.emplace_back(start, end, flags, pgoff, inode, name); }));
+  ASSERT_GT(maps.size(), 0u);
+  maps.clear();
+  ASSERT_TRUE(android::procinfo::ReadProcessMaps(getpid(), &maps));
+  ASSERT_GT(maps.size(), 0u);
+}
diff --git a/libprocinfo/testdata/maps b/libprocinfo/testdata/maps
new file mode 100644
index 0000000..098cf25
--- /dev/null
+++ b/libprocinfo/testdata/maps
@@ -0,0 +1,2043 @@
+12c00000-2ac00000 rw-p 00000000 00:05 10267643                           [anon:dalvik-main space (region space)]
+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                           [anon:dalvik-non moving space]
+73b93000-77b8b000 ---p 00008000 00:05 10267640                           [anon:dalvik-non moving space]
+77b8b000-97b8b000 rw-p 00000000 00:05 10267645                           [anon:dalvik-free list large object space]
+97b8b000-99b8b000 rw-p 00000000 00:05 10270989                           [anon:dalvik-data-code-cache]
+99b8b000-9bb8b000 r-xp 00000000 00:05 10270990                           [anon:dalvik-jit-code-cache]
+ebad6000-ebad7000 ---p 00000000 00:05 10269717                           [anon:dalvik-Sentinel fault page]
+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                       [anon:dalvik-LinearAlloc]
+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                       [anon:dalvik-Jit thread pool worker thread 0]
+705ff4b000-705ff4c000 ---p 00001000 00:05 10270991                       [anon:dalvik-Jit thread pool worker thread 0]
+705ff4c000-706004b000 rw-p 00002000 00:05 10270991                       [anon:dalvik-Jit thread pool worker thread 0]
+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                       [anon:dalvik-CompilerMetadata]
+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                       [anon:dalvik-CompilerMetadata]
+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                       [anon:dalvik-CompilerMetadata]
+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                       [anon:dalvik-CompilerMetadata]
+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                       [anon:dalvik-CompilerMetadata]
+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                       [anon:dalvik-CompilerMetadata]
+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                       [anon:dalvik-indirect ref table]
+7062b06000-7062bce000 rw-p 00000000 00:05 10270983                       [anon:dalvik-indirect ref table]
+7062bce000-7062dce000 rw-p 00000000 00:05 10270726                       [anon:dalvik-rb copying gc mark stack]
+7062dce000-70635ce000 rw-p 00000000 00:05 10270725                       [anon:dalvik-concurrent copying gc mark stack]
+70635ce000-7063dcf000 rw-p 00000000 00:05 10270724                       [anon:dalvik-live stack]
+7063dcf000-70645d0000 rw-p 00000000 00:05 10270723                       [anon:dalvik-allocation stack]
+70645d0000-70649d1000 rw-p 00000000 00:05 10270721                       [anon:dalvik-card table]
+70649d1000-7064ad1000 rw-p 00000000 00:05 10267648                       [anon:dalvik-large object free list space allocation info map]
+7064ad1000-7065ad1000 rw-p 00000000 00:05 10267644                       [anon:dalvik-region space live bitmap]
+7065ad1000-7065bd1000 rw-p 00000000 00:05 10267642                       [anon:dalvik-allocspace zygote / non moving space mark-bitmap 0]
+7065bd1000-7065cd1000 rw-p 00000000 00:05 10267641                       [anon:dalvik-allocspace zygote / non moving space live-bitmap 0]
+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                       [anon:dalvik-CompilerMetadata]
+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                       [anon:dalvik-indirect ref table]
+70e68af000-70e68b1000 rw-p 00000000 00:05 10282493                       [anon:dalvik-indirect ref table]
+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                       [anon:dalvik-indirect ref table]
+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                       [anon:dalvik-indirect ref table]
+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                       [anon:dalvik-classes.dex extracted in memory from /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk]
+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                       [anon:dalvik-CompilerMetadata]
+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                       [anon:dalvik-CompilerMetadata]
+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                       [anon:dalvik-CompilerMetadata]
+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                       [anon:dalvik-CompilerMetadata]
+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                       [anon:dalvik-CompilerMetadata]
+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                       [anon:dalvik-CompilerMetadata]
+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                       [anon:dalvik-CompilerMetadata]
+70eaaf0000-70eab10000 rw-p 00000000 00:05 10271005                       [anon:dalvik-CompilerMetadata]
+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                       [anon:dalvik-CompilerMetadata]
+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                       [anon:dalvik-CompilerMetadata]
+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                       [anon:dalvik-LinearAlloc]
+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                       [anon:dalvik-LinearAlloc]
+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                       [anon:dalvik-thread local mark stack]
+70eb5f6000-70eb5fa000 rw-p 00000000 00:05 10270981                       [anon:dalvik-thread local mark stack]
+70eb5fa000-70eb5fe000 rw-p 00000000 00:05 10270980                       [anon:dalvik-thread local mark stack]
+70eb5fe000-70eb602000 rw-p 00000000 00:05 10270979                       [anon:dalvik-thread local mark stack]
+70eb602000-70eb606000 rw-p 00000000 00:05 10270978                       [anon:dalvik-thread local mark stack]
+70eb606000-70eb60a000 rw-p 00000000 00:05 10270977                       [anon:dalvik-thread local mark stack]
+70eb60a000-70eb60e000 rw-p 00000000 00:05 10270976                       [anon:dalvik-thread local mark stack]
+70eb60e000-70eb612000 rw-p 00000000 00:05 10270975                       [anon:dalvik-thread local mark stack]
+70eb612000-70eb616000 rw-p 00000000 00:05 10270974                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70eb63d000-70eb641000 rw-p 00000000 00:05 10270972                       [anon:dalvik-thread local mark stack]
+70eb641000-70eb645000 rw-p 00000000 00:05 10270971                       [anon:dalvik-thread local mark stack]
+70eb645000-70eb649000 rw-p 00000000 00:05 10270970                       [anon:dalvik-thread local mark stack]
+70eb649000-70eb64d000 rw-p 00000000 00:05 10270969                       [anon:dalvik-thread local mark stack]
+70eb64d000-70eb651000 rw-p 00000000 00:05 10270968                       [anon:dalvik-thread local mark stack]
+70eb651000-70eb655000 rw-p 00000000 00:05 10270967                       [anon:dalvik-thread local mark stack]
+70eb655000-70eb659000 rw-p 00000000 00:05 10270966                       [anon:dalvik-thread local mark stack]
+70eb659000-70eb65d000 rw-p 00000000 00:05 10270965                       [anon:dalvik-thread local mark stack]
+70eb65d000-70eb661000 rw-p 00000000 00:05 10270964                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70eb6e6000-70eb6ea000 rw-p 00000000 00:05 10270962                       [anon:dalvik-thread local mark stack]
+70eb6ea000-70eb6ee000 rw-p 00000000 00:05 10270961                       [anon:dalvik-thread local mark stack]
+70eb6ee000-70eb6f2000 rw-p 00000000 00:05 10270960                       [anon:dalvik-thread local mark stack]
+70eb6f2000-70eb6f6000 rw-p 00000000 00:05 10270959                       [anon:dalvik-thread local mark stack]
+70eb6f6000-70eb6fa000 rw-p 00000000 00:05 10270958                       [anon:dalvik-thread local mark stack]
+70eb6fa000-70eb6fe000 rw-p 00000000 00:05 10270957                       [anon:dalvik-thread local mark stack]
+70eb6fe000-70eb702000 rw-p 00000000 00:05 10270956                       [anon:dalvik-thread local mark stack]
+70eb702000-70eb706000 rw-p 00000000 00:05 10270955                       [anon:dalvik-thread local mark stack]
+70eb706000-70eb70a000 rw-p 00000000 00:05 10270954                       [anon:dalvik-thread local mark stack]
+70eb70a000-70eb70e000 rw-p 00000000 00:05 10270953                       [anon:dalvik-thread local mark stack]
+70eb70e000-70eb712000 rw-p 00000000 00:05 10270952                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70eb738000-70eb73c000 rw-p 00000000 00:05 10270950                       [anon:dalvik-thread local mark stack]
+70eb73c000-70eb740000 rw-p 00000000 00:05 10270949                       [anon:dalvik-thread local mark stack]
+70eb740000-70eb744000 rw-p 00000000 00:05 10270948                       [anon:dalvik-thread local mark stack]
+70eb744000-70eb748000 rw-p 00000000 00:05 10270947                       [anon:dalvik-thread local mark stack]
+70eb748000-70eb74c000 rw-p 00000000 00:05 10270946                       [anon:dalvik-thread local mark stack]
+70eb74c000-70eb750000 rw-p 00000000 00:05 10270945                       [anon:dalvik-thread local mark stack]
+70eb750000-70eb754000 rw-p 00000000 00:05 10270944                       [anon:dalvik-thread local mark stack]
+70eb754000-70eb758000 rw-p 00000000 00:05 10270943                       [anon:dalvik-thread local mark stack]
+70eb758000-70eb75c000 rw-p 00000000 00:05 10270942                       [anon:dalvik-thread local mark stack]
+70eb75c000-70eb760000 rw-p 00000000 00:05 10270941                       [anon:dalvik-thread local mark stack]
+70eb760000-70eb764000 rw-p 00000000 00:05 10270940                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70eb76c000-70eb770000 rw-p 00000000 00:05 10270938                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70eb778000-70eb77c000 rw-p 00000000 00:05 10270936                       [anon:dalvik-thread local mark stack]
+70eb77c000-70eb780000 rw-p 00000000 00:05 10270935                       [anon:dalvik-thread local mark stack]
+70eb780000-70eb784000 rw-p 00000000 00:05 10270934                       [anon:dalvik-thread local mark stack]
+70eb784000-70eb788000 rw-p 00000000 00:05 10270933                       [anon:dalvik-thread local mark stack]
+70eb788000-70eb78c000 rw-p 00000000 00:05 10270932                       [anon:dalvik-thread local mark stack]
+70eb78c000-70eb790000 rw-p 00000000 00:05 10270931                       [anon:dalvik-thread local mark stack]
+70eb790000-70eb794000 rw-p 00000000 00:05 10270930                       [anon:dalvik-thread local mark stack]
+70eb794000-70eb798000 rw-p 00000000 00:05 10270929                       [anon:dalvik-thread local mark stack]
+70eb798000-70eb79c000 rw-p 00000000 00:05 10270928                       [anon:dalvik-thread local mark stack]
+70eb79c000-70eb7a0000 rw-p 00000000 00:05 10270927                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70eb7b6000-70eb7ba000 rw-p 00000000 00:05 10270923                       [anon:dalvik-thread local mark stack]
+70eb7ba000-70eb7be000 rw-p 00000000 00:05 10270922                       [anon:dalvik-thread local mark stack]
+70eb7be000-70eb7c2000 rw-p 00000000 00:05 10270921                       [anon:dalvik-thread local mark stack]
+70eb7c2000-70eb7c6000 rw-p 00000000 00:05 10270920                       [anon:dalvik-thread local mark stack]
+70eb7c6000-70eb7ca000 rw-p 00000000 00:05 10270919                       [anon:dalvik-thread local mark stack]
+70eb7ca000-70eb7ce000 rw-p 00000000 00:05 10270918                       [anon:dalvik-thread local mark stack]
+70eb7ce000-70eb7d2000 rw-p 00000000 00:05 10270917                       [anon:dalvik-thread local mark stack]
+70eb7d2000-70eb7d6000 rw-p 00000000 00:05 10270916                       [anon:dalvik-thread local mark stack]
+70eb7d6000-70eb7d7000 rw-p 00000000 00:00 0                              [anon:linker_alloc_small_objects]
+70eb7d7000-70eb7db000 rw-p 00000000 00:05 10270915                       [anon:dalvik-thread local mark stack]
+70eb7db000-70eb7df000 rw-p 00000000 00:05 10270914                       [anon:dalvik-thread local mark stack]
+70eb7df000-70eb7e3000 rw-p 00000000 00:05 10270913                       [anon:dalvik-thread local mark stack]
+70eb7e3000-70eb7e7000 rw-p 00000000 00:05 10270912                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70eb7ee000-70eb7f2000 rw-p 00000000 00:05 10270910                       [anon:dalvik-thread local mark stack]
+70eb7f2000-70eb7f6000 rw-p 00000000 00:05 10270909                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70eb7fc000-70eb800000 rw-p 00000000 00:05 10270907                       [anon:dalvik-thread local mark stack]
+70eb800000-70eb804000 rw-p 00000000 00:05 10270906                       [anon:dalvik-thread local mark stack]
+70eb804000-70eb808000 rw-p 00000000 00:05 10270905                       [anon:dalvik-thread local mark stack]
+70eb808000-70eb80c000 rw-p 00000000 00:05 10270904                       [anon:dalvik-thread local mark stack]
+70eb80c000-70eb810000 rw-p 00000000 00:05 10270903                       [anon:dalvik-thread local mark stack]
+70eb810000-70eb814000 rw-p 00000000 00:05 10270902                       [anon:dalvik-thread local mark stack]
+70eb814000-70eb815000 rw-p 00000000 00:00 0                              [anon:linker_alloc_vector]
+70eb815000-70eb819000 rw-p 00000000 00:05 10270901                       [anon:dalvik-thread local mark stack]
+70eb819000-70eb81d000 rw-p 00000000 00:05 10270900                       [anon:dalvik-thread local mark stack]
+70eb81d000-70eb81e000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70eb81e000-70eb822000 rw-p 00000000 00:05 10270899                       [anon:dalvik-thread local mark stack]
+70eb822000-70eb826000 rw-p 00000000 00:05 10270898                       [anon:dalvik-thread local mark stack]
+70eb826000-70eb82a000 rw-p 00000000 00:05 10270897                       [anon:dalvik-thread local mark stack]
+70eb82a000-70eb82e000 rw-p 00000000 00:05 10270896                       [anon:dalvik-thread local mark stack]
+70eb82e000-70eb832000 rw-p 00000000 00:05 10270895                       [anon:dalvik-thread local mark stack]
+70eb832000-70eb836000 rw-p 00000000 00:05 10270894                       [anon:dalvik-thread local mark stack]
+70eb836000-70eb837000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb837000-70eb83b000 rw-p 00000000 00:05 10270893                       [anon:dalvik-thread local mark stack]
+70eb83b000-70eb83f000 rw-p 00000000 00:05 10270892                       [anon:dalvik-thread local mark stack]
+70eb83f000-70eb843000 rw-p 00000000 00:05 10270891                       [anon:dalvik-thread local mark stack]
+70eb843000-70eb847000 rw-p 00000000 00:05 10270890                       [anon:dalvik-thread local mark stack]
+70eb847000-70eb84b000 rw-p 00000000 00:05 10270889                       [anon:dalvik-thread local mark stack]
+70eb84b000-70eb84f000 rw-p 00000000 00:05 10270888                       [anon:dalvik-thread local mark stack]
+70eb84f000-70eb850000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70eb850000-70eb854000 rw-p 00000000 00:05 10270887                       [anon:dalvik-thread local mark stack]
+70eb854000-70eb858000 rw-p 00000000 00:05 10270886                       [anon:dalvik-thread local mark stack]
+70eb858000-70eb85c000 rw-p 00000000 00:05 10270885                       [anon:dalvik-thread local mark stack]
+70eb85c000-70eb860000 rw-p 00000000 00:05 10270884                       [anon:dalvik-thread local mark stack]
+70eb860000-70eb864000 rw-p 00000000 00:05 10270883                       [anon:dalvik-thread local mark stack]
+70eb864000-70eb868000 rw-p 00000000 00:05 10270882                       [anon:dalvik-thread local mark stack]
+70eb868000-70eb869000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb869000-70eb86d000 rw-p 00000000 00:05 10270881                       [anon:dalvik-thread local mark stack]
+70eb86d000-70eb871000 rw-p 00000000 00:05 10270880                       [anon:dalvik-thread local mark stack]
+70eb871000-70eb875000 rw-p 00000000 00:05 10270879                       [anon:dalvik-thread local mark stack]
+70eb875000-70eb879000 rw-p 00000000 00:05 10270878                       [anon:dalvik-thread local mark stack]
+70eb879000-70eb87d000 rw-p 00000000 00:05 10270877                       [anon:dalvik-thread local mark stack]
+70eb87d000-70eb881000 rw-p 00000000 00:05 10270876                       [anon:dalvik-thread local mark stack]
+70eb881000-70eb885000 rw-p 00000000 00:05 10270875                       [anon:dalvik-thread local mark stack]
+70eb885000-70eb889000 rw-p 00000000 00:05 10270874                       [anon:dalvik-thread local mark stack]
+70eb889000-70eb88d000 rw-p 00000000 00:05 10270873                       [anon:dalvik-thread local mark stack]
+70eb88d000-70eb891000 rw-p 00000000 00:05 10270872                       [anon:dalvik-thread local mark stack]
+70eb891000-70eb892000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb892000-70eb896000 rw-p 00000000 00:05 10270871                       [anon:dalvik-thread local mark stack]
+70eb896000-70eb89a000 rw-p 00000000 00:05 10270870                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70eb8a0000-70eb8a4000 rw-p 00000000 00:05 10270868                       [anon:dalvik-thread local mark stack]
+70eb8a4000-70eb8a5000 r--p 00000000 00:00 0                              [anon:atexit handlers]
+70eb8a5000-70eb8a9000 rw-p 00000000 00:05 10270867                       [anon:dalvik-thread local mark stack]
+70eb8a9000-70eb8ad000 rw-p 00000000 00:05 10270866                       [anon:dalvik-thread local mark stack]
+70eb8ad000-70eb8b1000 rw-p 00000000 00:05 10270865                       [anon:dalvik-thread local mark stack]
+70eb8b1000-70eb8b5000 rw-p 00000000 00:05 10270864                       [anon:dalvik-thread local mark stack]
+70eb8b5000-70eb8b9000 rw-p 00000000 00:05 10270863                       [anon:dalvik-thread local mark stack]
+70eb8b9000-70eb8bd000 rw-p 00000000 00:05 10270862                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70eb8c5000-70eb8c9000 rw-p 00000000 00:05 10270860                       [anon:dalvik-thread local mark stack]
+70eb8c9000-70eb8cd000 rw-p 00000000 00:05 10270859                       [anon:dalvik-thread local mark stack]
+70eb8cd000-70eb8d1000 rw-p 00000000 00:05 10270858                       [anon:dalvik-thread local mark stack]
+70eb8d1000-70eb8d5000 rw-p 00000000 00:05 10270857                       [anon:dalvik-thread local mark stack]
+70eb8d5000-70eb8d7000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb8d7000-70eb8db000 rw-p 00000000 00:05 10270856                       [anon:dalvik-thread local mark stack]
+70eb8db000-70eb8df000 rw-p 00000000 00:05 10270855                       [anon:dalvik-thread local mark stack]
+70eb8df000-70eb8e3000 rw-p 00000000 00:05 10270854                       [anon:dalvik-thread local mark stack]
+70eb8e3000-70eb8e7000 rw-p 00000000 00:05 10270853                       [anon:dalvik-thread local mark stack]
+70eb8e7000-70eb8eb000 rw-p 00000000 00:05 10270852                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70eb8f1000-70eb8f5000 rw-p 00000000 00:05 10270850                       [anon:dalvik-thread local mark stack]
+70eb8f5000-70eb8f9000 rw-p 00000000 00:05 10270849                       [anon:dalvik-thread local mark stack]
+70eb8f9000-70eb8fd000 rw-p 00000000 00:05 10270848                       [anon:dalvik-thread local mark stack]
+70eb8fd000-70eb901000 rw-p 00000000 00:05 10270847                       [anon:dalvik-thread local mark stack]
+70eb901000-70eb905000 rw-p 00000000 00:05 10270846                       [anon:dalvik-thread local mark stack]
+70eb905000-70eb909000 rw-p 00000000 00:05 10270845                       [anon:dalvik-thread local mark stack]
+70eb909000-70eb90d000 rw-p 00000000 00:05 10270844                       [anon:dalvik-thread local mark stack]
+70eb90d000-70eb911000 rw-p 00000000 00:05 10270843                       [anon:dalvik-thread local mark stack]
+70eb911000-70eb915000 rw-p 00000000 00:05 10270842                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70eb921000-70eb925000 rw-p 00000000 00:05 10270839                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70eb92d000-70eb931000 rw-p 00000000 00:05 10270837                       [anon:dalvik-thread local mark stack]
+70eb931000-70eb935000 rw-p 00000000 00:05 10270836                       [anon:dalvik-thread local mark stack]
+70eb935000-70eb939000 rw-p 00000000 00:05 10270835                       [anon:dalvik-thread local mark stack]
+70eb939000-70eb93d000 rw-p 00000000 00:05 10270834                       [anon:dalvik-thread local mark stack]
+70eb93d000-70eb941000 rw-p 00000000 00:05 10270833                       [anon:dalvik-thread local mark stack]
+70eb941000-70eb945000 rw-p 00000000 00:05 10270832                       [anon:dalvik-thread local mark stack]
+70eb945000-70eb949000 rw-p 00000000 00:05 10270831                       [anon:dalvik-thread local mark stack]
+70eb949000-70eb94d000 rw-p 00000000 00:05 10270830                       [anon:dalvik-thread local mark stack]
+70eb94d000-70eb951000 rw-p 00000000 00:05 10270829                       [anon:dalvik-thread local mark stack]
+70eb951000-70eb991000 rw-p 00000000 00:05 10270722                       [anon:dalvik-mark stack]
+70eb991000-70eb992000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70eb992000-70eb996000 rw-p 00000000 00:05 10270828                       [anon:dalvik-thread local mark stack]
+70eb996000-70eb99a000 rw-p 00000000 00:05 10270827                       [anon:dalvik-thread local mark stack]
+70eb99a000-70eb99e000 rw-p 00000000 00:05 10270826                       [anon:dalvik-thread local mark stack]
+70eb99e000-70eb9a2000 rw-p 00000000 00:05 10270825                       [anon:dalvik-thread local mark stack]
+70eb9a2000-70eb9a4000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb9a4000-70eb9a8000 rw-p 00000000 00:05 10270824                       [anon:dalvik-thread local mark stack]
+70eb9a8000-70eb9ac000 rw-p 00000000 00:05 10270823                       [anon:dalvik-thread local mark stack]
+70eb9ac000-70eb9b0000 rw-p 00000000 00:05 10270822                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70eb9b6000-70eb9ba000 rw-p 00000000 00:05 10270820                       [anon:dalvik-thread local mark stack]
+70eb9ba000-70eb9be000 rw-p 00000000 00:05 10270819                       [anon:dalvik-thread local mark stack]
+70eb9be000-70eb9c2000 rw-p 00000000 00:05 10270818                       [anon:dalvik-thread local mark stack]
+70eb9c2000-70eb9c6000 rw-p 00000000 00:05 10270817                       [anon:dalvik-thread local mark stack]
+70eb9c6000-70eb9ca000 rw-p 00000000 00:05 10270816                       [anon:dalvik-thread local mark stack]
+70eb9ca000-70eb9ce000 rw-p 00000000 00:05 10270815                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70eb9d5000-70eb9d9000 rw-p 00000000 00:05 10270813                       [anon:dalvik-thread local mark stack]
+70eb9d9000-70eb9dd000 rw-p 00000000 00:05 10270812                       [anon:dalvik-thread local mark stack]
+70eb9dd000-70eb9e1000 rw-p 00000000 00:05 10270811                       [anon:dalvik-thread local mark stack]
+70eb9e1000-70eb9e5000 rw-p 00000000 00:05 10270810                       [anon:dalvik-thread local mark stack]
+70eb9e5000-70eb9e9000 rw-p 00000000 00:05 10270809                       [anon:dalvik-thread local mark stack]
+70eb9e9000-70eb9ed000 rw-p 00000000 00:05 10270808                       [anon:dalvik-thread local mark stack]
+70eb9ed000-70eb9f1000 rw-p 00000000 00:05 10270807                       [anon:dalvik-thread local mark stack]
+70eb9f1000-70eb9f5000 rw-p 00000000 00:05 10270806                       [anon:dalvik-thread local mark stack]
+70eb9f5000-70eb9f6000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb9f6000-70eb9f8000 rw-p 00000000 00:05 10271002                       [anon:dalvik-indirect ref table]
+70eb9f8000-70eb9fc000 rw-p 00000000 00:05 10270805                       [anon:dalvik-thread local mark stack]
+70eb9fc000-70eb9fd000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70eb9fd000-70eb9ff000 rw-p 00000000 00:05 10270999                       [anon:dalvik-indirect ref table]
+70eb9ff000-70eba00000 r--s 00000000 fc:00 983                            /system/framework/com.google.vr.platform.jar
+70eba00000-70eba04000 rw-p 00000000 00:05 10270804                       [anon:dalvik-thread local mark stack]
+70eba04000-70eba08000 rw-p 00000000 00:05 10270803                       [anon:dalvik-thread local mark stack]
+70eba08000-70eba0c000 rw-p 00000000 00:05 10270802                       [anon:dalvik-thread local mark stack]
+70eba0c000-70eba10000 rw-p 00000000 00:05 10270801                       [anon:dalvik-thread local mark stack]
+70eba10000-70eba14000 rw-p 00000000 00:05 10270800                       [anon:dalvik-thread local mark stack]
+70eba14000-70eba18000 rw-p 00000000 00:05 10270799                       [anon:dalvik-thread local mark stack]
+70eba18000-70eba1c000 rw-p 00000000 00:05 10270798                       [anon:dalvik-thread local mark stack]
+70eba1c000-70eba20000 rw-p 00000000 00:05 10270797                       [anon:dalvik-thread local mark stack]
+70eba20000-70eba24000 rw-p 00000000 00:05 10270796                       [anon:dalvik-thread local mark stack]
+70eba24000-70eba28000 rw-p 00000000 00:05 10270795                       [anon:dalvik-thread local mark stack]
+70eba28000-70eba2c000 rw-p 00000000 00:05 10270794                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70eba34000-70eba38000 rw-p 00000000 00:05 10270792                       [anon:dalvik-thread local mark stack]
+70eba38000-70eba3c000 rw-p 00000000 00:05 10270791                       [anon:dalvik-thread local mark stack]
+70eba3c000-70eba40000 rw-p 00000000 00:05 10270790                       [anon:dalvik-thread local mark stack]
+70eba40000-70eba44000 rw-p 00000000 00:05 10270789                       [anon:dalvik-thread local mark stack]
+70eba44000-70eba48000 rw-p 00000000 00:05 10270788                       [anon:dalvik-thread local mark stack]
+70eba48000-70eba4c000 rw-p 00000000 00:05 10270787                       [anon:dalvik-thread local mark stack]
+70eba4c000-70eba50000 rw-p 00000000 00:05 10270786                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70eba57000-70eba5b000 rw-p 00000000 00:05 10270784                       [anon:dalvik-thread local mark stack]
+70eba5b000-70eba5f000 rw-p 00000000 00:05 10270783                       [anon:dalvik-thread local mark stack]
+70eba5f000-70eba63000 rw-p 00000000 00:05 10270782                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70eba69000-70eba6d000 rw-p 00000000 00:05 10270780                       [anon:dalvik-thread local mark stack]
+70eba6d000-70eba71000 rw-p 00000000 00:05 10270779                       [anon:dalvik-thread local mark stack]
+70eba71000-70eba75000 rw-p 00000000 00:05 10270778                       [anon:dalvik-thread local mark stack]
+70eba75000-70eba95000 rw-p 00000000 00:05 10267647                       [anon:dalvik-large marked objects]
+70eba95000-70ebab5000 rw-p 00000000 00:05 10267646                       [anon:dalvik-large live objects]
+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                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-indirect ref table]
+70ebadf000-70ebae3000 rw-p 00000000 00:05 10270776                       [anon:dalvik-thread local mark stack]
+70ebae3000-70ebae7000 rw-p 00000000 00:05 10270775                       [anon:dalvik-thread local mark stack]
+70ebae7000-70ebaeb000 rw-p 00000000 00:05 10270774                       [anon:dalvik-thread local mark stack]
+70ebaeb000-70ebaef000 rw-p 00000000 00:05 10270773                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-indirect ref table]
+70ebb13000-70ebb17000 rw-p 00000000 00:05 10270772                       [anon:dalvik-thread local mark stack]
+70ebb17000-70ebb19000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebb19000-70ebb1d000 rw-p 00000000 00:05 10270771                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70ebba2000-70ebba6000 rw-p 00000000 00:05 10270769                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70ebbac000-70ebbb0000 rw-p 00000000 00:05 10270767                       [anon:dalvik-thread local mark stack]
+70ebbb0000-70ebbb4000 rw-p 00000000 00:05 10270766                       [anon:dalvik-thread local mark stack]
+70ebbb4000-70ebbb8000 rw-p 00000000 00:05 10270765                       [anon:dalvik-thread local mark stack]
+70ebbb8000-70ebbbc000 rw-p 00000000 00:05 10270764                       [anon:dalvik-thread local mark stack]
+70ebbbc000-70ebbc0000 rw-p 00000000 00:05 10270763                       [anon:dalvik-thread local mark stack]
+70ebbc0000-70ebbc4000 rw-p 00000000 00:05 10270762                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-indirect ref table]
+70ebbe8000-70ebbec000 rw-p 00000000 00:05 10270761                       [anon:dalvik-thread local mark stack]
+70ebbec000-70ebbf0000 rw-p 00000000 00:05 10270760                       [anon:dalvik-thread local mark stack]
+70ebbf0000-70ebbf4000 rw-p 00000000 00:05 10270759                       [anon:dalvik-thread local mark stack]
+70ebbf4000-70ebbf8000 rw-p 00000000 00:05 10270758                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-indirect ref table]
+70ebbfc000-70ebc00000 rw-p 00000000 00:05 10270757                       [anon:dalvik-thread local mark stack]
+70ebc00000-70ebc04000 rw-p 00000000 00:05 10270756                       [anon:dalvik-thread local mark stack]
+70ebc04000-70ebc08000 rw-p 00000000 00:05 10270755                       [anon:dalvik-thread local mark stack]
+70ebc08000-70ebc0c000 rw-p 00000000 00:05 10270754                       [anon:dalvik-thread local mark stack]
+70ebc0c000-70ebc10000 rw-p 00000000 00:05 10270753                       [anon:dalvik-thread local mark stack]
+70ebc10000-70ebc14000 rw-p 00000000 00:05 10270752                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70ebc1a000-70ebc1e000 rw-p 00000000 00:05 10270750                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-indirect ref table]
+70ebc42000-70ebc46000 rw-p 00000000 00:05 10270749                       [anon:dalvik-thread local mark stack]
+70ebc46000-70ebc4a000 rw-p 00000000 00:05 10270748                       [anon:dalvik-thread local mark stack]
+70ebc4a000-70ebc4e000 rw-p 00000000 00:05 10270747                       [anon:dalvik-thread local mark stack]
+70ebc4e000-70ebc4f000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70ebc4f000-70ebc51000 rw-p 00000000 00:05 10269718                       [anon:dalvik-indirect ref table]
+70ebc51000-70ebc55000 rw-p 00000000 00:05 10270746                       [anon:dalvik-thread local mark stack]
+70ebc55000-70ebc59000 rw-p 00000000 00:05 10270745                       [anon:dalvik-thread local mark stack]
+70ebc59000-70ebc5d000 rw-p 00000000 00:05 10270744                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70ebc85000-70ebc89000 rw-p 00000000 00:05 10270742                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70ebc91000-70ebc95000 rw-p 00000000 00:05 10270740                       [anon:dalvik-thread local mark stack]
+70ebc95000-70ebc99000 rw-p 00000000 00:05 10270739                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70ebca0000-70ebca4000 rw-p 00000000 00:05 10270737                       [anon:dalvik-thread local mark stack]
+70ebca4000-70ebca8000 rw-p 00000000 00:05 10270736                       [anon:dalvik-thread local mark stack]
+70ebca8000-70ebcac000 rw-p 00000000 00:05 10270735                       [anon:dalvik-thread local mark stack]
+70ebcac000-70ebcb0000 rw-p 00000000 00:05 10270734                       [anon:dalvik-thread local mark stack]
+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                       [anon:dalvik-thread local mark stack]
+70ebcd5000-70ebcd9000 rw-p 00000000 00:05 10270732                       [anon:dalvik-thread local mark stack]
+70ebcd9000-70ebcdd000 rw-p 00000000 00:05 10270731                       [anon:dalvik-thread local mark stack]
+70ebcdd000-70ebce1000 rw-p 00000000 00:05 10270730                       [anon:dalvik-thread local mark stack]
+70ebce1000-70ebce5000 rw-p 00000000 00:05 10270729                       [anon:dalvik-thread local mark stack]
+70ebce5000-70ebce9000 rw-p 00000000 00:05 10270728                       [anon:dalvik-thread local mark stack]
+70ebce9000-70ebcea000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebcea000-70ebcec000 rw-p 00000000 00:05 10270987                       [anon:dalvik-indirect ref table]
+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                       [anon:dalvik-thread local mark stack]
+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/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 b894656..2ec4754 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -3,13 +3,14 @@
 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"],
@@ -30,8 +31,8 @@
     name: "simg2img",
     host_supported: true,
     srcs: [
-        "simg2img.c",
-        "sparse_crc32.c",
+        "simg2img.cpp",
+        "sparse_crc32.cpp",
     ],
     static_libs: [
         "libsparse",
@@ -45,7 +46,7 @@
 cc_binary {
     name: "img2simg",
     host_supported: true,
-    srcs: ["img2simg.c"],
+    srcs: ["img2simg.cpp"],
     static_libs: [
         "libsparse",
         "libz",
@@ -57,7 +58,7 @@
 
 cc_binary_host {
     name: "append2simg",
-    srcs: ["append2simg.c"],
+    srcs: ["append2simg.cpp"],
     static_libs: [
         "libsparse",
         "libz",
@@ -66,3 +67,18 @@
 
     cflags: ["-Werror"],
 }
+
+python_binary_host {
+    name: "simg_dump.py",
+    main: "simg_dump.py",
+    srcs: ["simg_dump.py"],
+    version: {
+        py2: {
+            embedded_launcher: true,
+            enabled: true,
+        },
+        py3: {
+            enabled: false,
+        },
+    },
+}
diff --git a/libsparse/Android.mk b/libsparse/Android.mk
deleted file mode 100644
index 05e68bc..0000000
--- a/libsparse/Android.mk
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2010 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := simg_dump.py
-LOCAL_SRC_FILES := simg_dump.py
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_IS_HOST_MODULE := true
-LOCAL_CFLAGS := -Werror
-include $(BUILD_PREBUILT)
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..5b8179f
--- /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/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..24c6379
--- /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 = 0;
+  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 4379635..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 <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <string>
 #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
index d58f294..f5ee1da 100644
--- a/libstats/Android.bp
+++ b/libstats/Android.bp
@@ -17,12 +17,13 @@
 // ==========================================================
 // Native library to write stats log to statsd socket
 // ==========================================================
-cc_library_static {
+cc_library {
     name: "libstatssocket",
     srcs: [
         "stats_event_list.c",
         "statsd_writer.c",
     ],
+    host_supported: true,
     cflags: [
         "-Wall",
         "-Werror",
@@ -32,6 +33,7 @@
     ],
     export_include_dirs: ["include"],
     shared_libs: [
+        "libcutils",
         "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
index 5d174ae..845a197 100644
--- a/libstats/include/stats_event_list.h
+++ b/libstats/include/stats_event_list.h
@@ -18,12 +18,17 @@
 #define ANDROID_STATS_LOG_STATS_EVENT_LIST_H
 
 #include <log/log_event_list.h>
+#include <sys/uio.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);
+void note_log_drop(int error, int atom_tag);
+void stats_log_close();
+int android_log_write_char_array(android_log_context ctx, const char* value, size_t len);
+extern int (*write_to_statsd)(struct iovec* vec, size_t nr);
 
 #ifdef __cplusplus
 }
@@ -49,10 +54,6 @@
     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() {
@@ -242,8 +243,13 @@
         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); }
+    bool AppendCharArray(const char* value, size_t len) {
+        int retval = android_log_write_char_array(ctx, value, len);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
 };
 
 #endif
diff --git a/libstats/stats_event_list.c b/libstats/stats_event_list.c
index 966bb08..ae12cbe 100644
--- a/libstats/stats_event_list.c
+++ b/libstats/stats_event_list.c
@@ -17,6 +17,7 @@
 #include "include/stats_event_list.h"
 
 #include <string.h>
+#include <sys/time.h>
 #include "statsd_writer.h"
 
 #define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
@@ -40,7 +41,7 @@
 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;
+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.
@@ -119,6 +120,19 @@
     return retValue;
 }
 
+void note_log_drop(int error, int tag) {
+    statsdLoggerWrite.noteDrop(error, tag);
+}
+
+void stats_log_close() {
+    statsd_writer_init_lock();
+    write_to_statsd = __write_to_statsd_init;
+    if (statsdLoggerWrite.close) {
+        (*statsdLoggerWrite.close)();
+    }
+    statsd_writer_init_unlock();
+}
+
 /* log_init_lock assumed */
 static int __write_to_statsd_initialize_locked() {
     if (!statsdLoggerWrite.open || ((*statsdLoggerWrite.open)() < 0)) {
@@ -131,7 +145,7 @@
 }
 
 static int __write_to_stats_daemon(struct iovec* vec, size_t nr) {
-    int ret, save_errno;
+    int save_errno;
     struct timespec ts;
     size_t len, i;
 
@@ -143,16 +157,16 @@
     }
 
     save_errno = errno;
+#if defined(__ANDROID__)
     clock_gettime(CLOCK_REALTIME, &ts);
+#else
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    ts.tv_sec = tv.tv_sec;
+    ts.tv_nsec = tv.tv_usec * 1000;
+#endif
 
-    ret = 0;
-
-    ssize_t retval;
-    retval = (*statsdLoggerWrite.write)(&ts, vec, nr);
-    if (ret >= 0) {
-        ret = retval;
-    }
-
+    int ret = (int)(*statsdLoggerWrite.write)(&ts, vec, nr);
     errno = save_errno;
     return ret;
 }
@@ -178,4 +192,48 @@
     ret = write_to_statsd(vec, nr);
     errno = save_errno;
     return ret;
-}
\ No newline at end of file
+}
+
+static inline void copy4LE(uint8_t* buf, uint32_t val) {
+    buf[0] = val & 0xFF;
+    buf[1] = (val >> 8) & 0xFF;
+    buf[2] = (val >> 16) & 0xFF;
+    buf[3] = (val >> 24) & 0xFF;
+}
+
+// Note: this function differs from android_log_write_string8_len in that the length passed in
+// should be treated as actual length and not max length.
+int android_log_write_char_array(android_log_context ctx, const char* value, size_t actual_len) {
+    size_t needed;
+    ssize_t len = actual_len;
+    android_log_context_internal* context;
+
+    context = (android_log_context_internal*)ctx;
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+    if (context->overflow) {
+        return -EIO;
+    }
+    if (!value) {
+        value = "";
+        len = 0;
+    }
+    needed = sizeof(uint8_t) + sizeof(int32_t) + len;
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        /* Truncate string for delivery */
+        len = MAX_EVENT_PAYLOAD - context->pos - 1 - sizeof(int32_t);
+        if (len <= 0) {
+            context->overflow = true;
+            return -EIO;
+        }
+    }
+    context->count[context->list_nest_depth]++;
+    context->storage[context->pos + 0] = EVENT_TYPE_STRING;
+    copy4LE(&context->storage[context->pos + 1], len);
+    if (len) {
+        memcpy(&context->storage[context->pos + 5], value, len);
+    }
+    context->pos += needed;
+    return len;
+}
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
index 9953bba..b778f92 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/statsd_writer.c
@@ -15,8 +15,9 @@
  */
 #include "statsd_writer.h"
 
+#include <cutils/fs.h>
 #include <cutils/sockets.h>
-#include <endian.h>
+#include <cutils/threads.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -30,6 +31,7 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/uio.h>
 #include <sys/un.h>
 #include <time.h>
 #include <unistd.h>
@@ -37,7 +39,26 @@
 /* branchless on many architectures. */
 #define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
 
+#ifndef htole32
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htole32(x) (x)
+#else
+#define htole32(x) __bswap_32(x)
+#endif
+#endif
+
+#ifndef htole64
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htole64(x) (x)
+#else
+#define htole64(x) __bswap_64(x)
+#endif
+#endif
+
 static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
+static atomic_int dropped = 0;
+static atomic_int log_error = 0;
+static atomic_int atom_tag = 0;
 
 void statsd_writer_init_lock() {
     /*
@@ -59,14 +80,16 @@
 static int statsdOpen();
 static void statsdClose();
 static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
+static void statsdNoteDrop();
 
 struct android_log_transport_write statsdLoggerWrite = {
-    .name = "statsd",
-    .sock = -EBADF,
-    .available = statsdAvailable,
-    .open = statsdOpen,
-    .close = statsdClose,
-    .write = statsdWrite,
+        .name = "statsd",
+        .sock = -EBADF,
+        .available = statsdAvailable,
+        .open = statsdOpen,
+        .close = statsdClose,
+        .write = statsdWrite,
+        .noteDrop = statsdNoteDrop,
 };
 
 /* log_init_lock assumed */
@@ -75,7 +98,14 @@
 
     i = atomic_load(&statsdLoggerWrite.sock);
     if (i < 0) {
-        int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+        int flags = SOCK_DGRAM;
+#ifdef SOCK_CLOEXEC
+        flags |= SOCK_CLOEXEC;
+#endif
+#ifdef SOCK_NONBLOCK
+        flags |= SOCK_NONBLOCK;
+#endif
+        int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, flags, 0));
         if (sock < 0) {
             ret = -errno;
         } else {
@@ -131,6 +161,12 @@
     return 1;
 }
 
+static void statsdNoteDrop(int error, int tag) {
+    atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+    atomic_exchange_explicit(&log_error, error, memory_order_relaxed);
+    atomic_exchange_explicit(&atom_tag, tag, memory_order_relaxed);
+}
+
 static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
     ssize_t ret;
     int sock;
@@ -138,7 +174,6 @@
     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) {
@@ -178,11 +213,17 @@
     if (sock >= 0) {
         int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
         if (snapshot) {
-            android_log_event_int_t buffer;
+            android_log_event_long_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);
+            // store the last log error in the tag field. This tag field is not used by statsd.
+            buffer.header.tag = htole32(atomic_load(&log_error));
+            buffer.payload.type = EVENT_TYPE_LONG;
+            // format:
+            // |atom_tag|dropped_count|
+            int64_t composed_long = atomic_load(&atom_tag);
+            // Send 2 int32's via an int64.
+            composed_long = ((composed_long << 32) | ((int64_t)snapshot));
+            buffer.payload.data = htole64(composed_long);
 
             newVec[headerLength].iov_base = &buffer;
             newVec[headerLength].iov_len = sizeof(buffer);
@@ -252,8 +293,6 @@
 
     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
index 82e14e0..fe2d37c 100644
--- a/libstats/statsd_writer.h
+++ b/libstats/statsd_writer.h
@@ -38,6 +38,8 @@
     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);
+    /* note one log drop */
+    void (*noteDrop)(int error, int tag);
 };
 
 #endif  // ANDROID_STATS_LOG_STATS_WRITER_H
diff --git a/libsuspend/Android.bp b/libsuspend/Android.bp
index b3e36c2..c5f1f5e 100644
--- a/libsuspend/Android.bp
+++ b/libsuspend/Android.bp
@@ -2,11 +2,6 @@
 
 cc_library {
     name: "libsuspend",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
-
     srcs: [
         "autosuspend.c",
         "autosuspend_wakeup_count.cpp",
diff --git a/libsync/Android.bp b/libsync/Android.bp
index 3fae5e6..c996e1b 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -20,8 +20,10 @@
     cflags: ["-Werror"],
 }
 
-cc_library_shared {
+cc_library {
     name: "libsync",
+    recovery_available: true,
+    native_bridge_supported: true,
     defaults: ["libsync_defaults"],
 }
 
@@ -31,21 +33,6 @@
     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.
-cc_library_static {
-    name: "libsync_recovery",
-    defaults: ["libsync_defaults"],
-}
-
-cc_test {
-    name: "sync_test",
-    defaults: ["libsync_defaults"],
-    gtest: false,
-    srcs: ["sync_test.c"],
-}
-
 cc_test {
     name: "sync-unit-tests",
     shared_libs: ["libsync"],
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
index 68f74a0..32bb878 100644
--- a/libsync/include/android/sync.h
+++ b/libsync/include/android/sync.h
@@ -41,28 +41,8 @@
 
 __BEGIN_DECLS
 
-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];
-};
-
 /* timeout in msecs */
 int sync_wait(int fd, int timeout);
-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
 
diff --git a/libsync/include/ndk/sync.h b/libsync/include/ndk/sync.h
index 3c55783..2a59e35 100644
--- a/libsync/include/ndk/sync.h
+++ b/libsync/include/ndk/sync.h
@@ -14,16 +14,26 @@
  *  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__ >= __ANDROID_API_O__
+#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
@@ -53,21 +63,27 @@
  *
  * 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);
+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);
+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
@@ -78,11 +94,17 @@
 #pragma GCC diagnostic pop
 }
 
-/** Free a struct sync_file_info structure */
-void sync_file_info_free(struct sync_file_info *info);
+/**
+ * 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__ >= __ANDROID_API_O__
+#endif /* __ANDROID_API__ >= 26 */
 
 __END_DECLS
 
 #endif /* ANDROID_SYNC_H */
+
+/** @} */
diff --git a/libsync/sync.c b/libsync/sync.c
index 6b187fa..b8c48c7 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -30,6 +30,29 @@
 
 #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 {
diff --git a/libsync/sync_test.c b/libsync/sync_test.c
deleted file mode 100644
index f1ffdcf..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 <android/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 0fb86d6..011b09d 100644
--- a/libsync/tests/sync_test.cpp
+++ b/libsync/tests/sync_test.cpp
@@ -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.
diff --git a/libsystem/Android.bp b/libsystem/Android.bp
index 82bf1bc..b265b61 100644
--- a/libsystem/Android.bp
+++ b/libsystem/Android.bp
@@ -1,7 +1,9 @@
 cc_library_headers {
     name: "libsystem_headers",
     vendor_available: true,
+    recovery_available: true,
     host_supported: true,
+    native_bridge_supported: true,
     export_include_dirs: ["include"],
 
     target: {
diff --git a/libsystem/OWNERS b/libsystem/OWNERS
index aeb160c..fdea804 100644
--- a/libsystem/OWNERS
+++ b/libsystem/OWNERS
@@ -1,2 +1,9 @@
-jessehall@google.com
-olv@google.com
+# graphics/composer
+adyabr@google.com
+lpy@google.com
+marissaw@google.com
+stoza@google.com
+vhau@google.com
+
+# camera
+etalvala@google.com
diff --git a/libsystem/include/system/camera.h b/libsystem/include/system/camera.h
index 7d79673..2ca90c3 100644
--- a/libsystem/include/system/camera.h
+++ b/libsystem/include/system/camera.h
@@ -158,8 +158,8 @@
      *
      * When any camera method returns error, the client can use ping command
      * to see if the camera has been taken away by other clients. If the result
-     * is NO_ERROR, it means the camera hardware is not released. If the result
-     * is not NO_ERROR, the camera has been released and the existing client
+     * is OK, it means the camera hardware is not released. If the result
+     * is not OK, the camera has been released and the existing client
      * can silently finish itself or show a dialog.
      */
     CAMERA_CMD_PING = 9,
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 3a12292..ccda5d1 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
     name: "libsysutils",
     vendor_available: true,
     vndk: {
@@ -26,4 +26,30 @@
     ],
 
     export_include_dirs: ["include"],
+
+    tidy: true,
+    tidy_checks: [
+        "-*",
+        "cert-*",
+        "clang-analyzer-security*",
+        "android-*",
+    ],
+    tidy_checks_as_errors: [
+        "cert-*",
+        "clang-analyzer-security*",
+        "android-*",
+    ],
+}
+
+cc_test {
+    name: "libsysutils_tests",
+    test_suites: ["device-tests"],
+    srcs: [
+        "src/SocketListener_test.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libsysutils",
+    ],
 }
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/FrameworkCommand.h b/libsysutils/include/sysutils/FrameworkCommand.h
index 3e6264b..db17ba7 100644
--- a/libsysutils/include/sysutils/FrameworkCommand.h
+++ b/libsysutils/include/sysutils/FrameworkCommand.h
@@ -16,8 +16,6 @@
 #ifndef __FRAMEWORK_CMD_HANDLER_H
 #define __FRAMEWORK_CMD_HANDLER_H
 
-#include "List.h"
-
 class SocketClient;
 
 class FrameworkCommand { 
@@ -31,8 +29,7 @@
 
     virtual int runCommand(SocketClient *c, int argc, char **argv) = 0;
 
-    const char *getCommand() { return mCommand; }
+    const char* getCommand() const { return mCommand; }
 };
 
-typedef android::sysutils::List<FrameworkCommand *> FrameworkCommandCollection;
 #endif
diff --git a/libsysutils/include/sysutils/FrameworkListener.h b/libsysutils/include/sysutils/FrameworkListener.h
index 2137069..93d99c4 100644
--- a/libsysutils/include/sysutils/FrameworkListener.h
+++ b/libsysutils/include/sysutils/FrameworkListener.h
@@ -17,8 +17,10 @@
 #define _FRAMEWORKSOCKETLISTENER_H
 
 #include "SocketListener.h"
-#include "FrameworkCommand.h"
 
+#include <vector>
+
+class FrameworkCommand;
 class SocketClient;
 
 class FrameworkListener : public SocketListener {
@@ -31,20 +33,20 @@
 private:
     int mCommandCount;
     bool mWithSeq;
-    FrameworkCommandCollection *mCommands;
+    std::vector<FrameworkCommand*> mCommands;
     bool mSkipToNextNullByte;
 
 public:
     FrameworkListener(const char *socketName);
     FrameworkListener(const char *socketName, bool withSeq);
     FrameworkListener(int sock);
-    virtual ~FrameworkListener() {}
+    ~FrameworkListener() override {}
 
-protected:
+  protected:
     void registerCmd(FrameworkCommand *cmd);
-    virtual bool onDataAvailable(SocketClient *c);
+    bool onDataAvailable(SocketClient* c) override;
 
-private:
+  private:
     void dispatchCommand(SocketClient *c, char *data);
     void init(const char *socketName, bool withSeq);
 };
diff --git a/libsysutils/include/sysutils/List.h b/libsysutils/include/sysutils/List.h
deleted file mode 100644
index 31f7b37..0000000
--- a/libsysutils/include/sysutils/List.h
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * 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.
- */
-
-//
-// Templated list class.  Normally we'd use STL, but we don't have that.
-// This class mimics STL's interfaces.
-//
-// Objects are copied into the list with the '=' operator or with copy-
-// construction, so if the compiler's auto-generated versions won't work for
-// you, define your own.
-//
-// The only class you want to use from here is "List".
-//
-#ifndef _SYSUTILS_LIST_H
-#define _SYSUTILS_LIST_H
-
-#include <stddef.h>
-#include <stdint.h>
-
-namespace android {
-namespace sysutils {
-
-/*
- * Doubly-linked list.  Instantiate with "List<MyClass> myList".
- *
- * Objects added to the list are copied using the assignment operator,
- * so this must be defined.
- */
-template<typename T> 
-class List 
-{
-protected:
-    /*
-     * One element in the list.
-     */
-    class _Node {
-    public:
-        explicit _Node(const T& val) : mVal(val) {}
-        ~_Node() {}
-        inline T& getRef() { return mVal; }
-        inline const T& getRef() const { return mVal; }
-        inline _Node* getPrev() const { return mpPrev; }
-        inline _Node* getNext() const { return mpNext; }
-        inline void setVal(const T& val) { mVal = val; }
-        inline void setPrev(_Node* ptr) { mpPrev = ptr; }
-        inline void setNext(_Node* ptr) { mpNext = ptr; }
-    private:
-        friend class List;
-        friend class _ListIterator;
-        T           mVal;
-        _Node*      mpPrev;
-        _Node*      mpNext;
-    };
-
-    /*
-     * Iterator for walking through the list.
-     */
-    
-    template <typename TYPE>
-    struct CONST_ITERATOR {
-        typedef _Node const * NodePtr;
-        typedef const TYPE Type;
-    };
-    
-    template <typename TYPE>
-    struct NON_CONST_ITERATOR {
-        typedef _Node* NodePtr;
-        typedef TYPE Type;
-    };
-    
-    template<
-        typename U,
-        template <class> class Constness
-    > 
-    class _ListIterator {
-        typedef _ListIterator<U, Constness>     _Iter;
-        typedef typename Constness<U>::NodePtr  _NodePtr;
-        typedef typename Constness<U>::Type     _Type;
-
-        explicit _ListIterator(_NodePtr ptr) : mpNode(ptr) {}
-
-    public:
-        _ListIterator() {}
-        _ListIterator(const _Iter& rhs) : mpNode(rhs.mpNode) {}
-        ~_ListIterator() {}
-        
-        // this will handle conversions from iterator to const_iterator
-        // (and also all convertible iterators)
-        // Here, in this implementation, the iterators can be converted
-        // if the nodes can be converted
-        template<typename V> explicit 
-        _ListIterator(const V& rhs) : mpNode(rhs.mpNode) {}
-        
-
-        /*
-         * Dereference operator.  Used to get at the juicy insides.
-         */
-        _Type& operator*() const { return mpNode->getRef(); }
-        _Type* operator->() const { return &(mpNode->getRef()); }
-
-        /*
-         * Iterator comparison.
-         */
-        inline bool operator==(const _Iter& right) const { 
-            return mpNode == right.mpNode; }
-        
-        inline bool operator!=(const _Iter& right) const { 
-            return mpNode != right.mpNode; }
-
-        /*
-         * handle comparisons between iterator and const_iterator
-         */
-        template<typename OTHER>
-        inline bool operator==(const OTHER& right) const { 
-            return mpNode == right.mpNode; }
-        
-        template<typename OTHER>
-        inline bool operator!=(const OTHER& right) const { 
-            return mpNode != right.mpNode; }
-
-        /*
-         * Incr/decr, used to move through the list.
-         */
-        inline _Iter& operator++() {     // pre-increment
-            mpNode = mpNode->getNext();
-            return *this;
-        }
-        const _Iter operator++(int) {    // post-increment
-            _Iter tmp(*this);
-            mpNode = mpNode->getNext();
-            return tmp;
-        }
-        inline _Iter& operator--() {     // pre-increment
-            mpNode = mpNode->getPrev();
-            return *this;
-        }
-        const _Iter operator--(int) {   // post-increment
-            _Iter tmp(*this);
-            mpNode = mpNode->getPrev();
-            return tmp;
-        }
-
-        inline _NodePtr getNode() const { return mpNode; }
-
-        _NodePtr mpNode;    /* should be private, but older gcc fails */
-    private:
-        friend class List;
-    };
-
-public:
-    List() {
-        prep();
-    }
-    List(const List<T>& src) {      // copy-constructor
-        prep();
-        insert(begin(), src.begin(), src.end());
-    }
-    virtual ~List() {
-        clear();
-        delete[] (unsigned char*) mpMiddle;
-    }
-
-    typedef _ListIterator<T, NON_CONST_ITERATOR> iterator;
-    typedef _ListIterator<T, CONST_ITERATOR> const_iterator;
-
-    List<T>& operator=(const List<T>& right);
-
-    /* returns true if the list is empty */
-    inline bool empty() const { return mpMiddle->getNext() == mpMiddle; }
-
-    /* return #of elements in list */
-    size_t size() const {
-        return size_t(distance(begin(), end()));
-    }
-
-    /*
-     * Return the first element or one past the last element.  The
-     * _Node* we're returning is converted to an "iterator" by a
-     * constructor in _ListIterator.
-     */
-    inline iterator begin() { 
-        return iterator(mpMiddle->getNext()); 
-    }
-    inline const_iterator begin() const { 
-        return const_iterator(const_cast<_Node const*>(mpMiddle->getNext())); 
-    }
-    inline iterator end() { 
-        return iterator(mpMiddle); 
-    }
-    inline const_iterator end() const { 
-        return const_iterator(const_cast<_Node const*>(mpMiddle)); 
-    }
-
-    /* add the object to the head or tail of the list */
-    void push_front(const T& val) { insert(begin(), val); }
-    void push_back(const T& val) { insert(end(), val); }
-
-    /* insert before the current node; returns iterator at new node */
-    iterator insert(iterator posn, const T& val) 
-    {
-        _Node* newNode = new _Node(val);        // alloc & copy-construct
-        newNode->setNext(posn.getNode());
-        newNode->setPrev(posn.getNode()->getPrev());
-        posn.getNode()->getPrev()->setNext(newNode);
-        posn.getNode()->setPrev(newNode);
-        return iterator(newNode);
-    }
-
-    /* insert a range of elements before the current node */
-    void insert(iterator posn, const_iterator first, const_iterator last) {
-        for ( ; first != last; ++first)
-            insert(posn, *first);
-    }
-
-    /* remove one entry; returns iterator at next node */
-    iterator erase(iterator posn) {
-        _Node* pNext = posn.getNode()->getNext();
-        _Node* pPrev = posn.getNode()->getPrev();
-        pPrev->setNext(pNext);
-        pNext->setPrev(pPrev);
-        delete posn.getNode();
-        return iterator(pNext);
-    }
-
-    /* remove a range of elements */
-    iterator erase(iterator first, iterator last) {
-        while (first != last)
-            erase(first++);     // don't erase than incr later!
-        return iterator(last);
-    }
-
-    /* remove all contents of the list */
-    void clear() {
-        _Node* pCurrent = mpMiddle->getNext();
-        _Node* pNext;
-
-        while (pCurrent != mpMiddle) {
-            pNext = pCurrent->getNext();
-            delete pCurrent;
-            pCurrent = pNext;
-        }
-        mpMiddle->setPrev(mpMiddle);
-        mpMiddle->setNext(mpMiddle);
-    }
-
-    /*
-     * Measure the distance between two iterators.  On exist, "first"
-     * will be equal to "last".  The iterators must refer to the same
-     * list.
-     *
-     * FIXME: This is actually a generic iterator function. It should be a 
-     * template function at the top-level with specializations for things like
-     * vector<>, which can just do pointer math). Here we limit it to
-     * _ListIterator of the same type but different constness.
-     */
-    template<
-        typename U,
-        template <class> class CL,
-        template <class> class CR
-    > 
-    ptrdiff_t distance(
-            _ListIterator<U, CL> first, _ListIterator<U, CR> last) const 
-    {
-        ptrdiff_t count = 0;
-        while (first != last) {
-            ++first;
-            ++count;
-        }
-        return count;
-    }
-
-private:
-    /*
-     * I want a _Node but don't need it to hold valid data.  More
-     * to the point, I don't want T's constructor to fire, since it
-     * might have side-effects or require arguments.  So, we do this
-     * slightly uncouth storage alloc.
-     */
-    void prep() {
-        mpMiddle = (_Node*) new unsigned char[sizeof(_Node)];
-        mpMiddle->setPrev(mpMiddle);
-        mpMiddle->setNext(mpMiddle);
-    }
-
-    /*
-     * This node plays the role of "pointer to head" and "pointer to tail".
-     * It sits in the middle of a circular list of nodes.  The iterator
-     * runs around the circle until it encounters this one.
-     */
-    _Node*      mpMiddle;
-};
-
-/*
- * Assignment operator.
- *
- * The simplest way to do this would be to clear out the target list and
- * fill it with the source.  However, we can speed things along by
- * re-using existing elements.
- */
-template<class T>
-List<T>& List<T>::operator=(const List<T>& right)
-{
-    if (this == &right)
-        return *this;       // self-assignment
-    iterator firstDst = begin();
-    iterator lastDst = end();
-    const_iterator firstSrc = right.begin();
-    const_iterator lastSrc = right.end();
-    while (firstSrc != lastSrc && firstDst != lastDst)
-        *firstDst++ = *firstSrc++;
-    if (firstSrc == lastSrc)        // ran out of elements in source?
-        erase(firstDst, lastDst);   // yes, erase any extras
-    else
-        insert(lastDst, firstSrc, lastSrc);     // copy remaining over
-    return *this;
-}
-
-}; // namespace sysutils
-}; // namespace android
-
-#endif // _SYSUTILS_LIST_H
diff --git a/libsysutils/include/sysutils/OWNERS b/libsysutils/include/sysutils/OWNERS
new file mode 100644
index 0000000..4c99361
--- /dev/null
+++ b/libsysutils/include/sysutils/OWNERS
@@ -0,0 +1,2 @@
+include ../../src/OWNERS
+
diff --git a/libsysutils/include/sysutils/SocketClient.h b/libsysutils/include/sysutils/SocketClient.h
index 1004f06..c657526 100644
--- a/libsysutils/include/sysutils/SocketClient.h
+++ b/libsysutils/include/sysutils/SocketClient.h
@@ -1,8 +1,6 @@
 #ifndef _SOCKET_CLIENT_H
 #define _SOCKET_CLIENT_H
 
-#include "List.h"
-
 #include <pthread.h>
 #include <cutils/atomic.h>
 #include <sys/types.h>
@@ -35,7 +33,7 @@
     SocketClient(int sock, bool owned, bool useCmdNum);
     virtual ~SocketClient();
 
-    int getSocket() { return mSocket; }
+    int getSocket() const { return mSocket; }
     pid_t getPid() const { return mPid; }
     uid_t getUid() const { return mUid; }
     gid_t getGid() const { return mGid; }
@@ -84,5 +82,4 @@
     int sendDataLockedv(struct iovec *iov, int iovcnt);
 };
 
-typedef android::sysutils::List<SocketClient *> SocketClientCollection;
 #endif
diff --git a/libsysutils/include/sysutils/SocketListener.h b/libsysutils/include/sysutils/SocketListener.h
index bc93b86..67a691a 100644
--- a/libsysutils/include/sysutils/SocketListener.h
+++ b/libsysutils/include/sysutils/SocketListener.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2014 The Android Open Source Project
+ * 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.
@@ -18,6 +18,8 @@
 
 #include <pthread.h>
 
+#include <unordered_map>
+
 #include <sysutils/SocketClient.h>
 #include "SocketClientCommand.h"
 
@@ -25,7 +27,7 @@
     bool                    mListen;
     const char              *mSocketName;
     int                     mSock;
-    SocketClientCollection  *mClients;
+    std::unordered_map<int, SocketClient*> mClients;
     pthread_mutex_t         mClientsLock;
     int                     mCtrlPipe[2];
     pthread_t               mThread;
@@ -51,8 +53,13 @@
     virtual bool onDataAvailable(SocketClient *c) = 0;
 
 private:
-    bool release(SocketClient *c, bool wakeup);
     static void *threadStart(void *obj);
+
+    // Add all clients to a separate list, so we don't have to hold the lock
+    // while processing it.
+    std::vector<SocketClient*> snapshotClients();
+
+    bool release(SocketClient *c, bool wakeup);
     void runListener();
     void init(const char *socketName, int socketFd, bool listen, bool useCmdNum);
 };
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 87e2684..523584a 100644
--- a/libsysutils/src/FrameworkListener.cpp
+++ b/libsysutils/src/FrameworkListener.cpp
@@ -26,9 +26,7 @@
 #include <sysutils/FrameworkListener.h>
 #include <sysutils/SocketClient.h>
 
-static const int CMD_BUF_SIZE = 1024;
-
-#define UNUSED __attribute__((unused))
+static const int CMD_BUF_SIZE = 4096;
 
 FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :
                             SocketListener(socketName, true, withSeq) {
@@ -42,11 +40,10 @@
 
 FrameworkListener::FrameworkListener(int sock) :
                             SocketListener(sock, true) {
-    init(NULL, false);
+    init(nullptr, false);
 }
 
-void FrameworkListener::init(const char *socketName UNUSED, bool withSeq) {
-    mCommands = new FrameworkCommandCollection();
+void FrameworkListener::init(const char* /*socketName*/, bool withSeq) {
     errorRate = 0;
     mCommandCount = 0;
     mWithSeq = withSeq;
@@ -91,11 +88,10 @@
 }
 
 void FrameworkListener::registerCmd(FrameworkCommand *cmd) {
-    mCommands->push_back(cmd);
+    mCommands.push_back(cmd);
 }
 
 void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
-    FrameworkCommandCollection::iterator i;
     int argc = 0;
     char *argv[FrameworkListener::CMD_ARGS_MAX];
     char tmp[CMD_BUF_SIZE];
@@ -154,7 +150,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;
                 }
@@ -193,9 +189,7 @@
         goto out;
     }
 
-    for (i = mCommands->begin(); i != mCommands->end(); ++i) {
-        FrameworkCommand *c = *i;
-
+    for (auto* c : mCommands) {
         if (!strcmp(argv[0], c->getCommand())) {
             if (c->runCommand(cli, argc, argv)) {
                 SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index f0c66ec..8fe7854 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -39,14 +39,17 @@
 const int LOCAL_QLOG_NL_EVENT = 112;
 const int LOCAL_NFLOG_PACKET = NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET;
 
+#include <android-base/parseint.h>
 #include <log/log.h>
 #include <sysutils/NetlinkEvent.h>
 
+using android::base::ParseInt;
+
 NetlinkEvent::NetlinkEvent() {
     mAction = Action::kUnknown;
     memset(mParams, 0, sizeof(mParams));
-    mPath = NULL;
-    mSubsystem = NULL;
+    mPath = nullptr;
+    mSubsystem = nullptr;
 }
 
 NetlinkEvent::~NetlinkEvent() {
@@ -89,7 +92,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
 }
@@ -158,7 +161,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] = "";
 
@@ -286,7 +289,7 @@
 bool NetlinkEvent::parseNfPacketMessage(struct nlmsghdr *nh) {
     int uid = -1;
     int len = 0;
-    char* raw = NULL;
+    char* raw = nullptr;
 
     struct nlattr* uid_attr = findNlAttr(nh, sizeof(struct genlmsghdr), NFULA_UID);
     if (uid_attr) {
@@ -301,8 +304,9 @@
         raw = (char*)nlAttrData(payload);
     }
 
-    char* hex = (char*) calloc(1, 5 + (len * 2));
-    strcpy(hex, "HEX=");
+    size_t hexSize = 5 + (len * 2);
+    char* hex = (char*)calloc(1, hexSize);
+    strlcpy(hex, "HEX=", hexSize);
     for (int i = 0; i < len; i++) {
         hex[4 + (i * 2)] = "0123456789abcdef"[(raw[i] >> 4) & 0xf];
         hex[5 + (i * 2)] = "0123456789abcdef"[raw[i] & 0xf];
@@ -374,6 +378,7 @@
                     continue;
                 if (!if_indextoname(* (int *) RTA_DATA(rta), dev))
                     return false;
+                continue;
             default:
                 continue;
         }
@@ -473,23 +478,20 @@
         struct nd_opt_rdnss *rndss_opt = (struct nd_opt_rdnss *) opthdr;
         const uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime);
 
-        // Construct "SERVERS=<comma-separated string of DNS addresses>".
-        static const char kServerTag[] = "SERVERS=";
-        static const size_t kTagLength = strlen(kServerTag);
+        // Construct a comma-separated string of DNS addresses.
         // Reserve sufficient space for an IPv6 link-local address: all but the
         // last address are followed by ','; the last is followed by '\0'.
         static const size_t kMaxSingleAddressLength =
                 INET6_ADDRSTRLEN + strlen("%") + IFNAMSIZ + strlen(",");
-        const size_t bufsize = kTagLength + numaddrs * kMaxSingleAddressLength;
+        const size_t bufsize = numaddrs * kMaxSingleAddressLength;
         char *buf = (char *) malloc(bufsize);
         if (!buf) {
             SLOGE("RDNSS option: out of memory\n");
             return false;
         }
-        strcpy(buf, kServerTag);
-        size_t pos = kTagLength;
 
         struct in6_addr *addrs = (struct in6_addr *) (rndss_opt + 1);
+        size_t pos = 0;
         for (int i = 0; i < numaddrs; i++) {
             if (i > 0) {
                 buf[pos++] = ',';
@@ -507,7 +509,8 @@
         mSubsystem = strdup("net");
         asprintf(&mParams[0], "INTERFACE=%s", ifname);
         asprintf(&mParams[1], "LIFETIME=%u", lifetime);
-        mParams[2] = buf;
+        asprintf(&mParams[2], "SERVERS=%s", buf);
+        free(buf);
     } else if (opthdr->nd_opt_type == ND_OPT_DNSSL) {
         // TODO: support DNSSL.
     } else {
@@ -584,7 +587,7 @@
         (prefixlen == 0 || !memcmp(str, prefix, prefixlen))) {
         return str + prefixlen;
     } else {
-        return NULL;
+        return nullptr;
     }
 }
 
@@ -625,16 +628,18 @@
             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) {
-                mSeq = atoi(a);
-            } else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != NULL) {
+            } else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != nullptr) {
+                if (!ParseInt(a, &mSeq)) {
+                    SLOGE("NetlinkEvent::parseAsciiNetlinkMessage: failed to parse SEQNUM=%s", a);
+                }
+            } 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);
@@ -656,14 +661,14 @@
 
 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) {
diff --git a/libsysutils/src/OWNERS b/libsysutils/src/OWNERS
new file mode 100644
index 0000000..c65a40d
--- /dev/null
+++ b/libsysutils/src/OWNERS
@@ -0,0 +1,2 @@
+per-file OWNERS,Netlink* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, satk@google.com
+
diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp
index 971f908..fe2f3d6 100644
--- a/libsysutils/src/SocketClient.cpp
+++ b/libsysutils/src/SocketClient.cpp
@@ -27,6 +27,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <android-base/file.h>
+#include <android-base/macros.h>
 #include <log/log.h>
 #include <sysutils/SocketClient.h>
 
@@ -42,8 +44,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 +137,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++) = '"';
@@ -145,7 +147,8 @@
         switch (*arg) {
         case '\\':
         case '"':
-            *(current++) = '\\'; // fallthrough
+            *(current++) = '\\';
+            FALLTHROUGH_INTENDED;
         default:
             *(current++) = *(arg++);
         }
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp
index 3f8f3db..9780606 100644
--- a/libsysutils/src/SocketListener.cpp
+++ b/libsysutils/src/SocketListener.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2014 The Android Open Source Project
+ * 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.
@@ -19,13 +19,15 @@
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <sys/select.h>
+#include <sys/poll.h>
 #include <sys/socket.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/un.h>
 #include <unistd.h>
 
+#include <vector>
+
 #include <cutils/sockets.h>
 #include <log/log.h>
 #include <sysutils/SocketListener.h>
@@ -39,7 +41,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,8 +53,7 @@
     mSocketName = socketName;
     mSock = socketFd;
     mUseCmdNum = useCmdNum;
-    pthread_mutex_init(&mClientsLock, NULL);
-    mClients = new SocketClientCollection();
+    pthread_mutex_init(&mClientsLock, nullptr);
 }
 
 SocketListener::~SocketListener() {
@@ -63,12 +64,9 @@
         close(mCtrlPipe[0]);
         close(mCtrlPipe[1]);
     }
-    SocketClientCollection::iterator it;
-    for (it = mClients->begin(); it != mClients->end();) {
-        (*it)->decRef();
-        it = mClients->erase(it);
+    for (auto pair : mClients) {
+        pair.second->decRef();
     }
-    delete mClients;
 }
 
 int SocketListener::startListener() {
@@ -95,14 +93,14 @@
         SLOGE("Unable to listen on socket (%s)", strerror(errno));
         return -1;
     } else if (!mListen)
-        mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));
+        mClients[mSock] = new SocketClient(mSock, false, mUseCmdNum);
 
-    if (pipe(mCtrlPipe)) {
+    if (pipe2(mCtrlPipe, O_CLOEXEC)) {
         SLOGE("pipe failed (%s)", strerror(errno));
         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;
     }
@@ -135,11 +133,10 @@
         mSock = -1;
     }
 
-    SocketClientCollection::iterator it;
-    for (it = mClients->begin(); it != mClients->end();) {
-        delete (*it);
-        it = mClients->erase(it);
+    for (auto pair : mClients) {
+        delete pair.second;
     }
+    mClients.clear();
     return 0;
 }
 
@@ -147,52 +144,35 @@
     SocketListener *me = reinterpret_cast<SocketListener *>(obj);
 
     me->runListener();
-    pthread_exit(NULL);
-    return NULL;
+    pthread_exit(nullptr);
+    return nullptr;
 }
 
 void SocketListener::runListener() {
-
-    SocketClientCollection pendingList;
-
-    while(1) {
-        SocketClientCollection::iterator it;
-        fd_set read_fds;
-        int rc = 0;
-        int max = -1;
-
-        FD_ZERO(&read_fds);
-
-        if (mListen) {
-            max = mSock;
-            FD_SET(mSock, &read_fds);
-        }
-
-        FD_SET(mCtrlPipe[0], &read_fds);
-        if (mCtrlPipe[0] > max)
-            max = mCtrlPipe[0];
+    while (true) {
+        std::vector<pollfd> fds;
 
         pthread_mutex_lock(&mClientsLock);
-        for (it = mClients->begin(); it != mClients->end(); ++it) {
+        fds.reserve(2 + mClients.size());
+        fds.push_back({.fd = mCtrlPipe[0], .events = POLLIN});
+        if (mListen) fds.push_back({.fd = mSock, .events = POLLIN});
+        for (auto pair : mClients) {
             // NB: calling out to an other object with mClientsLock held (safe)
-            int fd = (*it)->getSocket();
-            FD_SET(fd, &read_fds);
-            if (fd > max) {
-                max = fd;
-            }
+            const int fd = pair.second->getSocket();
+            if (fd != pair.first) SLOGE("fd mismatch: %d != %d", fd, pair.first);
+            fds.push_back({.fd = fd, .events = POLLIN});
         }
         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 (errno == EINTR)
-                continue;
-            SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);
+
+        SLOGV("mListen=%d, mSocketName=%s", mListen, mSocketName);
+        int rc = TEMP_FAILURE_RETRY(poll(fds.data(), fds.size(), -1));
+        if (rc < 0) {
+            SLOGE("poll failed (%s) mListen=%d", strerror(errno), mListen);
             sleep(1);
             continue;
-        } else if (!rc)
-            continue;
+        }
 
-        if (FD_ISSET(mCtrlPipe[0], &read_fds)) {
+        if (fds[0].revents & (POLLIN | POLLERR)) {
             char c = CtrlPipe_Shutdown;
             TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));
             if (c == CtrlPipe_Shutdown) {
@@ -200,7 +180,7 @@
             }
             continue;
         }
-        if (mListen && FD_ISSET(mSock, &read_fds)) {
+        if (mListen && (fds[1].revents & (POLLIN | POLLERR))) {
             int c = TEMP_FAILURE_RETRY(accept4(mSock, nullptr, nullptr, SOCK_CLOEXEC));
             if (c < 0) {
                 SLOGE("accept failed (%s)", strerror(errno));
@@ -208,32 +188,33 @@
                 continue;
             }
             pthread_mutex_lock(&mClientsLock);
-            mClients->push_back(new SocketClient(c, true, mUseCmdNum));
+            mClients[c] = new SocketClient(c, true, mUseCmdNum);
             pthread_mutex_unlock(&mClientsLock);
         }
 
-        /* Add all active clients to the pending list first */
-        pendingList.clear();
+        // Add all active clients to the pending list first, so we can release
+        // the lock before invoking the callbacks.
+        std::vector<SocketClient*> pending;
         pthread_mutex_lock(&mClientsLock);
-        for (it = mClients->begin(); it != mClients->end(); ++it) {
-            SocketClient* c = *it;
-            // NB: calling out to an other object with mClientsLock held (safe)
-            int fd = c->getSocket();
-            if (FD_ISSET(fd, &read_fds)) {
-                pendingList.push_back(c);
+        const int size = fds.size();
+        for (int i = mListen ? 2 : 1; i < size; ++i) {
+            const struct pollfd& p = fds[i];
+            if (p.revents & (POLLIN | POLLERR)) {
+                auto it = mClients.find(p.fd);
+                if (it == mClients.end()) {
+                    SLOGE("fd vanished: %d", p.fd);
+                    continue;
+                }
+                SocketClient* c = it->second;
+                pending.push_back(c);
                 c->incRef();
             }
         }
         pthread_mutex_unlock(&mClientsLock);
 
-        /* Process the pending list, since it is owned by the thread,
-         * there is no need to lock it */
-        while (!pendingList.empty()) {
-            /* Pop the first item from the list */
-            it = pendingList.begin();
-            SocketClient* c = *it;
-            pendingList.erase(it);
-            /* Process it, if false is returned, remove from list */
+        for (SocketClient* c : pending) {
+            // Process it, if false is returned, remove from the map
+            SLOGV("processing fd %d", c->getSocket());
             if (!onDataAvailable(c)) {
                 release(c, false);
             }
@@ -246,17 +227,10 @@
     bool ret = false;
     /* if our sockets are connection-based, remove and destroy it */
     if (mListen && c) {
-        /* Remove the client from our array */
+        /* Remove the client from our map */
         SLOGV("going to zap %d for %s", c->getSocket(), mSocketName);
         pthread_mutex_lock(&mClientsLock);
-        SocketClientCollection::iterator it;
-        for (it = mClients->begin(); it != mClients->end(); ++it) {
-            if (*it == c) {
-                mClients->erase(it);
-                ret = true;
-                break;
-            }
-        }
+        ret = (mClients.erase(c->getSocket()) != 0);
         pthread_mutex_unlock(&mClientsLock);
         if (ret) {
             ret = c->decRef();
@@ -269,26 +243,22 @@
     return ret;
 }
 
-void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) {
-    SocketClientCollection safeList;
-
-    /* Add all active clients to the safe list first */
-    safeList.clear();
+std::vector<SocketClient*> SocketListener::snapshotClients() {
+    std::vector<SocketClient*> clients;
     pthread_mutex_lock(&mClientsLock);
-    SocketClientCollection::iterator i;
-
-    for (i = mClients->begin(); i != mClients->end(); ++i) {
-        SocketClient* c = *i;
+    clients.reserve(mClients.size());
+    for (auto pair : mClients) {
+        SocketClient* c = pair.second;
         c->incRef();
-        safeList.push_back(c);
+        clients.push_back(c);
     }
     pthread_mutex_unlock(&mClientsLock);
 
-    while (!safeList.empty()) {
-        /* Pop the first item from the list */
-        i = safeList.begin();
-        SocketClient* c = *i;
-        safeList.erase(i);
+    return clients;
+}
+
+void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) {
+    for (SocketClient* c : snapshotClients()) {
         // broadcasts are unsolicited and should not include a cmd number
         if (c->sendMsg(code, msg, addErrno, false)) {
             SLOGW("Error sending broadcast (%s)", strerror(errno));
@@ -298,25 +268,7 @@
 }
 
 void SocketListener::runOnEachSocket(SocketClientCommand *command) {
-    SocketClientCollection safeList;
-
-    /* Add all active clients to the safe list first */
-    safeList.clear();
-    pthread_mutex_lock(&mClientsLock);
-    SocketClientCollection::iterator i;
-
-    for (i = mClients->begin(); i != mClients->end(); ++i) {
-        SocketClient* c = *i;
-        c->incRef();
-        safeList.push_back(c);
-    }
-    pthread_mutex_unlock(&mClientsLock);
-
-    while (!safeList.empty()) {
-        /* Pop the first item from the list */
-        i = safeList.begin();
-        SocketClient* c = *i;
-        safeList.erase(i);
+    for (SocketClient* c : snapshotClients()) {
         command->runSocketCommand(c);
         c->decRef();
     }
diff --git a/libsysutils/src/SocketListener_test.cpp b/libsysutils/src/SocketListener_test.cpp
new file mode 100644
index 0000000..d6bfd02
--- /dev/null
+++ b/libsysutils/src/SocketListener_test.cpp
@@ -0,0 +1,183 @@
+/*
+ * 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 <sysutils/FrameworkCommand.h>
+#include <sysutils/FrameworkListener.h>
+
+#include <poll.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <algorithm>
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+#include <gtest/gtest.h>
+
+using android::base::unique_fd;
+
+namespace {
+
+std::string testSocketPath() {
+    const testing::TestInfo* const test_info =
+            testing::UnitTest::GetInstance()->current_test_info();
+    return std::string(ANDROID_SOCKET_DIR "/") + std::string(test_info->test_case_name()) +
+           std::string(".") + std::string(test_info->name());
+}
+
+unique_fd serverSocket(const std::string& path) {
+    unlink(path.c_str());
+
+    unique_fd fd(socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+    EXPECT_GE(fd.get(), 0);
+
+    struct sockaddr_un addr = {.sun_family = AF_UNIX};
+    strlcpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path));
+
+    EXPECT_EQ(bind(fd.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)), 0)
+            << "bind() to " << path << " failed: " << strerror(errno);
+    EXPECT_EQ(android_get_control_socket(path.c_str()), -1);
+
+    return fd;
+}
+
+unique_fd clientSocket(const std::string& path) {
+    unique_fd fd(socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+    EXPECT_GE(fd.get(), 0);
+
+    struct sockaddr_un addr = {.sun_family = AF_UNIX};
+    strlcpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path));
+
+    EXPECT_EQ(0, connect(fd.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)))
+            << "connect() to " << path << " failed: " << strerror(errno);
+
+    return fd;
+}
+
+void sendCmd(int fd, const char* cmd) {
+    EXPECT_TRUE(android::base::WriteFully(fd, cmd, strlen(cmd) + 1))
+            << "write() to socket failed: " << strerror(errno);
+}
+
+std::string recvReply(int fd) {
+    pollfd fds = {.fd = fd, .events = POLLIN};
+    int poll_events = poll(&fds, 1, -1);
+    EXPECT_EQ(1, poll_events);
+
+    // Technically, this one-shot read() is incorrect: we should keep on
+    // reading the socket until we get a \0. But this is also how
+    // FrameworkListener::onDataAvailable() reads, and it works because
+    // replies are always send with a single write() call, and clients
+    // always read replies before queueing the next command.
+    char buf[1024];
+    ssize_t len = read(fd, buf, sizeof(buf));
+    EXPECT_GE(len, 0) << "read() from socket failed: " << strerror(errno);
+    return len > 0 ? std::string(buf, buf + len) : "";
+}
+
+// Test command which echoes back all its arguments as a comma-separated list.
+// Always returns error code 42
+//
+// TODO: enable testing replies with addErrno=true and useCmdNum=true
+class TestCommand : public FrameworkCommand {
+  public:
+    TestCommand() : FrameworkCommand("test") {}
+    ~TestCommand() override {}
+
+    int runCommand(SocketClient* cli, int argc, char** argv) {
+        std::vector<std::string> args(argv, argv + argc);
+        std::string reply = android::base::Join(args, ',');
+        cli->sendMsg(42, reply.c_str(), /*addErrno=*/false, /*useCmdNum=*/false);
+        return 0;
+    }
+};
+
+// A test listener with a single command.
+class TestListener : public FrameworkListener {
+  public:
+    TestListener(int fd) : FrameworkListener(fd) {
+        registerCmd(new TestCommand);  // Leaked :-(
+    }
+};
+
+}  // unnamed namespace
+
+class FrameworkListenerTest : public testing::Test {
+  public:
+    FrameworkListenerTest() {
+        mSocketPath = testSocketPath();
+        mSserverFd = serverSocket(mSocketPath);
+        mListener = std::make_unique<TestListener>(mSserverFd.get());
+        EXPECT_EQ(0, mListener->startListener());
+    }
+
+    ~FrameworkListenerTest() override {
+        EXPECT_EQ(0, mListener->stopListener());
+
+        // Wouldn't it be cool if unique_fd had an option for taking care of this?
+        unlink(mSocketPath.c_str());
+    }
+
+    void testCommand(const char* command, const char* expected) {
+        unique_fd client_fd = clientSocket(mSocketPath);
+        sendCmd(client_fd.get(), command);
+
+        std::string reply = recvReply(client_fd.get());
+        EXPECT_EQ(std::string(expected) + '\0', reply);
+    }
+
+  protected:
+    std::string mSocketPath;
+    unique_fd mSserverFd;
+    std::unique_ptr<TestListener> mListener;
+};
+
+TEST_F(FrameworkListenerTest, DoesNothing) {
+    // Let the test harness start and stop a FrameworkListener
+    // without sending any commands through it.
+}
+
+TEST_F(FrameworkListenerTest, DispatchesValidCommands) {
+    testCommand("test", "42 test");
+    testCommand("test arg1 arg2", "42 test,arg1,arg2");
+    testCommand("test \"arg1 still_arg1\" arg2", "42 test,arg1 still_arg1,arg2");
+    testCommand("test \"escaped quote: '\\\"'\"", "42 test,escaped quote: '\"'");
+
+    // Perhaps this behavior was unintended, but would be good to detect any
+    // changes, in case anyone depends on it.
+    testCommand("test   ", "42 test,,,");
+}
+
+TEST_F(FrameworkListenerTest, RejectsInvalidCommands) {
+    testCommand("unknown arg1 arg2", "500 Command not recognized");
+    testCommand("test \"arg1 arg2", "500 Unclosed quotes error");
+    testCommand("test \\a", "500 Unsupported escape sequence");
+}
+
+TEST_F(FrameworkListenerTest, MultipleClients) {
+    unique_fd client1 = clientSocket(mSocketPath);
+    unique_fd client2 = clientSocket(mSocketPath);
+    sendCmd(client1.get(), "test 1");
+    sendCmd(client2.get(), "test 2");
+
+    EXPECT_EQ(std::string("42 test,2") + '\0', recvReply(client2.get()));
+    EXPECT_EQ(std::string("42 test,1") + '\0', recvReply(client1.get()));
+}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index adbacdb..5423de5 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -38,6 +38,7 @@
 cc_library {
     name: "libunwindstack",
     vendor_available: true,
+    recovery_available: true,
     vndk: {
         enabled: true,
         support_system_process: true,
@@ -47,6 +48,8 @@
 
     srcs: [
         "ArmExidx.cpp",
+        "DexFile.cpp",
+        "DexFiles.cpp",
         "DwarfCfa.cpp",
         "DwarfEhFrameWithHdr.cpp",
         "DwarfMemory.cpp",
@@ -55,11 +58,13 @@
         "Elf.cpp",
         "ElfInterface.cpp",
         "ElfInterfaceArm.cpp",
+        "Global.cpp",
         "JitDebug.cpp",
         "Log.cpp",
         "MapInfo.cpp",
         "Maps.cpp",
         "Memory.cpp",
+        "LocalUnwinder.cpp",
         "Regs.cpp",
         "RegsArm.cpp",
         "RegsArm64.cpp",
@@ -85,11 +90,25 @@
         },
         vendor: {
             cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
-            exclude_static_libs: ["libunwindstack_dex"],
-            exclude_shared_libs: ["libdexfile"],
+            exclude_srcs: [
+                "DexFile.cpp",
+                "DexFiles.cpp",
+            ],
+            exclude_shared_libs: [
+                "libdexfile_support",
+            ],
+        },
+        recovery: {
+            cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+            exclude_srcs: [
+                "DexFile.cpp",
+                "DexFiles.cpp",
+            ],
+            exclude_shared_libs: [
+                "libdexfile_support",
+            ],
         },
     },
-    whole_static_libs: ["libunwindstack_dex"],
 
     arch: {
         x86: {
@@ -106,84 +125,37 @@
         },
     },
 
+    whole_static_libs: [
+        "libdemangle"
+    ],
+
+    static_libs: [
+        "libprocinfo",
+    ],
+
     shared_libs: [
         "libbase",
-        "libdexfile",
+        "libdexfile_support",
         "liblog",
         "liblzma",
     ],
 }
 
-// Isolate the dex file processing into a separate library. Currently,
-// it is necessary to add art include directories directly, which also
-// adds the art elf.h file in the include path, overriding the system one.
-// Work to isolate libdexfile is b/72216369.
-cc_library_static {
-    name: "libunwindstack_dex",
-    vendor_available: false,
-    defaults: ["libunwindstack_flags"],
-
-    cflags: [
-        "-Wexit-time-destructors",
-    ],
-
-    srcs: [
-        "DexFile.cpp",
-        "DexFiles.cpp",
-    ],
-    target: {
-        // Always disable optimizations for host to make it easier to debug.
-        host: {
-            cflags: [
-                "-O0",
-                "-g",
-            ],
-        },
-    },
-
-    shared_libs: [
-        "libbase",
-        "libdexfile",
-    ],
-    local_include_dirs: ["include"],
-    allow_undefined_symbols: true,
-
-    // libdexfile will eventually properly export headers, for now include
-    // these directly.
-    include_dirs: [
-        "art/runtime",
-    ],
-}
-
 //-------------------------------------------------------------------------
 // Unit Tests
 //-------------------------------------------------------------------------
 cc_test_library {
-    name: "libunwindstack_dex_test",
-    vendor_available: false,
+    name: "libunwindstack_local",
     defaults: ["libunwindstack_flags"],
+    srcs: ["tests/TestLocal.cpp"],
 
-    shared: {
-        enabled: false,
-    },
-
-    srcs: [
-        "tests/DexFileTest.cpp",
-        "tests/DexFilesTest.cpp",
+    cflags: [
+        "-O0",
+        "-g",
     ],
-    local_include_dirs: ["include"],
-    allow_undefined_symbols: true,
 
     shared_libs: [
-        "libbase",
         "libunwindstack",
-        "libdexfile",
-    ],
-
-    // libdexfile will eventually properly export headers, for now include
-    // these directly.
-    include_dirs: [
-        "art/runtime",
     ],
 }
 
@@ -194,6 +166,8 @@
     srcs: [
         "tests/ArmExidxDecodeTest.cpp",
         "tests/ArmExidxExtractTest.cpp",
+        "tests/DexFileTest.cpp",
+        "tests/DexFilesTest.cpp",
         "tests/DwarfCfaLogTest.cpp",
         "tests/DwarfCfaTest.cpp",
         "tests/DwarfDebugFrameTest.cpp",
@@ -211,23 +185,31 @@
         "tests/ElfTest.cpp",
         "tests/ElfTestUtils.cpp",
         "tests/JitDebugTest.cpp",
+        "tests/LocalUnwinderTest.cpp",
         "tests/LogFake.cpp",
+        "tests/MapInfoCreateMemoryTest.cpp",
+        "tests/MapInfoGetBuildIDTest.cpp",
         "tests/MapInfoGetElfTest.cpp",
         "tests/MapInfoGetLoadBiasTest.cpp",
+        "tests/MapInfoTest.cpp",
         "tests/MapsTest.cpp",
         "tests/MemoryBufferTest.cpp",
+        "tests/MemoryCacheTest.cpp",
         "tests/MemoryFake.cpp",
         "tests/MemoryFileTest.cpp",
         "tests/MemoryLocalTest.cpp",
         "tests/MemoryOfflineBufferTest.cpp",
         "tests/MemoryOfflineTest.cpp",
         "tests/MemoryRangeTest.cpp",
+        "tests/MemoryRangesTest.cpp",
         "tests/MemoryRemoteTest.cpp",
         "tests/MemoryTest.cpp",
+        "tests/RegsInfoTest.cpp",
         "tests/RegsIterateTest.cpp",
         "tests/RegsStepIfSignalHandlerTest.cpp",
         "tests/RegsTest.cpp",
         "tests/SymbolsTest.cpp",
+        "tests/TestUtils.cpp",
         "tests/UnwindOfflineTest.cpp",
         "tests/UnwindTest.cpp",
         "tests/UnwinderTest.cpp",
@@ -243,28 +225,37 @@
         "liblog",
         "liblzma",
         "libunwindstack",
+        "libdexfile_support",
     ],
 
     static_libs: [
         "libgmock",
     ],
 
-    whole_static_libs: ["libunwindstack_dex_test"],
-
+    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/invalid_elf_offset_arm/*",
         "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/shared_lib_in_apk_arm64/*",
+        "tests/files/offline/shared_lib_in_apk_memory_only_arm64/*",
+        "tests/files/offline/shared_lib_in_apk_single_map_arm64/*",
         "tests/files/offline/straddle_arm/*",
         "tests/files/offline/straddle_arm64/*",
     ],
+    required: [
+        "libunwindstack_local",
+    ],
 }
 
 //-------------------------------------------------------------------------
@@ -279,6 +270,15 @@
         "libbase",
         "liblzma",
     ],
+    target: {
+        // Always disable optimizations for host to make it easier to debug.
+        host: {
+            cflags: [
+                "-O0",
+                "-g",
+            ],
+        },
+    },
 }
 
 cc_binary {
@@ -326,6 +326,29 @@
     ],
 }
 
+//-------------------------------------------------------------------------
+// Benchmarks
+//-------------------------------------------------------------------------
+cc_benchmark {
+    name: "unwind_benchmarks",
+    host_supported: true,
+    defaults: ["libunwindstack_flags"],
+
+    // Disable optimizations so that all of the calls are not optimized away.
+    cflags: [
+        "-O0",
+    ],
+
+    srcs: [
+        "benchmarks/unwind_benchmarks.cpp",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libunwindstack",
+    ],
+}
+
 // Generates the elf data for use in the tests for .gnu_debugdata frames.
 // Once these files are generated, use the xz command to compress the data.
 cc_binary_host {
diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp
index 6e397e3..818f5d1 100644
--- a/libunwindstack/ArmExidx.cpp
+++ b/libunwindstack/ArmExidx.cpp
@@ -31,6 +31,8 @@
 
 namespace unwindstack {
 
+static constexpr uint8_t LOG_CFA_REG = 64;
+
 void ArmExidx::LogRawData() {
   std::string log_str("Raw Data:");
   for (const uint8_t data : data_) {
@@ -63,8 +65,10 @@
   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;
@@ -86,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;
@@ -163,7 +167,7 @@
     data_.push_back(ARM_OP_FINISH);
   }
 
-  if (log_) {
+  if (log_type_ == ARM_LOG_FULL) {
     LogRawData();
   }
   return true;
@@ -190,32 +194,45 @@
   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])) {
@@ -246,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;
     }
@@ -270,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;
     }
@@ -307,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;
@@ -326,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;
@@ -334,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;
@@ -342,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;
     }
@@ -373,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;
@@ -387,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;
     }
@@ -404,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;
     }
@@ -422,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;
@@ -433,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;
     }
@@ -489,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;
       }
@@ -510,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;
@@ -543,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;
       }
@@ -570,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;
       }
@@ -590,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;
       }
@@ -606,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;
@@ -619,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;
     }
@@ -646,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;
@@ -664,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;
       }
@@ -674,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;
       }
@@ -696,4 +827,36 @@
   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 96756a0..d9fc371 100644
--- a/libunwindstack/ArmExidx.h
+++ b/libunwindstack/ArmExidx.h
@@ -20,6 +20,7 @@
 #include <stdint.h>
 
 #include <deque>
+#include <map>
 
 namespace unwindstack {
 
@@ -44,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)
@@ -52,6 +59,8 @@
 
   void LogRawData();
 
+  void LogByReg();
+
   bool ExtractEntryData(uint32_t entry_offset);
 
   bool Eval();
@@ -71,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);
@@ -103,10 +113,12 @@
   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
diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp
index b18b0ce..eaf867f 100644
--- a/libunwindstack/DexFile.cpp
+++ b/libunwindstack/DexFile.cpp
@@ -23,12 +23,7 @@
 #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 <art_api/dex_file_support.h>
 
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Memory.h>
@@ -37,149 +32,71 @@
 
 namespace unwindstack {
 
-DexFile* DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info) {
+std::unique_ptr<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<DexFile> dex_file =
+        DexFileFromFile::Create(dex_file_offset_in_memory - info->start + info->offset, info->name);
+    if (dex_file) {
+      return dex_file;
     }
   }
-
-  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_);
-  }
+  return DexFileFromMemory::Create(dex_file_offset_in_memory, memory, info->name);
 }
 
 bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
                                    uint64_t* method_offset) {
-  if (dex_file_ == nullptr) {
+  art_api::dex::MethodInfo method_info = GetMethodInfoForOffset(dex_offset, false);
+  if (method_info.offset == 0) {
     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.
-  }
-
-  for (uint32_t i = 0; i < dex_file_->NumClassDefs(); ++i) {
-    const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(i);
-    const uint8_t* class_data = dex_file_->GetClassData(class_def);
-    if (class_data == nullptr) {
-      continue;
-    }
-    for (art::ClassDataItemIterator it(*dex_file_.get(), class_data); it.HasNext(); it.Next()) {
-      if (!it.IsAtMethod()) {
-        continue;
-      }
-      const art::DexFile::CodeItem* code_item = it.GetMethodCodeItem();
-      if (code_item == nullptr) {
-        continue;
-      }
-      art::CodeItemInstructionAccessor code(*dex_file_.get(), code_item);
-      if (!code.HasCodeItem()) {
-        continue;
-      }
-
-      uint64_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin();
-      size_t size = code.InsnsSizeInCodeUnits() * sizeof(uint16_t);
-      if (offset <= dex_offset && dex_offset < offset + size) {
-        *method_name = dex_file_->PrettyMethod(it.GetMemberIndex(), false);
-        *method_offset = dex_offset - offset;
-        return true;
-      }
-    }
-  }
-  return false;
+  *method_name = method_info.name;
+  *method_offset = dex_offset - method_info.offset;
+  return true;
 }
 
-bool DexFileFromFile::Open(uint64_t dex_file_offset_in_file, const std::string& file) {
+std::unique_ptr<DexFileFromFile> DexFileFromFile::Create(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;
+    return nullptr;
   }
 
-  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;
+  std::unique_ptr<art_api::dex::DexFile> art_dex_file =
+      OpenFromFd(fd, dex_file_offset_in_file, file, &error_msg);
+  if (art_dex_file == nullptr) {
+    return nullptr;
+  }
+
+  return std::unique_ptr<DexFileFromFile>(new DexFileFromFile(std::move(*art_dex_file.release())));
 }
 
-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;
-  }
+std::unique_ptr<DexFileFromMemory> DexFileFromMemory::Create(uint64_t dex_file_offset_in_memory,
+                                                             Memory* memory,
+                                                             const std::string& name) {
+  std::vector<uint8_t> backing_memory;
 
-  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;
+  for (size_t size = 0;;) {
+    std::string error_msg;
+    std::unique_ptr<art_api::dex::DexFile> art_dex_file =
+        OpenFromMemory(backing_memory.data(), &size, name, &error_msg);
+
+    if (art_dex_file != nullptr) {
+      return std::unique_ptr<DexFileFromMemory>(
+          new DexFileFromMemory(std::move(*art_dex_file.release()), std::move(backing_memory)));
     }
-    if (computed_file_size > file_size) {
-      file_size = computed_file_size;
+
+    if (!error_msg.empty()) {
+      return nullptr;
     }
-  } else if (!art::StandardDexFile::IsMagicValid(header->magic_)) {
-    return false;
+
+    backing_memory.resize(size);
+    if (!memory->ReadFully(dex_file_offset_in_memory, backing_memory.data(),
+                           backing_memory.size())) {
+      return nullptr;
+    }
   }
-
-  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
index 3ce2f1e..ca658e6 100644
--- a/libunwindstack/DexFile.h
+++ b/libunwindstack/DexFile.h
@@ -19,47 +19,47 @@
 
 #include <stdint.h>
 
+#include <map>
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
-#include <dex/dex_file-inl.h>
+#include <art_api/dex_file_support.h>
 
 namespace unwindstack {
 
-class DexFile {
+class DexFile : protected art_api::dex::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);
+  static std::unique_ptr<DexFile> Create(uint64_t dex_file_offset_in_memory, Memory* memory,
+                                         MapInfo* info);
 
  protected:
-  std::unique_ptr<const art::DexFile> dex_file_;
+  DexFile(art_api::dex::DexFile&& art_dex_file) : art_api::dex::DexFile(std::move(art_dex_file)) {}
 };
 
 class DexFileFromFile : public DexFile {
  public:
-  DexFileFromFile() = default;
-  virtual ~DexFileFromFile();
-
-  bool Open(uint64_t dex_file_offset_in_file, const std::string& name);
+  static std::unique_ptr<DexFileFromFile> Create(uint64_t dex_file_offset_in_file,
+                                                 const std::string& file);
 
  private:
-  void* mapped_memory_ = nullptr;
-  size_t size_ = 0;
+  DexFileFromFile(art_api::dex::DexFile&& art_dex_file) : DexFile(std::move(art_dex_file)) {}
 };
 
 class DexFileFromMemory : public DexFile {
  public:
-  DexFileFromMemory() = default;
-  virtual ~DexFileFromMemory() = default;
-
-  bool Open(uint64_t dex_file_offset_in_memory, Memory* memory);
+  static std::unique_ptr<DexFileFromMemory> Create(uint64_t dex_file_offset_in_memory,
+                                                   Memory* memory, const std::string& name);
 
  private:
+  DexFileFromMemory(art_api::dex::DexFile&& art_dex_file, std::vector<uint8_t>&& memory)
+      : DexFile(std::move(art_dex_file)), memory_(std::move(memory)) {}
+
   std::vector<uint8_t> memory_;
 };
 
diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp
index 430f6c5..63a77e5 100644
--- a/libunwindstack/DexFiles.cpp
+++ b/libunwindstack/DexFiles.cpp
@@ -43,19 +43,15 @@
   uint64_t dex_file;
 };
 
-DexFiles::DexFiles(std::shared_ptr<Memory>& memory) : memory_(memory) {}
+DexFiles::DexFiles(std::shared_ptr<Memory>& memory) : Global(memory) {}
 
 DexFiles::DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
-    : memory_(memory), search_libs_(search_libs) {}
+    : Global(memory, search_libs) {}
 
-DexFiles::~DexFiles() {
-  for (auto& entry : files_) {
-    delete entry.second;
-  }
-}
+DexFiles::~DexFiles() {}
 
-void DexFiles::SetArch(ArchEnum arch) {
-  switch (arch) {
+void DexFiles::ProcessArch() {
+  switch (arch()) {
     case ARCH_ARM:
     case ARCH_MIPS:
     case ARCH_X86:
@@ -117,6 +113,11 @@
   return true;
 }
 
+bool DexFiles::ReadVariableData(uint64_t ptr_offset) {
+  entry_addr_ = (this->*read_entry_ptr_func_)(ptr_offset);
+  return entry_addr_ != 0;
+}
+
 void DexFiles::Init(Maps* maps) {
   if (initialized_) {
     return;
@@ -124,36 +125,7 @@
   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;
-      }
-    }
-  }
+  FindAndReadVariable(maps, "__dex_debug_descriptor");
 }
 
 DexFile* DexFiles::GetDexFile(uint64_t dex_file_offset, MapInfo* info) {
@@ -161,10 +133,11 @@
   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;
+    std::unique_ptr<DexFile> new_dex_file = DexFile::Create(dex_file_offset, memory_.get(), info);
+    dex_file = new_dex_file.get();
+    files_[dex_file_offset] = std::move(new_dex_file);
   } else {
-    dex_file = entry->second;
+    dex_file = entry->second.get();
   }
   return dex_file;
 }
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index 6ecedce..c128b9b 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -21,6 +21,7 @@
 #include <type_traits>
 #include <vector>
 
+#include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 
 #include <unwindstack/DwarfError.h>
@@ -154,13 +155,15 @@
       break;
     case DwarfCfaInfo::DWARF_DISPLAY_ADVANCE_LOC:
       *cur_pc += value;
-    // Fall through to log the value.
+      FALLTHROUGH_INTENDED;
+      // Fall through to log the value.
     case DwarfCfaInfo::DWARF_DISPLAY_NUMBER:
       string += " " + std::to_string(value);
       break;
     case DwarfCfaInfo::DWARF_DISPLAY_SET_LOC:
       *cur_pc = value;
-    // Fall through to log the value.
+      FALLTHROUGH_INTENDED;
+      // Fall through to log the value.
     case DwarfCfaInfo::DWARF_DISPLAY_ADDRESS:
       if (std::is_same<AddressType, uint32_t>::value) {
         string += android::base::StringPrintf(" 0x%" PRIx32, static_cast<uint32_t>(value));
@@ -201,7 +204,7 @@
 bool DwarfCfa<AddressType>::LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op,
                                            uint64_t* cur_pc) {
   const auto* cfa = &DwarfCfaInfo::kTable[op];
-  if (cfa->name == nullptr) {
+  if (cfa->name[0] == '\0') {
     log(indent, "Illegal");
     log(indent, "Raw Data: 0x%02x", op);
     return true;
@@ -257,15 +260,15 @@
   }
 
   // Log any of the expression data.
-  for (const auto line : expression_lines) {
+  for (const auto& line : expression_lines) {
     log(indent + 1, "%s", line.c_str());
   }
   return true;
 }
 
 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;
@@ -301,8 +304,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;
   }
@@ -674,29 +677,29 @@
         {DW_EH_PE_uleb128, DW_EH_PE_block},
         {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_EVAL_BLOCK},
     },
-    {nullptr, 0, 0, {}, {}},  // 0x17 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x18 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x19 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x1a illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x1b illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x1c DW_CFA_lo_user (Treat as illegal)
-    {nullptr, 0, 0, {}, {}},  // 0x1d illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x1e illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x1f illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x20 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x21 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x22 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x23 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x24 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x25 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x26 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x27 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x28 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x29 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x2a illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x2b illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x2c illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x2d DW_CFA_GNU_window_save (Treat as illegal)
+    {"", 0, 0, {}, {}},  // 0x17 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x18 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x19 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x1a illegal cfa
+    {"", 0, 0, {}, {}},  // 0x1b illegal cfa
+    {"", 0, 0, {}, {}},  // 0x1c DW_CFA_lo_user (Treat as illegal)
+    {"", 0, 0, {}, {}},  // 0x1d illegal cfa
+    {"", 0, 0, {}, {}},  // 0x1e illegal cfa
+    {"", 0, 0, {}, {}},  // 0x1f illegal cfa
+    {"", 0, 0, {}, {}},  // 0x20 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x21 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x22 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x23 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x24 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x25 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x26 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x27 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x28 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x29 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x2a illegal cfa
+    {"", 0, 0, {}, {}},  // 0x2b illegal cfa
+    {"", 0, 0, {}, {}},  // 0x2c illegal cfa
+    {"", 0, 0, {}, {}},  // 0x2d DW_CFA_GNU_window_save (Treat as illegal)
     {
         "DW_CFA_GNU_args_size",  // 0x2e DW_CFA_GNU_args_size
         2,
@@ -711,21 +714,21 @@
         {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
         {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
     },
-    {nullptr, 0, 0, {}, {}},  // 0x31 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x32 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x33 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x34 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x35 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x36 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x37 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x38 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x39 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x3a illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x3b illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x3c illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x3d illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x3e illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x3f DW_CFA_hi_user (Treat as illegal)
+    {"", 0, 0, {}, {}},  // 0x31 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x32 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x33 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x34 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x35 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x36 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x37 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x38 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x39 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x3a illegal cfa
+    {"", 0, 0, {}, {}},  // 0x3b illegal cfa
+    {"", 0, 0, {}, {}},  // 0x3c illegal cfa
+    {"", 0, 0, {}, {}},  // 0x3d illegal cfa
+    {"", 0, 0, {}, {}},  // 0x3e illegal cfa
+    {"", 0, 0, {}, {}},  // 0x3f DW_CFA_hi_user (Treat as illegal)
 };
 
 // Explicitly instantiate DwarfCfa.
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
index 16c66e2..569c17c 100644
--- a/libunwindstack/DwarfCfa.h
+++ b/libunwindstack/DwarfCfa.h
@@ -49,7 +49,14 @@
   };
 
   struct Info {
-    const char* name;
+    // It may seem cleaner to just change the type of 'name' to 'const char *'.
+    // However, having a pointer here would require relocation at runtime,
+    // causing 'kTable' to be placed in data.rel.ro section instead of rodata
+    // section, adding memory pressure to the system.  Note that this is only
+    // safe because this is only used in C++ code.  C++ standard, unlike C
+    // standard, mandates the array size to be large enough to hold the NULL
+    // terminator when initialized with a string literal.
+    const char name[36];
     uint8_t supported_version;
     uint8_t num_operands;
     uint8_t operands[2];
@@ -71,8 +78,7 @@
   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);
 
   const DwarfErrorData& last_error() { return last_error_; }
   DwarfErrorCode LastErrorCode() { return last_error_.code; }
diff --git a/libunwindstack/DwarfDebugFrame.h b/libunwindstack/DwarfDebugFrame.h
index 635cefd..388ab0a 100644
--- a/libunwindstack/DwarfDebugFrame.h
+++ b/libunwindstack/DwarfDebugFrame.h
@@ -26,9 +26,9 @@
 namespace unwindstack {
 
 template <typename AddressType>
-class DwarfDebugFrame : public DwarfSectionImpl<AddressType> {
+class DwarfDebugFrame : public DwarfSectionImplNoHdr<AddressType> {
  public:
-  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);
   }
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h
index 7a41e45..df441fb 100644
--- a/libunwindstack/DwarfEhFrame.h
+++ b/libunwindstack/DwarfEhFrame.h
@@ -25,9 +25,9 @@
 namespace unwindstack {
 
 template <typename AddressType>
-class DwarfEhFrame : public DwarfSectionImpl<AddressType> {
+class DwarfEhFrame : public DwarfSectionImplNoHdr<AddressType> {
  public:
-  DwarfEhFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+  DwarfEhFrame(Memory* memory) : DwarfSectionImplNoHdr<AddressType>(memory) {}
   virtual ~DwarfEhFrame() = default;
 
   uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
diff --git a/libunwindstack/DwarfEhFrameWithHdr.cpp b/libunwindstack/DwarfEhFrameWithHdr.cpp
index 9a49013..802beca 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.cpp
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -22,19 +22,27 @@
 
 #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) {
-  uint8_t data[4];
+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();
@@ -53,6 +61,14 @@
   table_encoding_ = data[3];
   table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
 
+  // If we can't perform a binary search on the entries, it's not worth
+  // using this object. The calling code will fall back to the DwarfEhFrame
+  // object in this case.
+  if (table_entry_size_ == 0) {
+    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+
   memory_.set_pc_offset(memory_.cur_offset());
   if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
     last_error_.code = DWARF_ERROR_MEMORY_INVALID;
@@ -81,12 +97,22 @@
 }
 
 template <typename AddressType>
-const DwarfFde* DwarfEhFrameWithHdr<AddressType>::GetFdeFromIndex(size_t index) {
-  const FdeInfo* info = GetFdeInfoFromIndex(index);
-  if (info == nullptr) {
+const DwarfFde* DwarfEhFrameWithHdr<AddressType>::GetFdeFromPc(uint64_t pc) {
+  uint64_t fde_offset;
+  if (!GetFdeOffsetFromPc(pc, &fde_offset)) {
     return nullptr;
   }
-  return this->GetFdeFromOffset(info->offset);
+  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>
@@ -100,7 +126,7 @@
 
   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());
+  memory_.set_pc_offset(0);
   uint64_t value;
   if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
       !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
@@ -109,18 +135,23 @@
     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_);
+bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
+  if (fde_count_ == 0) {
+    return false;
+  }
 
   size_t first = 0;
-  size_t last = total_entries;
+  size_t last = fde_count_;
   while (first < last) {
     size_t current = (first + last) / 2;
     const FdeInfo* info = GetFdeInfoFromIndex(current);
@@ -149,83 +180,17 @@
 }
 
 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;
+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;
     }
-    if (pc < info->pc) {
-      return GetFdeOffsetBinary(pc, fde_offset, fde_info_.size());
+    const DwarfFde* fde = this->GetFdeFromOffset(info->offset);
+    if (fde == nullptr) {
+      break;
     }
-  }
-
-  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_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-
-    FdeInfo* info = &fde_info_[current];
-    if (!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;
-    }
-    info->pc = value + 4;
-
-    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);
+    fdes->push_back(fde);
   }
 }
 
diff --git a/libunwindstack/DwarfEhFrameWithHdr.h b/libunwindstack/DwarfEhFrameWithHdr.h
index 3571166..0e5eef7 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.h
+++ b/libunwindstack/DwarfEhFrameWithHdr.h
@@ -21,7 +21,7 @@
 
 #include <unordered_map>
 
-#include "DwarfEhFrame.h"
+#include <unwindstack/DwarfSection.h>
 
 namespace unwindstack {
 
@@ -29,35 +29,47 @@
 class Memory;
 
 template <typename AddressType>
-class DwarfEhFrameWithHdr : public DwarfEhFrame<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>::fde_count_;
+  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) : DwarfEhFrame<AddressType>(memory) {}
+  DwarfEhFrameWithHdr(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
   virtual ~DwarfEhFrameWithHdr() = default;
 
-  bool Init(uint64_t offset, uint64_t size) override;
+  uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
+    return this->memory_.cur_offset() - pointer - 4;
+  }
 
-  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
+  uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
+    return this->memory_.cur_offset() - pointer - 8;
+  }
 
-  const DwarfFde* GetFdeFromIndex(size_t index) override;
+  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_;
@@ -70,6 +82,7 @@
   uint64_t entries_data_offset_;
   uint64_t cur_entries_offset_ = 0;
 
+  uint64_t fde_count_;
   std::unordered_map<uint64_t, FdeInfo> fde_info_;
 };
 
diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp
index 6ffdc0d..b505900 100644
--- a/libunwindstack/DwarfMemory.cpp
+++ b/libunwindstack/DwarfMemory.cpp
@@ -104,7 +104,6 @@
 
 bool DwarfMemory::AdjustEncodedValue(uint8_t encoding, uint64_t* value) {
   CHECK((encoding & 0x0f) == 0);
-  CHECK(encoding != DW_EH_PE_aligned);
 
   // Handle the encoding.
   switch (encoding) {
diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp
index 5bc60b9..393eb3e 100644
--- a/libunwindstack/DwarfOp.cpp
+++ b/libunwindstack/DwarfOp.cpp
@@ -32,8 +32,1451 @@
 
 namespace unwindstack {
 
+enum DwarfOpHandleFunc : uint8_t {
+  OP_ILLEGAL = 0,
+  OP_DEREF,
+  OP_DEREF_SIZE,
+  OP_PUSH,
+  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,
+  OP_EQ,
+  OP_GE,
+  OP_GT,
+  OP_LE,
+  OP_LT,
+  OP_NE,
+  OP_SKIP,
+  OP_LIT,
+  OP_REG,
+  OP_REGX,
+  OP_BREG,
+  OP_BREGX,
+  OP_NOP,
+  OP_NOT_IMPLEMENTED,
+};
+
+struct OpCallback {
+  // It may seem tempting to "clean this up" by replacing "const char[26]" with
+  // "const char*", but doing so would place the entire callback table in
+  // .data.rel.ro section, instead of .rodata section, and thus increase
+  // dirty memory usage.  Libunwindstack is used by the linker and therefore
+  // loaded for every running process, so every bit of memory counts.
+  // Unlike C standard, C++ standard guarantees this array is big enough to
+  // store the names, or else we would get a compilation error.
+  const char name[26];
+
+  // Similarily for this field, we do NOT want to directly store function
+  // pointers here. Not only would that cause the callback table to be placed
+  // in .data.rel.ro section, but it would be duplicated for each AddressType.
+  // Instead, we use DwarfOpHandleFunc enum to decouple the callback table from
+  // the function pointers.
+  DwarfOpHandleFunc handle_func;
+
+  uint8_t num_required_stack_values;
+  uint8_t num_operands;
+  uint8_t operands[2];
+};
+
+constexpr static OpCallback kCallbackTable[256] = {
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0x00 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0x01 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0x02 illegal op
+    {
+        // 0x03 DW_OP_addr
+        "DW_OP_addr",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_absptr},
+    },
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0x04 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0x05 illegal op
+    {
+        // 0x06 DW_OP_deref
+        "DW_OP_deref",
+        OP_DEREF,
+        1,
+        0,
+        {},
+    },
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0x07 illegal op
+    {
+        // 0x08 DW_OP_const1u
+        "DW_OP_const1u",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_udata1},
+    },
+    {
+        // 0x09 DW_OP_const1s
+        "DW_OP_const1s",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_sdata1},
+    },
+    {
+        // 0x0a DW_OP_const2u
+        "DW_OP_const2u",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_udata2},
+    },
+    {
+        // 0x0b DW_OP_const2s
+        "DW_OP_const2s",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_sdata2},
+    },
+    {
+        // 0x0c DW_OP_const4u
+        "DW_OP_const4u",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_udata4},
+    },
+    {
+        // 0x0d DW_OP_const4s
+        "DW_OP_const4s",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_sdata4},
+    },
+    {
+        // 0x0e DW_OP_const8u
+        "DW_OP_const8u",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_udata8},
+    },
+    {
+        // 0x0f DW_OP_const8s
+        "DW_OP_const8s",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_sdata8},
+    },
+    {
+        // 0x10 DW_OP_constu
+        "DW_OP_constu",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_uleb128},
+    },
+    {
+        // 0x11 DW_OP_consts
+        "DW_OP_consts",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x12 DW_OP_dup
+        "DW_OP_dup",
+        OP_DUP,
+        1,
+        0,
+        {},
+    },
+    {
+        // 0x13 DW_OP_drop
+        "DW_OP_drop",
+        OP_DROP,
+        1,
+        0,
+        {},
+    },
+    {
+        // 0x14 DW_OP_over
+        "DW_OP_over",
+        OP_OVER,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x15 DW_OP_pick
+        "DW_OP_pick",
+        OP_PICK,
+        0,
+        1,
+        {DW_EH_PE_udata1},
+    },
+    {
+        // 0x16 DW_OP_swap
+        "DW_OP_swap",
+        OP_SWAP,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x17 DW_OP_rot
+        "DW_OP_rot",
+        OP_ROT,
+        3,
+        0,
+        {},
+    },
+    {
+        // 0x18 DW_OP_xderef
+        "DW_OP_xderef",
+        OP_NOT_IMPLEMENTED,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x19 DW_OP_abs
+        "DW_OP_abs",
+        OP_ABS,
+        1,
+        0,
+        {},
+    },
+    {
+        // 0x1a DW_OP_and
+        "DW_OP_and",
+        OP_AND,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x1b DW_OP_div
+        "DW_OP_div",
+        OP_DIV,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x1c DW_OP_minus
+        "DW_OP_minus",
+        OP_MINUS,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x1d DW_OP_mod
+        "DW_OP_mod",
+        OP_MOD,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x1e DW_OP_mul
+        "DW_OP_mul",
+        OP_MUL,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x1f DW_OP_neg
+        "DW_OP_neg",
+        OP_NEG,
+        1,
+        0,
+        {},
+    },
+    {
+        // 0x20 DW_OP_not
+        "DW_OP_not",
+        OP_NOT,
+        1,
+        0,
+        {},
+    },
+    {
+        // 0x21 DW_OP_or
+        "DW_OP_or",
+        OP_OR,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x22 DW_OP_plus
+        "DW_OP_plus",
+        OP_PLUS,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x23 DW_OP_plus_uconst
+        "DW_OP_plus_uconst",
+        OP_PLUS_UCONST,
+        1,
+        1,
+        {DW_EH_PE_uleb128},
+    },
+    {
+        // 0x24 DW_OP_shl
+        "DW_OP_shl",
+        OP_SHL,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x25 DW_OP_shr
+        "DW_OP_shr",
+        OP_SHR,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x26 DW_OP_shra
+        "DW_OP_shra",
+        OP_SHRA,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x27 DW_OP_xor
+        "DW_OP_xor",
+        OP_XOR,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x28 DW_OP_bra
+        "DW_OP_bra",
+        OP_BRA,
+        1,
+        1,
+        {DW_EH_PE_sdata2},
+    },
+    {
+        // 0x29 DW_OP_eq
+        "DW_OP_eq",
+        OP_EQ,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x2a DW_OP_ge
+        "DW_OP_ge",
+        OP_GE,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x2b DW_OP_gt
+        "DW_OP_gt",
+        OP_GT,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x2c DW_OP_le
+        "DW_OP_le",
+        OP_LE,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x2d DW_OP_lt
+        "DW_OP_lt",
+        OP_LT,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x2e DW_OP_ne
+        "DW_OP_ne",
+        OP_NE,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x2f DW_OP_skip
+        "DW_OP_skip",
+        OP_SKIP,
+        0,
+        1,
+        {DW_EH_PE_sdata2},
+    },
+    {
+        // 0x30 DW_OP_lit0
+        "DW_OP_lit0",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x31 DW_OP_lit1
+        "DW_OP_lit1",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x32 DW_OP_lit2
+        "DW_OP_lit2",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x33 DW_OP_lit3
+        "DW_OP_lit3",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x34 DW_OP_lit4
+        "DW_OP_lit4",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x35 DW_OP_lit5
+        "DW_OP_lit5",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x36 DW_OP_lit6
+        "DW_OP_lit6",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x37 DW_OP_lit7
+        "DW_OP_lit7",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x38 DW_OP_lit8
+        "DW_OP_lit8",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x39 DW_OP_lit9
+        "DW_OP_lit9",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x3a DW_OP_lit10
+        "DW_OP_lit10",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x3b DW_OP_lit11
+        "DW_OP_lit11",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x3c DW_OP_lit12
+        "DW_OP_lit12",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x3d DW_OP_lit13
+        "DW_OP_lit13",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x3e DW_OP_lit14
+        "DW_OP_lit14",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x3f DW_OP_lit15
+        "DW_OP_lit15",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x40 DW_OP_lit16
+        "DW_OP_lit16",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x41 DW_OP_lit17
+        "DW_OP_lit17",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x42 DW_OP_lit18
+        "DW_OP_lit18",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x43 DW_OP_lit19
+        "DW_OP_lit19",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x44 DW_OP_lit20
+        "DW_OP_lit20",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x45 DW_OP_lit21
+        "DW_OP_lit21",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x46 DW_OP_lit22
+        "DW_OP_lit22",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x47 DW_OP_lit23
+        "DW_OP_lit23",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x48 DW_OP_lit24
+        "DW_OP_lit24",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x49 DW_OP_lit25
+        "DW_OP_lit25",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x4a DW_OP_lit26
+        "DW_OP_lit26",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x4b DW_OP_lit27
+        "DW_OP_lit27",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x4c DW_OP_lit28
+        "DW_OP_lit28",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x4d DW_OP_lit29
+        "DW_OP_lit29",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x4e DW_OP_lit30
+        "DW_OP_lit30",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x4f DW_OP_lit31
+        "DW_OP_lit31",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x50 DW_OP_reg0
+        "DW_OP_reg0",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x51 DW_OP_reg1
+        "DW_OP_reg1",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x52 DW_OP_reg2
+        "DW_OP_reg2",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x53 DW_OP_reg3
+        "DW_OP_reg3",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x54 DW_OP_reg4
+        "DW_OP_reg4",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x55 DW_OP_reg5
+        "DW_OP_reg5",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x56 DW_OP_reg6
+        "DW_OP_reg6",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x57 DW_OP_reg7
+        "DW_OP_reg7",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x58 DW_OP_reg8
+        "DW_OP_reg8",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x59 DW_OP_reg9
+        "DW_OP_reg9",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x5a DW_OP_reg10
+        "DW_OP_reg10",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x5b DW_OP_reg11
+        "DW_OP_reg11",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x5c DW_OP_reg12
+        "DW_OP_reg12",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x5d DW_OP_reg13
+        "DW_OP_reg13",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x5e DW_OP_reg14
+        "DW_OP_reg14",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x5f DW_OP_reg15
+        "DW_OP_reg15",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x60 DW_OP_reg16
+        "DW_OP_reg16",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x61 DW_OP_reg17
+        "DW_OP_reg17",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x62 DW_OP_reg18
+        "DW_OP_reg18",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x63 DW_OP_reg19
+        "DW_OP_reg19",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x64 DW_OP_reg20
+        "DW_OP_reg20",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x65 DW_OP_reg21
+        "DW_OP_reg21",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x66 DW_OP_reg22
+        "DW_OP_reg22",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x67 DW_OP_reg23
+        "DW_OP_reg23",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x68 DW_OP_reg24
+        "DW_OP_reg24",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x69 DW_OP_reg25
+        "DW_OP_reg25",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x6a DW_OP_reg26
+        "DW_OP_reg26",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x6b DW_OP_reg27
+        "DW_OP_reg27",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x6c DW_OP_reg28
+        "DW_OP_reg28",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x6d DW_OP_reg29
+        "DW_OP_reg29",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x6e DW_OP_reg30
+        "DW_OP_reg30",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x6f DW_OP_reg31
+        "DW_OP_reg31",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x70 DW_OP_breg0
+        "DW_OP_breg0",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x71 DW_OP_breg1
+        "DW_OP_breg1",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x72 DW_OP_breg2
+        "DW_OP_breg2",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x73 DW_OP_breg3
+        "DW_OP_breg3",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x74 DW_OP_breg4
+        "DW_OP_breg4",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x75 DW_OP_breg5
+        "DW_OP_breg5",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x76 DW_OP_breg6
+        "DW_OP_breg6",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x77 DW_OP_breg7
+        "DW_OP_breg7",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x78 DW_OP_breg8
+        "DW_OP_breg8",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x79 DW_OP_breg9
+        "DW_OP_breg9",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x7a DW_OP_breg10
+        "DW_OP_breg10",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x7b DW_OP_breg11
+        "DW_OP_breg11",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x7c DW_OP_breg12
+        "DW_OP_breg12",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x7d DW_OP_breg13
+        "DW_OP_breg13",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x7e DW_OP_breg14
+        "DW_OP_breg14",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x7f DW_OP_breg15
+        "DW_OP_breg15",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x80 DW_OP_breg16
+        "DW_OP_breg16",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x81 DW_OP_breg17
+        "DW_OP_breg17",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x82 DW_OP_breg18
+        "DW_OP_breg18",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x83 DW_OP_breg19
+        "DW_OP_breg19",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x84 DW_OP_breg20
+        "DW_OP_breg20",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x85 DW_OP_breg21
+        "DW_OP_breg21",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x86 DW_OP_breg22
+        "DW_OP_breg22",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x87 DW_OP_breg23
+        "DW_OP_breg23",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x88 DW_OP_breg24
+        "DW_OP_breg24",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x89 DW_OP_breg25
+        "DW_OP_breg25",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x8a DW_OP_breg26
+        "DW_OP_breg26",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x8b DW_OP_breg27
+        "DW_OP_breg27",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x8c DW_OP_breg28
+        "DW_OP_breg28",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x8d DW_OP_breg29
+        "DW_OP_breg29",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x8e DW_OP_breg30
+        "DW_OP_breg30",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x8f DW_OP_breg31
+        "DW_OP_breg31",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x90 DW_OP_regx
+        "DW_OP_regx",
+        OP_REGX,
+        0,
+        1,
+        {DW_EH_PE_uleb128},
+    },
+    {
+        // 0x91 DW_OP_fbreg
+        "DW_OP_fbreg",
+        OP_NOT_IMPLEMENTED,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x92 DW_OP_bregx
+        "DW_OP_bregx",
+        OP_BREGX,
+        0,
+        2,
+        {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+    },
+    {
+        // 0x93 DW_OP_piece
+        "DW_OP_piece",
+        OP_NOT_IMPLEMENTED,
+        0,
+        1,
+        {DW_EH_PE_uleb128},
+    },
+    {
+        // 0x94 DW_OP_deref_size
+        "DW_OP_deref_size",
+        OP_DEREF_SIZE,
+        1,
+        1,
+        {DW_EH_PE_udata1},
+    },
+    {
+        // 0x95 DW_OP_xderef_size
+        "DW_OP_xderef_size",
+        OP_NOT_IMPLEMENTED,
+        0,
+        1,
+        {DW_EH_PE_udata1},
+    },
+    {
+        // 0x96 DW_OP_nop
+        "DW_OP_nop",
+        OP_NOP,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x97 DW_OP_push_object_address
+        "DW_OP_push_object_address",
+        OP_NOT_IMPLEMENTED,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x98 DW_OP_call2
+        "DW_OP_call2",
+        OP_NOT_IMPLEMENTED,
+        0,
+        1,
+        {DW_EH_PE_udata2},
+    },
+    {
+        // 0x99 DW_OP_call4
+        "DW_OP_call4",
+        OP_NOT_IMPLEMENTED,
+        0,
+        1,
+        {DW_EH_PE_udata4},
+    },
+    {
+        // 0x9a DW_OP_call_ref
+        "DW_OP_call_ref",
+        OP_NOT_IMPLEMENTED,
+        0,
+        0,  // Has a different sized operand (4 bytes or 8 bytes).
+        {},
+    },
+    {
+        // 0x9b DW_OP_form_tls_address
+        "DW_OP_form_tls_address",
+        OP_NOT_IMPLEMENTED,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x9c DW_OP_call_frame_cfa
+        "DW_OP_call_frame_cfa",
+        OP_NOT_IMPLEMENTED,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x9d DW_OP_bit_piece
+        "DW_OP_bit_piece",
+        OP_NOT_IMPLEMENTED,
+        0,
+        2,
+        {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+    },
+    {
+        // 0x9e DW_OP_implicit_value
+        "DW_OP_implicit_value",
+        OP_NOT_IMPLEMENTED,
+        0,
+        1,
+        {DW_EH_PE_uleb128},
+    },
+    {
+        // 0x9f DW_OP_stack_value
+        "DW_OP_stack_value",
+        OP_NOT_IMPLEMENTED,
+        1,
+        0,
+        {},
+    },
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xa0 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xa1 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xa2 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xa3 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xa4 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xa5 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xa6 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xa7 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xa8 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xa9 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xaa illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xab illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xac illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xad illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xae illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xaf illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xb0 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xb1 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xb2 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xb3 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xb4 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xb5 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xb6 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xb7 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xb8 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xb9 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xba illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xbb illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xbc illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xbd illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xbe illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xbf illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xc0 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xc1 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xc2 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xc3 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xc4 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xc5 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xc6 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xc7 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xc8 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xc9 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xca illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xcb illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xcc illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xcd illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xce illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xcf illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xd0 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xd1 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xd2 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xd3 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xd4 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xd5 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xd6 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xd7 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xd8 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xd9 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xda illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xdb illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xdc illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xdd illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xde illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xdf illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xe0 DW_OP_lo_user
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xe1 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xe2 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xe3 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xe4 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xe5 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xe6 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xe7 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xe8 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xe9 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xea illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xeb illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xec illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xed illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xee illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xef illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xf0 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xf1 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xf2 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xf3 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xf4 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xf5 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xf6 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xf7 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xf8 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xf9 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xfa illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xfb illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xfc illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xfd illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xfe illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xff DW_OP_hi_user
+};
+
 template <typename AddressType>
-constexpr typename DwarfOp<AddressType>::OpCallback DwarfOp<AddressType>::kCallbackTable[256];
+const typename DwarfOp<AddressType>::OpHandleFuncPtr DwarfOp<AddressType>::kOpHandleFuncList[] = {
+    [OP_ILLEGAL] = nullptr,
+    [OP_DEREF] = &DwarfOp<AddressType>::op_deref,
+    [OP_DEREF_SIZE] = &DwarfOp<AddressType>::op_deref_size,
+    [OP_PUSH] = &DwarfOp<AddressType>::op_push,
+    [OP_DUP] = &DwarfOp<AddressType>::op_dup,
+    [OP_DROP] = &DwarfOp<AddressType>::op_drop,
+    [OP_OVER] = &DwarfOp<AddressType>::op_over,
+    [OP_PICK] = &DwarfOp<AddressType>::op_pick,
+    [OP_SWAP] = &DwarfOp<AddressType>::op_swap,
+    [OP_ROT] = &DwarfOp<AddressType>::op_rot,
+    [OP_ABS] = &DwarfOp<AddressType>::op_abs,
+    [OP_AND] = &DwarfOp<AddressType>::op_and,
+    [OP_DIV] = &DwarfOp<AddressType>::op_div,
+    [OP_MINUS] = &DwarfOp<AddressType>::op_minus,
+    [OP_MOD] = &DwarfOp<AddressType>::op_mod,
+    [OP_MUL] = &DwarfOp<AddressType>::op_mul,
+    [OP_NEG] = &DwarfOp<AddressType>::op_neg,
+    [OP_NOT] = &DwarfOp<AddressType>::op_not,
+    [OP_OR] = &DwarfOp<AddressType>::op_or,
+    [OP_PLUS] = &DwarfOp<AddressType>::op_plus,
+    [OP_PLUS_UCONST] = &DwarfOp<AddressType>::op_plus_uconst,
+    [OP_SHL] = &DwarfOp<AddressType>::op_shl,
+    [OP_SHR] = &DwarfOp<AddressType>::op_shr,
+    [OP_SHRA] = &DwarfOp<AddressType>::op_shra,
+    [OP_XOR] = &DwarfOp<AddressType>::op_xor,
+    [OP_BRA] = &DwarfOp<AddressType>::op_bra,
+    [OP_EQ] = &DwarfOp<AddressType>::op_eq,
+    [OP_GE] = &DwarfOp<AddressType>::op_ge,
+    [OP_GT] = &DwarfOp<AddressType>::op_gt,
+    [OP_LE] = &DwarfOp<AddressType>::op_le,
+    [OP_LT] = &DwarfOp<AddressType>::op_lt,
+    [OP_NE] = &DwarfOp<AddressType>::op_ne,
+    [OP_SKIP] = &DwarfOp<AddressType>::op_skip,
+    [OP_LIT] = &DwarfOp<AddressType>::op_lit,
+    [OP_REG] = &DwarfOp<AddressType>::op_reg,
+    [OP_REGX] = &DwarfOp<AddressType>::op_regx,
+    [OP_BREG] = &DwarfOp<AddressType>::op_breg,
+    [OP_BREGX] = &DwarfOp<AddressType>::op_bregx,
+    [OP_NOP] = &DwarfOp<AddressType>::op_nop,
+    [OP_NOT_IMPLEMENTED] = &DwarfOp<AddressType>::op_not_implemented,
+};
 
 template <typename AddressType>
 bool DwarfOp<AddressType>::Eval(uint64_t start, uint64_t end) {
@@ -97,12 +1540,13 @@
   }
 
   const auto* op = &kCallbackTable[cur_op_];
-  const auto handle_func = op->handle_func;
-  if (handle_func == nullptr) {
+  if (op->handle_func == OP_ILLEGAL) {
     last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
     return false;
   }
 
+  const auto handle_func = kOpHandleFuncList[op->handle_func];
+
   // Make sure that the required number of stack elements is available.
   if (stack_.size() < op->num_required_stack_values) {
     last_error_.code = DWARF_ERROR_STACK_INDEX_NOT_VALID;
@@ -135,7 +1579,7 @@
     std::string raw_string(android::base::StringPrintf("Raw Data: 0x%02x", cur_op));
     std::string log_string;
     const auto* op = &kCallbackTable[cur_op];
-    if (op->handle_func == nullptr) {
+    if (op->handle_func == OP_ILLEGAL) {
       log_string = "Illegal";
     } else {
       log_string = op->name;
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h
index 4c69b3d..ac9fd2d 100644
--- a/libunwindstack/DwarfOp.h
+++ b/libunwindstack/DwarfOp.h
@@ -42,14 +42,6 @@
   // Signed version of AddressType
   typedef typename std::make_signed<AddressType>::type SignedType;
 
-  struct OpCallback {
-    const char* name;
-    bool (DwarfOp::*handle_func)();
-    uint8_t num_required_stack_values;
-    uint8_t num_operands;
-    uint8_t operands[2];
-  };
-
  public:
   DwarfOp(DwarfMemory* memory, Memory* regular_memory)
       : memory_(memory), regular_memory_(regular_memory) {}
@@ -143,1342 +135,8 @@
   bool op_nop();
   bool op_not_implemented();
 
-  constexpr static OpCallback kCallbackTable[256] = {
-      {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,
-          0,
-          1,
-          {DW_EH_PE_absptr},
-      },
-      {nullptr, nullptr, 0, 0, {}},  // 0x04 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0x05 illegal op
-      {
-          // 0x06 DW_OP_deref
-          "DW_OP_deref",
-          &DwarfOp::op_deref,
-          1,
-          0,
-          {},
-      },
-      {nullptr, nullptr, 0, 0, {}},  // 0x07 illegal op
-      {
-          // 0x08 DW_OP_const1u
-          "DW_OP_const1u",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_udata1},
-      },
-      {
-          // 0x09 DW_OP_const1s
-          "DW_OP_const1s",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_sdata1},
-      },
-      {
-          // 0x0a DW_OP_const2u
-          "DW_OP_const2u",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_udata2},
-      },
-      {
-          // 0x0b DW_OP_const2s
-          "DW_OP_const2s",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_sdata2},
-      },
-      {
-          // 0x0c DW_OP_const4u
-          "DW_OP_const4u",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_udata4},
-      },
-      {
-          // 0x0d DW_OP_const4s
-          "DW_OP_const4s",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_sdata4},
-      },
-      {
-          // 0x0e DW_OP_const8u
-          "DW_OP_const8u",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_udata8},
-      },
-      {
-          // 0x0f DW_OP_const8s
-          "DW_OP_const8s",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_sdata8},
-      },
-      {
-          // 0x10 DW_OP_constu
-          "DW_OP_constu",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_uleb128},
-      },
-      {
-          // 0x11 DW_OP_consts
-          "DW_OP_consts",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x12 DW_OP_dup
-          "DW_OP_dup",
-          &DwarfOp::op_dup,
-          1,
-          0,
-          {},
-      },
-      {
-          // 0x13 DW_OP_drop
-          "DW_OP_drop",
-          &DwarfOp::op_drop,
-          1,
-          0,
-          {},
-      },
-      {
-          // 0x14 DW_OP_over
-          "DW_OP_over",
-          &DwarfOp::op_over,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x15 DW_OP_pick
-          "DW_OP_pick",
-          &DwarfOp::op_pick,
-          0,
-          1,
-          {DW_EH_PE_udata1},
-      },
-      {
-          // 0x16 DW_OP_swap
-          "DW_OP_swap",
-          &DwarfOp::op_swap,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x17 DW_OP_rot
-          "DW_OP_rot",
-          &DwarfOp::op_rot,
-          3,
-          0,
-          {},
-      },
-      {
-          // 0x18 DW_OP_xderef
-          "DW_OP_xderef",
-          &DwarfOp::op_not_implemented,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x19 DW_OP_abs
-          "DW_OP_abs",
-          &DwarfOp::op_abs,
-          1,
-          0,
-          {},
-      },
-      {
-          // 0x1a DW_OP_and
-          "DW_OP_and",
-          &DwarfOp::op_and,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x1b DW_OP_div
-          "DW_OP_div",
-          &DwarfOp::op_div,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x1c DW_OP_minus
-          "DW_OP_minus",
-          &DwarfOp::op_minus,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x1d DW_OP_mod
-          "DW_OP_mod",
-          &DwarfOp::op_mod,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x1e DW_OP_mul
-          "DW_OP_mul",
-          &DwarfOp::op_mul,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x1f DW_OP_neg
-          "DW_OP_neg",
-          &DwarfOp::op_neg,
-          1,
-          0,
-          {},
-      },
-      {
-          // 0x20 DW_OP_not
-          "DW_OP_not",
-          &DwarfOp::op_not,
-          1,
-          0,
-          {},
-      },
-      {
-          // 0x21 DW_OP_or
-          "DW_OP_or",
-          &DwarfOp::op_or,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x22 DW_OP_plus
-          "DW_OP_plus",
-          &DwarfOp::op_plus,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x23 DW_OP_plus_uconst
-          "DW_OP_plus_uconst",
-          &DwarfOp::op_plus_uconst,
-          1,
-          1,
-          {DW_EH_PE_uleb128},
-      },
-      {
-          // 0x24 DW_OP_shl
-          "DW_OP_shl",
-          &DwarfOp::op_shl,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x25 DW_OP_shr
-          "DW_OP_shr",
-          &DwarfOp::op_shr,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x26 DW_OP_shra
-          "DW_OP_shra",
-          &DwarfOp::op_shra,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x27 DW_OP_xor
-          "DW_OP_xor",
-          &DwarfOp::op_xor,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x28 DW_OP_bra
-          "DW_OP_bra",
-          &DwarfOp::op_bra,
-          1,
-          1,
-          {DW_EH_PE_sdata2},
-      },
-      {
-          // 0x29 DW_OP_eq
-          "DW_OP_eq",
-          &DwarfOp::op_eq,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x2a DW_OP_ge
-          "DW_OP_ge",
-          &DwarfOp::op_ge,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x2b DW_OP_gt
-          "DW_OP_gt",
-          &DwarfOp::op_gt,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x2c DW_OP_le
-          "DW_OP_le",
-          &DwarfOp::op_le,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x2d DW_OP_lt
-          "DW_OP_lt",
-          &DwarfOp::op_lt,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x2e DW_OP_ne
-          "DW_OP_ne",
-          &DwarfOp::op_ne,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x2f DW_OP_skip
-          "DW_OP_skip",
-          &DwarfOp::op_skip,
-          0,
-          1,
-          {DW_EH_PE_sdata2},
-      },
-      {
-          // 0x30 DW_OP_lit0
-          "DW_OP_lit0",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x31 DW_OP_lit1
-          "DW_OP_lit1",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x32 DW_OP_lit2
-          "DW_OP_lit2",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x33 DW_OP_lit3
-          "DW_OP_lit3",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x34 DW_OP_lit4
-          "DW_OP_lit4",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x35 DW_OP_lit5
-          "DW_OP_lit5",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x36 DW_OP_lit6
-          "DW_OP_lit6",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x37 DW_OP_lit7
-          "DW_OP_lit7",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x38 DW_OP_lit8
-          "DW_OP_lit8",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x39 DW_OP_lit9
-          "DW_OP_lit9",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x3a DW_OP_lit10
-          "DW_OP_lit10",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x3b DW_OP_lit11
-          "DW_OP_lit11",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x3c DW_OP_lit12
-          "DW_OP_lit12",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x3d DW_OP_lit13
-          "DW_OP_lit13",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x3e DW_OP_lit14
-          "DW_OP_lit14",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x3f DW_OP_lit15
-          "DW_OP_lit15",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x40 DW_OP_lit16
-          "DW_OP_lit16",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x41 DW_OP_lit17
-          "DW_OP_lit17",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x42 DW_OP_lit18
-          "DW_OP_lit18",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x43 DW_OP_lit19
-          "DW_OP_lit19",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x44 DW_OP_lit20
-          "DW_OP_lit20",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x45 DW_OP_lit21
-          "DW_OP_lit21",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x46 DW_OP_lit22
-          "DW_OP_lit22",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x47 DW_OP_lit23
-          "DW_OP_lit23",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x48 DW_OP_lit24
-          "DW_OP_lit24",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x49 DW_OP_lit25
-          "DW_OP_lit25",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x4a DW_OP_lit26
-          "DW_OP_lit26",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x4b DW_OP_lit27
-          "DW_OP_lit27",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x4c DW_OP_lit28
-          "DW_OP_lit28",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x4d DW_OP_lit29
-          "DW_OP_lit29",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x4e DW_OP_lit30
-          "DW_OP_lit30",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x4f DW_OP_lit31
-          "DW_OP_lit31",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x50 DW_OP_reg0
-          "DW_OP_reg0",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x51 DW_OP_reg1
-          "DW_OP_reg1",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x52 DW_OP_reg2
-          "DW_OP_reg2",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x53 DW_OP_reg3
-          "DW_OP_reg3",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x54 DW_OP_reg4
-          "DW_OP_reg4",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x55 DW_OP_reg5
-          "DW_OP_reg5",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x56 DW_OP_reg6
-          "DW_OP_reg6",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x57 DW_OP_reg7
-          "DW_OP_reg7",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x58 DW_OP_reg8
-          "DW_OP_reg8",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x59 DW_OP_reg9
-          "DW_OP_reg9",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x5a DW_OP_reg10
-          "DW_OP_reg10",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x5b DW_OP_reg11
-          "DW_OP_reg11",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x5c DW_OP_reg12
-          "DW_OP_reg12",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x5d DW_OP_reg13
-          "DW_OP_reg13",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x5e DW_OP_reg14
-          "DW_OP_reg14",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x5f DW_OP_reg15
-          "DW_OP_reg15",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x60 DW_OP_reg16
-          "DW_OP_reg16",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x61 DW_OP_reg17
-          "DW_OP_reg17",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x62 DW_OP_reg18
-          "DW_OP_reg18",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x63 DW_OP_reg19
-          "DW_OP_reg19",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x64 DW_OP_reg20
-          "DW_OP_reg20",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x65 DW_OP_reg21
-          "DW_OP_reg21",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x66 DW_OP_reg22
-          "DW_OP_reg22",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x67 DW_OP_reg23
-          "DW_OP_reg23",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x68 DW_OP_reg24
-          "DW_OP_reg24",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x69 DW_OP_reg25
-          "DW_OP_reg25",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x6a DW_OP_reg26
-          "DW_OP_reg26",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x6b DW_OP_reg27
-          "DW_OP_reg27",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x6c DW_OP_reg28
-          "DW_OP_reg28",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x6d DW_OP_reg29
-          "DW_OP_reg29",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x6e DW_OP_reg30
-          "DW_OP_reg30",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x6f DW_OP_reg31
-          "DW_OP_reg31",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x70 DW_OP_breg0
-          "DW_OP_breg0",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x71 DW_OP_breg1
-          "DW_OP_breg1",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x72 DW_OP_breg2
-          "DW_OP_breg2",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x73 DW_OP_breg3
-          "DW_OP_breg3",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x74 DW_OP_breg4
-          "DW_OP_breg4",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x75 DW_OP_breg5
-          "DW_OP_breg5",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x76 DW_OP_breg6
-          "DW_OP_breg6",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x77 DW_OP_breg7
-          "DW_OP_breg7",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x78 DW_OP_breg8
-          "DW_OP_breg8",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x79 DW_OP_breg9
-          "DW_OP_breg9",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x7a DW_OP_breg10
-          "DW_OP_breg10",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x7b DW_OP_breg11
-          "DW_OP_breg11",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x7c DW_OP_breg12
-          "DW_OP_breg12",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x7d DW_OP_breg13
-          "DW_OP_breg13",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x7e DW_OP_breg14
-          "DW_OP_breg14",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x7f DW_OP_breg15
-          "DW_OP_breg15",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x80 DW_OP_breg16
-          "DW_OP_breg16",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x81 DW_OP_breg17
-          "DW_OP_breg17",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x82 DW_OP_breg18
-          "DW_OP_breg18",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x83 DW_OP_breg19
-          "DW_OP_breg19",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x84 DW_OP_breg20
-          "DW_OP_breg20",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x85 DW_OP_breg21
-          "DW_OP_breg21",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x86 DW_OP_breg22
-          "DW_OP_breg22",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x87 DW_OP_breg23
-          "DW_OP_breg23",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x88 DW_OP_breg24
-          "DW_OP_breg24",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x89 DW_OP_breg25
-          "DW_OP_breg25",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x8a DW_OP_breg26
-          "DW_OP_breg26",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x8b DW_OP_breg27
-          "DW_OP_breg27",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x8c DW_OP_breg28
-          "DW_OP_breg28",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x8d DW_OP_breg29
-          "DW_OP_breg29",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x8e DW_OP_breg30
-          "DW_OP_breg30",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x8f DW_OP_breg31
-          "DW_OP_breg31",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x90 DW_OP_regx
-          "DW_OP_regx",
-          &DwarfOp::op_regx,
-          0,
-          1,
-          {DW_EH_PE_uleb128},
-      },
-      {
-          // 0x91 DW_OP_fbreg
-          "DW_OP_fbreg",
-          &DwarfOp::op_not_implemented,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x92 DW_OP_bregx
-          "DW_OP_bregx",
-          &DwarfOp::op_bregx,
-          0,
-          2,
-          {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
-      },
-      {
-          // 0x93 DW_OP_piece
-          "DW_OP_piece",
-          &DwarfOp::op_not_implemented,
-          0,
-          1,
-          {DW_EH_PE_uleb128},
-      },
-      {
-          // 0x94 DW_OP_deref_size
-          "DW_OP_deref_size",
-          &DwarfOp::op_deref_size,
-          1,
-          1,
-          {DW_EH_PE_udata1},
-      },
-      {
-          // 0x95 DW_OP_xderef_size
-          "DW_OP_xderef_size",
-          &DwarfOp::op_not_implemented,
-          0,
-          1,
-          {DW_EH_PE_udata1},
-      },
-      {
-          // 0x96 DW_OP_nop
-          "DW_OP_nop",
-          &DwarfOp::op_nop,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x97 DW_OP_push_object_address
-          "DW_OP_push_object_address",
-          &DwarfOp::op_not_implemented,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x98 DW_OP_call2
-          "DW_OP_call2",
-          &DwarfOp::op_not_implemented,
-          0,
-          1,
-          {DW_EH_PE_udata2},
-      },
-      {
-          // 0x99 DW_OP_call4
-          "DW_OP_call4",
-          &DwarfOp::op_not_implemented,
-          0,
-          1,
-          {DW_EH_PE_udata4},
-      },
-      {
-          // 0x9a DW_OP_call_ref
-          "DW_OP_call_ref",
-          &DwarfOp::op_not_implemented,
-          0,
-          0,  // Has a different sized operand (4 bytes or 8 bytes).
-          {},
-      },
-      {
-          // 0x9b DW_OP_form_tls_address
-          "DW_OP_form_tls_address",
-          &DwarfOp::op_not_implemented,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x9c DW_OP_call_frame_cfa
-          "DW_OP_call_frame_cfa",
-          &DwarfOp::op_not_implemented,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x9d DW_OP_bit_piece
-          "DW_OP_bit_piece",
-          &DwarfOp::op_not_implemented,
-          0,
-          2,
-          {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
-      },
-      {
-          // 0x9e DW_OP_implicit_value
-          "DW_OP_implicit_value",
-          &DwarfOp::op_not_implemented,
-          0,
-          1,
-          {DW_EH_PE_uleb128},
-      },
-      {
-          // 0x9f DW_OP_stack_value
-          "DW_OP_stack_value",
-          &DwarfOp::op_not_implemented,
-          1,
-          0,
-          {},
-      },
-      {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
-  };
+  using OpHandleFuncPtr = bool (DwarfOp::*)();
+  static const OpHandleFuncPtr kOpHandleFuncList[];
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 65eec65..849a31a 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -36,24 +36,6 @@
 
 DwarfSection::DwarfSection(Memory* memory) : memory_(memory) {}
 
-const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
-  uint64_t fde_offset;
-  if (!GetFdeOffsetFromPc(pc, &fde_offset)) {
-    return nullptr;
-  }
-  const DwarfFde* fde = 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;
-}
-
 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);
@@ -81,6 +63,314 @@
 }
 
 template <typename AddressType>
+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 (!FillInCieHeader(cie) || !FillInCie(cie)) {
+    // Erase the cached entry.
+    cie_entries_.erase(offset);
+    return nullptr;
+  }
+  return cie;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInCieHeader(DwarfCie* cie) {
+  cie->lsda_encoding = DW_EH_PE_omit;
+  uint32_t length32;
+  if (!memory_.ReadBytes(&length32, sizeof(length32))) {
+    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_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+
+    cie->cfa_instructions_end = memory_.cur_offset() + length64;
+    cie->fde_address_encoding = DW_EH_PE_sdata8;
+
+    uint64_t cie_id;
+    if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    if (cie_id != cie64_value_) {
+      // This is not a Cie, something has gone horribly wrong.
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+  } else {
+    // 32 bit Cie
+    cie->cfa_instructions_end = memory_.cur_offset() + length32;
+    cie->fde_address_encoding = DW_EH_PE_sdata4;
+
+    uint32_t cie_id;
+    if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    if (cie_id != cie32_value_) {
+      // This is not a Cie, something has gone horribly wrong.
+      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_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+
+  if (cie->version != 1 && cie->version != 3 && cie->version != 4 && cie->version != 5) {
+    // Unrecognized version.
+    last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
+    return false;
+  }
+
+  // Read the augmentation string.
+  char aug_value;
+  do {
+    if (!memory_.ReadBytes(&aug_value, 1)) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    cie->augmentation_string.push_back(aug_value);
+  } while (aug_value != '\0');
+
+  if (cie->version == 4 || cie->version == 5) {
+    // Skip the Address Size field since we only use it for validation.
+    memory_.set_cur_offset(memory_.cur_offset() + 1);
+
+    // Segment Size
+    if (!memory_.ReadBytes(&cie->segment_size, 1)) {
+      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_.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_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+
+  if (cie->version == 1) {
+    // Return Address is a single byte.
+    uint8_t return_address_register;
+    if (!memory_.ReadBytes(&return_address_register, 1)) {
+      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_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+
+  if (cie->augmentation_string[0] != 'z') {
+    cie->cfa_instructions_offset = memory_.cur_offset();
+    return true;
+  }
+
+  uint64_t aug_length;
+  if (!memory_.ReadULEB128(&aug_length)) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+  cie->cfa_instructions_offset = memory_.cur_offset() + aug_length;
+
+  for (size_t i = 1; i < cie->augmentation_string.size(); i++) {
+    switch (cie->augmentation_string[i]) {
+      case 'L':
+        if (!memory_.ReadBytes(&cie->lsda_encoding, 1)) {
+          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_.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_.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_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = memory_.cur_offset();
+          return false;
+        }
+        break;
+    }
+  }
+  return true;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromOffset(uint64_t offset) {
+  auto fde_entry = fde_entries_.find(offset);
+  if (fde_entry != fde_entries_.end()) {
+    return &fde_entry->second;
+  }
+  DwarfFde* fde = &fde_entries_[offset];
+  memory_.set_cur_offset(offset);
+  if (!FillInFdeHeader(fde) || !FillInFde(fde)) {
+    fde_entries_.erase(offset);
+    return nullptr;
+  }
+  return fde;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInFdeHeader(DwarfFde* fde) {
+  uint32_t length32;
+  if (!memory_.ReadBytes(&length32, sizeof(length32))) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+
+  if (length32 == static_cast<uint32_t>(-1)) {
+    // 64 bit Fde.
+    uint64_t length64;
+    if (!memory_.ReadBytes(&length64, sizeof(length64))) {
+      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_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    if (value64 == cie64_value_) {
+      // This is a Cie, this means something has gone wrong.
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+
+    // Get the Cie pointer, which is necessary to properly read the rest of
+    // of the Fde information.
+    fde->cie_offset = GetCieOffsetFromFde64(value64);
+  } else {
+    // 32 bit Fde.
+    fde->cfa_instructions_end = memory_.cur_offset() + length32;
+
+    uint32_t value32;
+    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_) {
+      // This is a Cie, this means something has gone wrong.
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+
+    // Get the Cie pointer, which is necessary to properly read the rest of
+    // 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 = GetCieFromOffset(fde->cie_offset);
+  if (cie == nullptr) {
+    return false;
+  }
+  fde->cie = cie;
+
+  if (cie->segment_size != 0) {
+    // Skip over the segment selector for now.
+    cur_offset += cie->segment_size;
+  }
+  memory_.set_cur_offset(cur_offset);
+
+  // 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);
+
+  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_.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_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+
+    // Set our position to after all of the augmentation data.
+    memory_.set_cur_offset(cur_offset + aug_length);
+  }
+  fde->cfa_instructions_offset = memory_.cur_offset();
+
+  return true;
+}
+
+template <typename AddressType>
 bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, Memory* regular_memory,
                                                    AddressType* value,
                                                    RegsInfo<AddressType>* regs_info,
@@ -171,6 +461,7 @@
       if (reg == eval_info->cie->return_address_register) {
         eval_info->return_address_undefined = true;
       }
+      break;
     default:
       break;
   }
@@ -260,305 +551,6 @@
 }
 
 template <typename AddressType>
-const DwarfCie* DwarfSectionImpl<AddressType>::GetCie(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)) {
-    // Erase the cached entry.
-    cie_entries_.erase(offset);
-    return nullptr;
-  }
-  return cie;
-}
-
-template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::FillInCie(DwarfCie* cie) {
-  uint32_t length32;
-  if (!memory_.ReadBytes(&length32, sizeof(length32))) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-  // Set the default for the lsda encoding.
-  cie->lsda_encoding = DW_EH_PE_omit;
-
-  if (length32 == static_cast<uint32_t>(-1)) {
-    // 64 bit Cie
-    uint64_t length64;
-    if (!memory_.ReadBytes(&length64, sizeof(length64))) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-
-    cie->cfa_instructions_end = memory_.cur_offset() + length64;
-    cie->fde_address_encoding = DW_EH_PE_sdata8;
-
-    uint64_t cie_id;
-    if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    if (cie_id != cie64_value_) {
-      // This is not a Cie, something has gone horribly wrong.
-      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
-      return false;
-    }
-  } else {
-    // 32 bit Cie
-    cie->cfa_instructions_end = memory_.cur_offset() + length32;
-    cie->fde_address_encoding = DW_EH_PE_sdata4;
-
-    uint32_t cie_id;
-    if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    if (cie_id != cie32_value_) {
-      // This is not a Cie, something has gone horribly wrong.
-      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
-      return false;
-    }
-  }
-
-  if (!memory_.ReadBytes(&cie->version, sizeof(cie->version))) {
-    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_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
-    return false;
-  }
-
-  // Read the augmentation string.
-  char aug_value;
-  do {
-    if (!memory_.ReadBytes(&aug_value, 1)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    cie->augmentation_string.push_back(aug_value);
-  } while (aug_value != '\0');
-
-  if (cie->version == 4) {
-    // Skip the Address Size field since we only use it for validation.
-    memory_.set_cur_offset(memory_.cur_offset() + 1);
-
-    // Segment Size
-    if (!memory_.ReadBytes(&cie->segment_size, 1)) {
-      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_.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_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-
-  if (cie->version == 1) {
-    // Return Address is a single byte.
-    uint8_t return_address_register;
-    if (!memory_.ReadBytes(&return_address_register, 1)) {
-      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_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-
-  if (cie->augmentation_string[0] != 'z') {
-    cie->cfa_instructions_offset = memory_.cur_offset();
-    return true;
-  }
-
-  uint64_t aug_length;
-  if (!memory_.ReadULEB128(&aug_length)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-  cie->cfa_instructions_offset = memory_.cur_offset() + aug_length;
-
-  for (size_t i = 1; i < cie->augmentation_string.size(); i++) {
-    switch (cie->augmentation_string[i]) {
-      case 'L':
-        if (!memory_.ReadBytes(&cie->lsda_encoding, 1)) {
-          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_.code = DWARF_ERROR_MEMORY_INVALID;
-          last_error_.address = memory_.cur_offset();
-          return false;
-        }
-        if (!memory_.ReadEncodedValue<AddressType>(encoding, &cie->personality_handler)) {
-          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_.code = DWARF_ERROR_MEMORY_INVALID;
-          last_error_.address = memory_.cur_offset();
-          return false;
-        }
-        break;
-    }
-  }
-  return true;
-}
-
-template <typename AddressType>
-const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromOffset(uint64_t offset) {
-  auto fde_entry = fde_entries_.find(offset);
-  if (fde_entry != fde_entries_.end()) {
-    return &fde_entry->second;
-  }
-  DwarfFde* fde = &fde_entries_[offset];
-  memory_.set_cur_offset(offset);
-  if (!FillInFde(fde)) {
-    fde_entries_.erase(offset);
-    return nullptr;
-  }
-  return fde;
-}
-
-template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::FillInFde(DwarfFde* fde) {
-  uint32_t length32;
-  if (!memory_.ReadBytes(&length32, sizeof(length32))) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-
-  if (length32 == static_cast<uint32_t>(-1)) {
-    // 64 bit Fde.
-    uint64_t length64;
-    if (!memory_.ReadBytes(&length64, sizeof(length64))) {
-      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_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    if (value64 == cie64_value_) {
-      // This is a Cie, this means something has gone wrong.
-      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
-      return false;
-    }
-
-    // Get the Cie pointer, which is necessary to properly read the rest of
-    // of the Fde information.
-    fde->cie_offset = GetCieOffsetFromFde64(value64);
-  } else {
-    // 32 bit Fde.
-    fde->cfa_instructions_end = memory_.cur_offset() + length32;
-
-    uint32_t value32;
-    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_) {
-      // This is a Cie, this means something has gone wrong.
-      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
-      return false;
-    }
-
-    // Get the Cie pointer, which is necessary to properly read the rest of
-    // of the Fde information.
-    fde->cie_offset = GetCieOffsetFromFde32(value32);
-  }
-  uint64_t cur_offset = memory_.cur_offset();
-
-  const DwarfCie* cie = GetCie(fde->cie_offset);
-  if (cie == nullptr) {
-    return false;
-  }
-  fde->cie = cie;
-
-  if (cie->segment_size != 0) {
-    // Skip over the segment selector for now.
-    cur_offset += cie->segment_size;
-  }
-  memory_.set_cur_offset(cur_offset);
-
-  if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_start)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-  fde->pc_start = AdjustPcFromFde(fde->pc_start);
-
-  if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &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_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    uint64_t cur_offset = memory_.cur_offset();
-
-    if (!memory_.ReadEncodedValue<AddressType>(cie->lsda_encoding, &fde->lsda_address)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-
-    // Set our position to after all of the augmentation data.
-    memory_.set_cur_offset(cur_offset + aug_length);
-  }
-  fde->cfa_instructions_offset = memory_.cur_offset();
-
-  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);
@@ -582,17 +574,16 @@
 }
 
 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;
   }
@@ -600,309 +591,225 @@
 }
 
 template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::Init(uint64_t offset, uint64_t size) {
+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_data_offset(offset);
   memory_.set_cur_offset(offset);
-  memory_.set_pc_offset(offset);
-
-  return CreateSortedFdeList();
-}
-
-template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::GetCieInfo(uint8_t* segment_size, uint8_t* encoding) {
-  uint8_t version;
-  if (!memory_.ReadBytes(&version, 1)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    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_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      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_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      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_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-  } while (value & 0x80);
-
-  // Skip data alignment factor
-  do {
-    if (!memory_.ReadBytes(&value, 1)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      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_.code = DWARF_ERROR_MEMORY_INVALID;
-        last_error_.address = memory_.cur_offset();
-        return false;
-      }
-    } while (value & 0x80);
-  }
-
-  // Skip the augmentation length.
-  do {
-    if (!memory_.ReadBytes(&value, 1)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      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_.code = DWARF_ERROR_MEMORY_INVALID;
-        last_error_.address = memory_.cur_offset();
-        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_.code = DWARF_ERROR_MEMORY_INVALID;
-        last_error_.address = memory_.cur_offset();
-        return false;
-      }
-      uint64_t value;
-      if (!memory_.template ReadEncodedValue<AddressType>(encoding, &value)) {
-        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-        last_error_.address = memory_.cur_offset();
-        return false;
-      }
-    }
-  }
-
-  // It should be impossible to get here.
-  abort();
-}
-
-template <typename AddressType>
-bool DwarfSectionImpl<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_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-  start = AdjustPcFromFde(start);
-
-  uint64_t length;
-  if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &length)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-  if (length != 0) {
-    fdes_.emplace_back(entry_offset, start, length);
-  }
+  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>
-bool DwarfSectionImpl<AddressType>::CreateSortedFdeList() {
-  memory_.set_cur_offset(entries_offset_);
+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);
+  }
+}
 
-  // 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() < entries_end_) {
-    uint64_t cur_entry_offset = memory_.cur_offset();
+template <typename AddressType>
+bool DwarfSectionImplNoHdr<AddressType>::GetNextCieOrFde(DwarfFde** fde_entry) {
+  uint64_t start_offset = next_entries_offset_;
 
-    // Figure out the entry length and type.
-    uint32_t value32;
+  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;
     }
 
-    uint64_t next_entry_offset;
-    if (value32 == static_cast<uint32_t>(-1)) {
-      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_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_.code = DWARF_ERROR_MEMORY_INVALID;
-        last_error_.address = memory_.cur_offset();
-        return false;
-      }
-
-      if (value64 == cie64_value_) {
-        // Cie 64 bit
-        address_encoding = DW_EH_PE_sdata8;
-        if (!GetCieInfo(&segment_size, &address_encoding)) {
-          return false;
-        }
-        cie_offset = cur_entry_offset;
-      } else {
-        uint64_t last_cie_offset = GetCieOffsetFromFde64(value64);
-        if (last_cie_offset != cie_offset) {
-          // This means that this Fde is not following the Cie.
-          last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
-          return false;
-        }
-
-        // Fde 64 bit
-        if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
-          return false;
-        }
-      }
+    if (value32 == cie32_value_) {
+      entry_is_cie = true;
+      cie_fde_encoding = DW_EH_PE_sdata4;
     } 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_.code = DWARF_ERROR_MEMORY_INVALID;
-        last_error_.address = memory_.cur_offset();
-        return false;
-      }
-
-      if (value32 == cie32_value_) {
-        // Cie 32 bit
-        address_encoding = DW_EH_PE_sdata4;
-        if (!GetCieInfo(&segment_size, &address_encoding)) {
-          return false;
-        }
-        cie_offset = cur_entry_offset;
-      } else {
-        uint64_t last_cie_offset = GetCieOffsetFromFde32(value32);
-        if (last_cie_offset != cie_offset) {
-          // This means that this Fde is not following the Cie.
-          last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
-          return false;
-        }
-
-        // Fde 32 bit
-        if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
-          return false;
-        }
-      }
+      cie_offset = this->GetCieOffsetFromFde32(value32);
     }
-
-    if (next_entry_offset < memory_.cur_offset()) {
-      // Simply consider the processing done in this case.
-      break;
-    }
-    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;
-  });
+  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;
 
-  fde_count_ = fdes_.size();
+    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>
-bool DwarfSectionImpl<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;
+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 {
-      first = current + 1;
+      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);
     }
   }
-  return false;
+
+  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* DwarfSectionImpl<AddressType>::GetFdeFromIndex(size_t index) {
-  if (index >= fdes_.size()) {
-    return nullptr;
+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;
+    }
   }
-  return this->GetFdeFromOffset(fdes_[index].offset);
+
+  // 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>;
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 3762107..3454913 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -40,7 +40,7 @@
 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) {
+bool Elf::Init() {
   load_bias_ = 0;
   if (!memory_) {
     return false;
@@ -53,12 +53,8 @@
 
   valid_ = interface_->Init(&load_bias_);
   if (valid_) {
-    interface_->InitHeaders();
-    if (init_gnu_debugdata) {
-      InitGnuDebugdata();
-    } else {
-      gnu_debugdata_interface_.reset(nullptr);
-    }
+    interface_->InitHeaders(load_bias_);
+    InitGnuDebugdata();
   } else {
     interface_.reset(nullptr);
   }
@@ -83,7 +79,7 @@
   // is in the uncompressed data.
   uint64_t load_bias;
   if (gnu->Init(&load_bias)) {
-    gnu->InitHeaders();
+    gnu->InitHeaders(load_bias);
     interface_->SetGnuDebugdataInterface(gnu);
   } else {
     // Free all of the memory associated with the gnu_debugdata section.
@@ -92,9 +88,17 @@
   }
 }
 
-bool Elf::GetSoname(std::string* name) {
+void Elf::Invalidate() {
+  interface_.reset(nullptr);
+  valid_ = false;
+}
+
+std::string Elf::GetSoname() {
   std::lock_guard<std::mutex> guard(lock_);
-  return valid_ && interface_->GetSoname(name);
+  if (!valid_) {
+    return "";
+  }
+  return interface_->GetSoname();
 }
 
 uint64_t Elf::GetRelPc(uint64_t pc, const MapInfo* map_info) {
@@ -103,9 +107,9 @@
 
 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, load_bias_, name, func_offset) ||
-                    (gnu_debugdata_interface_ && gnu_debugdata_interface_->GetFunctionName(
-                                                     addr, load_bias_, name, func_offset)));
+  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) {
@@ -139,6 +143,13 @@
   return true;
 }
 
+std::string Elf::GetBuildID() {
+  if (!valid_) {
+    return "";
+  }
+  return interface_->GetBuildID();
+}
+
 void Elf::GetLastError(ErrorData* data) {
   if (valid_) {
     *data = interface_->last_error();
@@ -149,7 +160,7 @@
   if (valid_) {
     return interface_->LastErrorCode();
   }
-  return ERROR_NONE;
+  return ERROR_INVALID_ELF;
 }
 
 uint64_t Elf::GetLastErrorAddress() {
@@ -159,22 +170,23 @@
   return 0;
 }
 
+// The relative pc expectd by this function is relative to the start of the elf.
+bool Elf::StepIfSignalHandler(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
+  if (!valid_) {
+    return false;
+  }
+  return regs->StepIfSignalHandler(rel_pc, this, process_memory);
+}
+
 // 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) {
+bool Elf::Step(uint64_t 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, load_bias_, regs, process_memory, finished);
+  return interface_->Step(rel_pc, regs, process_memory, finished);
 }
 
 bool Elf::IsValidElf(Memory* memory) {
@@ -194,33 +206,32 @@
   return true;
 }
 
-void Elf::GetInfo(Memory* memory, bool* valid, uint64_t* size) {
+bool Elf::GetInfo(Memory* memory, uint64_t* size) {
   if (!IsValidElf(memory)) {
-    *valid = false;
-    return;
+    return false;
   }
   *size = 0;
-  *valid = true;
 
-  // Now read the section header information.
   uint8_t class_type;
   if (!memory->ReadFully(EI_CLASS, &class_type, 1)) {
-    return;
+    return false;
   }
+
+  // Get the maximum size of the elf data from the header.
   if (class_type == ELFCLASS32) {
     ElfInterface32::GetMaxSize(memory, size);
   } else if (class_type == ELFCLASS64) {
     ElfInterface64::GetMaxSize(memory, size);
   } else {
-    *valid = false;
+    return false;
   }
+  return true;
 }
 
 bool Elf::IsValidPc(uint64_t pc) {
   if (!valid_ || pc < load_bias_) {
     return false;
   }
-  pc -= load_bias_;
 
   if (interface_->IsValidPc(pc)) {
     return true;
@@ -380,4 +391,22 @@
   return false;
 }
 
+std::string Elf::GetBuildID(Memory* memory) {
+  if (!IsValidElf(memory)) {
+    return "";
+  }
+
+  uint8_t class_type;
+  if (!memory->Read(EI_CLASS, &class_type, 1)) {
+    return "";
+  }
+
+  if (class_type == ELFCLASS32) {
+    return ElfInterface::ReadBuildIDFromMemory<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr>(memory);
+  } else if (class_type == ELFCLASS64) {
+    return ElfInterface::ReadBuildIDFromMemory<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr>(memory);
+  }
+  return "";
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 10afe33..f0e4138 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -29,12 +29,12 @@
 #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 "DwarfEhFrameWithHdr.h"
+#include "MemoryBuffer.h"
 #include "Symbols.h"
 
 namespace unwindstack {
@@ -87,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);
 
@@ -106,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);
@@ -124,10 +124,10 @@
 }
 
 template <typename AddressType>
-void ElfInterface::InitHeadersWithTemplate() {
+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_)) {
+    if (!eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_, load_bias)) {
       eh_frame_.reset(nullptr);
     }
   }
@@ -136,7 +136,7 @@
     // 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_)) {
+    if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_, load_bias)) {
       eh_frame_.reset(nullptr);
     }
   }
@@ -150,7 +150,7 @@
 
   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);
@@ -167,29 +167,24 @@
     return false;
   }
 
-  if (!ReadProgramHeaders<EhdrType, PhdrType>(ehdr, load_bias)) {
-    return false;
-  }
-
-  // We could still potentially unwind without the section header
-  // information, so ignore any errors.
-  if (!ReadSectionHeaders<EhdrType, ShdrType>(ehdr)) {
-    log(0, "Malformed section header found, ignoring...");
-  }
+  // If we have enough information that this is an elf file, then allow
+  // malformed program and section headers.
+  ReadProgramHeaders<EhdrType, PhdrType>(ehdr, load_bias);
+  ReadSectionHeaders<EhdrType, ShdrType>(ehdr);
   return true;
 }
 
 template <typename EhdrType, typename PhdrType>
 uint64_t ElfInterface::GetLoadBias(Memory* memory) {
   EhdrType ehdr;
-  if (!memory->Read(0, &ehdr, sizeof(ehdr))) {
+  if (!memory->ReadFully(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->Read(offset, &phdr, sizeof(phdr))) {
+    if (!memory->ReadFully(offset, &phdr, sizeof(phdr))) {
       return 0;
     }
     if (phdr.p_type == PT_LOAD && phdr.p_offset == 0) {
@@ -200,53 +195,21 @@
 }
 
 template <typename EhdrType, typename PhdrType>
-bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias) {
+void 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_->ReadField(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) {
-      last_error_.code = ERROR_MEMORY_INVALID;
-      last_error_.address =
-          offset + reinterpret_cast<uintptr_t>(&phdr.p_type) - reinterpret_cast<uintptr_t>(&phdr);
-      return false;
-    }
-
-    if (HandleType(offset, phdr.p_type, *load_bias)) {
-      continue;
+    if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
+      return;
     }
 
     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))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_flags) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       if ((phdr.p_flags & PF_X) == 0) {
         continue;
       }
 
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_vaddr) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_offset) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_memsz) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        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) {
@@ -256,53 +219,78 @@
     }
 
     case PT_GNU_EH_FRAME:
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_offset) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       // This is really the pointer to the .eh_frame_hdr section.
       eh_frame_hdr_offset_ = phdr.p_offset;
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_memsz) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       eh_frame_hdr_size_ = phdr.p_memsz;
       break;
 
     case PT_DYNAMIC:
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_offset) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       dynamic_offset_ = phdr.p_offset;
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_vaddr) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       dynamic_vaddr_ = phdr.p_vaddr;
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_memsz) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       dynamic_size_ = phdr.p_memsz;
       break;
+
+    default:
+      HandleUnknownType(phdr.p_type, phdr.p_offset, phdr.p_filesz);
+      break;
     }
   }
-  return true;
+}
+
+template <typename NhdrType>
+std::string ElfInterface::ReadBuildID() {
+  // Ensure there is no overflow in any of the calulations below.
+  uint64_t tmp;
+  if (__builtin_add_overflow(gnu_build_id_offset_, gnu_build_id_size_, &tmp)) {
+    return "";
+  }
+
+  uint64_t offset = 0;
+  while (offset < gnu_build_id_size_) {
+    if (gnu_build_id_size_ - offset < sizeof(NhdrType)) {
+      return "";
+    }
+    NhdrType hdr;
+    if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &hdr, sizeof(hdr))) {
+      return "";
+    }
+    offset += sizeof(hdr);
+
+    if (gnu_build_id_size_ - offset < hdr.n_namesz) {
+      return "";
+    }
+    if (hdr.n_namesz > 0) {
+      std::string name(hdr.n_namesz, '\0');
+      if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &(name[0]), hdr.n_namesz)) {
+        return "";
+      }
+
+      // Trim trailing \0 as GNU is stored as a C string in the ELF file.
+      if (name.back() == '\0')
+        name.resize(name.size() - 1);
+
+      // Align hdr.n_namesz to next power multiple of 4. See man 5 elf.
+      offset += (hdr.n_namesz + 3) & ~3;
+
+      if (name == "GNU" && hdr.n_type == NT_GNU_BUILD_ID) {
+        if (gnu_build_id_size_ - offset < hdr.n_descsz || hdr.n_descsz == 0) {
+          return "";
+        }
+        std::string build_id(hdr.n_descsz, '\0');
+        if (memory_->ReadFully(gnu_build_id_offset_ + offset, &build_id[0], hdr.n_descsz)) {
+          return build_id;
+        }
+        return "";
+      }
+    }
+    // Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
+    offset += (hdr.n_descsz + 3) & ~3;
+  }
+  return "";
 }
 
 template <typename EhdrType, typename ShdrType>
-bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) {
+void ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) {
   uint64_t offset = ehdr.e_shoff;
   uint64_t sec_offset = 0;
   uint64_t sec_size = 0;
@@ -313,8 +301,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;
     }
@@ -323,10 +310,8 @@
   // 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_->Read(offset, &shdr, sizeof(shdr))) {
-      last_error_.code = ERROR_MEMORY_INVALID;
-      last_error_.address = offset;
-      return false;
+    if (!memory_->ReadFully(offset, &shdr, sizeof(shdr))) {
+      return;
     }
 
     if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
@@ -334,18 +319,14 @@
       // the string terminated names.
       ShdrType str_shdr;
       if (shdr.sh_link >= ehdr.e_shnum) {
-        last_error_.code = ERROR_UNWIND_INFO;
-        return false;
+        continue;
       }
       uint64_t str_offset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize;
-      if (!memory_->Read(str_offset, &str_shdr, sizeof(str_shdr))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = str_offset;
-        return false;
+      if (!memory_->ReadFully(str_offset, &str_shdr, sizeof(str_shdr))) {
+        continue;
       }
       if (str_shdr.sh_type != SHT_STRTAB) {
-        last_error_.code = ERROR_UNWIND_INFO;
-        return false;
+        continue;
       }
       symbols_.push_back(new Symbols(shdr.sh_offset, shdr.sh_size, shdr.sh_entsize,
                                      str_shdr.sh_offset, str_shdr.sh_size));
@@ -379,19 +360,26 @@
       // 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)));
+    } else if (shdr.sh_type == SHT_NOTE) {
+      if (shdr.sh_name < sec_size) {
+        std::string name;
+        if (memory_->ReadString(sec_offset + shdr.sh_name, &name) &&
+            name == ".note.gnu.build-id") {
+          gnu_build_id_offset_ = shdr.sh_offset;
+          gnu_build_id_size_ = shdr.sh_size;
+        }
+      }
     }
   }
-  return true;
 }
 
 template <typename DynType>
-bool ElfInterface::GetSonameWithTemplate(std::string* soname) {
+std::string ElfInterface::GetSonameWithTemplate() {
   if (soname_type_ == SONAME_INVALID) {
-    return false;
+    return "";
   }
   if (soname_type_ == SONAME_VALID) {
-    *soname = soname_;
-    return true;
+    return soname_;
   }
 
   soname_type_ = SONAME_INVALID;
@@ -408,7 +396,7 @@
     if (!memory_->ReadFully(offset, &dyn, sizeof(dyn))) {
       last_error_.code = ERROR_MEMORY_INVALID;
       last_error_.address = offset;
-      return false;
+      return "";
     }
 
     if (dyn.d_tag == DT_STRTAB) {
@@ -427,28 +415,27 @@
     if (entry.first == strtab_addr) {
       soname_offset = entry.second + soname_offset;
       if (soname_offset >= entry.second + strtab_size) {
-        return false;
+        return "";
       }
       if (!memory_->ReadString(soname_offset, &soname_)) {
-        return false;
+        return "";
       }
       soname_type_ = SONAME_VALID;
-      *soname = soname_;
-      return true;
+      return soname_;
     }
   }
-  return false;
+  return "";
 }
 
 template <typename SymType>
-bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, uint64_t load_bias, std::string* name,
+bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, std::string* name,
                                                uint64_t* func_offset) {
   if (symbols_.empty()) {
     return false;
   }
 
   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;
     }
   }
@@ -469,34 +456,25 @@
   return false;
 }
 
-bool ElfInterface::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
-                        bool* finished) {
+bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
   last_error_.code = ERROR_NONE;
   last_error_.address = 0;
 
-  // Adjust the load bias to get the real relative pc.
-  if (pc < load_bias) {
-    last_error_.code = ERROR_UNWIND_INFO;
-    return false;
-  }
-  uint64_t adjusted_pc = pc - load_bias;
-
   // 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(adjusted_pc, regs, process_memory, finished)) {
+  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(adjusted_pc, regs, process_memory, finished)) {
+  if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory, finished)) {
     return true;
   }
 
-  // Finally try the gnu_debugdata interface, but always use a zero load bias.
   if (gnu_debugdata_interface_ != nullptr &&
-      gnu_debugdata_interface_->Step(pc, 0, regs, process_memory, finished)) {
+      gnu_debugdata_interface_->Step(pc, regs, process_memory, finished)) {
     return true;
   }
 
@@ -558,25 +536,127 @@
   *size = ehdr.e_shoff + ehdr.e_shentsize * ehdr.e_shnum;
 }
 
+template <typename EhdrType, typename ShdrType>
+bool GetBuildIDInfo(Memory* memory, uint64_t* build_id_offset, uint64_t* build_id_size) {
+  EhdrType ehdr;
+  if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
+    return false;
+  }
+
+  uint64_t offset = ehdr.e_shoff;
+  uint64_t sec_offset;
+  uint64_t sec_size;
+  ShdrType shdr;
+  if (ehdr.e_shstrndx >= ehdr.e_shnum) {
+    return false;
+  }
+
+  uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
+  if (!memory->ReadFully(sh_offset, &shdr, sizeof(shdr))) {
+    return false;
+  }
+  sec_offset = shdr.sh_offset;
+  sec_size = shdr.sh_size;
+
+  // 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->ReadFully(offset, &shdr, sizeof(shdr))) {
+      return false;
+    }
+    std::string name;
+    if (shdr.sh_type == SHT_NOTE && shdr.sh_name < sec_size &&
+        memory->ReadString(sec_offset + shdr.sh_name, &name) && name == ".note.gnu.build-id") {
+      *build_id_offset = shdr.sh_offset;
+      *build_id_size = shdr.sh_size;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+template <typename EhdrType, typename ShdrType, typename NhdrType>
+std::string ElfInterface::ReadBuildIDFromMemory(Memory* memory) {
+  uint64_t note_offset;
+  uint64_t note_size;
+  if (!GetBuildIDInfo<EhdrType, ShdrType>(memory, &note_offset, &note_size)) {
+    return "";
+  }
+
+  // Ensure there is no overflow in any of the calculations below.
+  uint64_t tmp;
+  if (__builtin_add_overflow(note_offset, note_size, &tmp)) {
+    return "";
+  }
+
+  uint64_t offset = 0;
+  while (offset < note_size) {
+    if (note_size - offset < sizeof(NhdrType)) {
+      return "";
+    }
+    NhdrType hdr;
+    if (!memory->ReadFully(note_offset + offset, &hdr, sizeof(hdr))) {
+      return "";
+    }
+    offset += sizeof(hdr);
+
+    if (note_size - offset < hdr.n_namesz) {
+      return "";
+    }
+    if (hdr.n_namesz > 0) {
+      std::string name(hdr.n_namesz, '\0');
+      if (!memory->ReadFully(note_offset + offset, &(name[0]), hdr.n_namesz)) {
+        return "";
+      }
+
+      // Trim trailing \0 as GNU is stored as a C string in the ELF file.
+      if (name.back() == '\0') name.resize(name.size() - 1);
+
+      // Align hdr.n_namesz to next power multiple of 4. See man 5 elf.
+      offset += (hdr.n_namesz + 3) & ~3;
+
+      if (name == "GNU" && hdr.n_type == NT_GNU_BUILD_ID) {
+        if (note_size - offset < hdr.n_descsz || hdr.n_descsz == 0) {
+          return "";
+        }
+        std::string build_id(hdr.n_descsz - 1, '\0');
+        if (memory->ReadFully(note_offset + offset, &build_id[0], hdr.n_descsz)) {
+          return build_id;
+        }
+        return "";
+      }
+    }
+    // Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
+    offset += (hdr.n_descsz + 3) & ~3;
+  }
+  return "";
+}
+
 // 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>(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&, uint64_t*);
-template bool ElfInterface::ReadProgramHeaders<Elf64_Ehdr, Elf64_Phdr>(const Elf64_Ehdr&, uint64_t*);
+template void ElfInterface::ReadProgramHeaders<Elf32_Ehdr, Elf32_Phdr>(const Elf32_Ehdr&,
+                                                                       uint64_t*);
+template void 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&);
+template void ElfInterface::ReadSectionHeaders<Elf32_Ehdr, Elf32_Shdr>(const Elf32_Ehdr&);
+template void ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf64_Ehdr&);
 
-template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
-template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
+template std::string ElfInterface::ReadBuildID<Elf32_Nhdr>();
+template std::string ElfInterface::ReadBuildID<Elf64_Nhdr>();
 
-template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, uint64_t, std::string*,
+template std::string ElfInterface::GetSonameWithTemplate<Elf32_Dyn>();
+template std::string ElfInterface::GetSonameWithTemplate<Elf64_Dyn>();
+
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, std::string*,
                                                                    uint64_t*);
-template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, uint64_t, std::string*,
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*,
                                                                    uint64_t*);
 
 template bool ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(const std::string&, uint64_t*);
@@ -588,4 +668,9 @@
 template uint64_t ElfInterface::GetLoadBias<Elf32_Ehdr, Elf32_Phdr>(Memory*);
 template uint64_t ElfInterface::GetLoadBias<Elf64_Ehdr, Elf64_Phdr>(Memory*);
 
+template std::string ElfInterface::ReadBuildIDFromMemory<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr>(
+    Memory*);
+template std::string ElfInterface::ReadBuildIDFromMemory<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr>(
+    Memory*);
+
 }  // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index f93baeb..3dd5d54 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -26,6 +26,14 @@
 
 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;
@@ -79,41 +87,35 @@
 #define PT_ARM_EXIDX 0x70000001
 #endif
 
-bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type, uint64_t load_bias) {
+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;
-  }
-  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, uint64_t load_bias, Regs* regs, Memory* process_memory,
-                           bool* finished) {
+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, load_bias, regs, process_memory, finished) ||
-         StepExidx(pc, load_bias, regs, process_memory, finished);
+  return ElfInterface32::Step(pc, regs, process_memory, finished) ||
+         StepExidx(pc, regs, process_memory, finished);
 }
 
-bool ElfInterfaceArm::StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
-                                bool* finished) {
+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) {
+  if (pc < load_bias_) {
     last_error_.code = ERROR_UNWIND_INFO;
     return false;
   }
-  pc -= load_bias;
+  pc -= load_bias_;
 
   RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
   uint64_t entry_offset;
@@ -167,13 +169,12 @@
   return return_value;
 }
 
-bool ElfInterfaceArm::GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
-                                      uint64_t* offset) {
+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, load_bias, name, offset)) {
+  if (ElfInterface32::GetFunctionName(addr | 1, name, offset)) {
     *offset &= ~1;
     return true;
   }
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index c1597ce..4c3a0c3 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -64,28 +64,30 @@
   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, uint64_t load_bias) override;
+  void HandleUnknownType(uint32_t type, uint64_t ph_offset, uint64_t ph_filesz) override;
 
-  bool Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
-            bool* finished) override;
+  bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) override;
 
-  bool StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
-                 bool* finished);
+  bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);
 
-  bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
-                       uint64_t* offset) override;
+  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) override;
 
   uint64_t start_offset() { return start_offset_; }
 
   size_t total_entries() { return total_entries_; }
 
+  void set_load_bias(uint64_t load_bias) { load_bias_ = load_bias; }
+
  protected:
   uint64_t start_offset_ = 0;
   size_t total_entries_ = 0;
+  uint64_t load_bias_ = 0;
 
   std::unordered_map<size_t, uint32_t> addrs_;
 };
diff --git a/libunwindstack/Global.cpp b/libunwindstack/Global.cpp
new file mode 100644
index 0000000..a20be00
--- /dev/null
+++ b/libunwindstack/Global.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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 <string.h>
+#include <sys/mman.h>
+
+#include <string>
+#include <vector>
+
+#include <unwindstack/Global.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+Global::Global(std::shared_ptr<Memory>& memory) : memory_(memory) {}
+Global::Global(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
+    : memory_(memory), search_libs_(search_libs) {}
+
+void Global::SetArch(ArchEnum arch) {
+  if (arch_ == ARCH_UNKNOWN) {
+    arch_ = arch;
+    ProcessArch();
+  }
+}
+
+uint64_t Global::GetVariableOffset(MapInfo* info, const std::string& variable) {
+  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) {
+      return 0;
+    }
+  }
+
+  Elf* elf = info->GetElf(memory_, arch());
+  uint64_t ptr;
+  // Find first non-empty list (libraries might be loaded multiple times).
+  if (elf->GetGlobalVariable(variable, &ptr) && ptr != 0) {
+    return ptr + info->start;
+  }
+  return 0;
+}
+
+void Global::FindAndReadVariable(Maps* maps, const char* var_str) {
+  std::string variable(var_str);
+  // When looking for global variables, do not arbitrarily search every
+  // readable map. Instead look for a specific pattern that must exist.
+  // The pattern should be a readable map, followed by a read-write
+  // map with a non-zero offset.
+  // For example:
+  //   f0000-f1000 0 r-- /system/lib/libc.so
+  //   f1000-f2000 1000 r-x /system/lib/libc.so
+  //   f2000-f3000 2000 rw- /system/lib/libc.so
+  // This also works:
+  //   f0000-f2000 0 r-- /system/lib/libc.so
+  //   f2000-f3000 2000 rw- /system/lib/libc.so
+  MapInfo* map_start = nullptr;
+  for (const auto& info : *maps) {
+    if (map_start != nullptr) {
+      if (map_start->name == info->name) {
+        if (info->offset != 0 &&
+            (info->flags & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE)) {
+          uint64_t ptr = GetVariableOffset(map_start, variable);
+          if (ptr != 0 && ReadVariableData(ptr)) {
+            break;
+          } else {
+            // Failed to find the global variable, do not bother trying again.
+            map_start = nullptr;
+          }
+        }
+      } else {
+        map_start = nullptr;
+      }
+    }
+    if (map_start == nullptr && (info->flags & PROT_READ) && info->offset == 0 &&
+        !info->name.empty()) {
+      map_start = info.get();
+    }
+  }
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp
index 0c319ec..8a85607 100644
--- a/libunwindstack/JitDebug.cpp
+++ b/libunwindstack/JitDebug.cpp
@@ -23,7 +23,8 @@
 #include <unwindstack/Elf.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
+
+#include "MemoryRange.h"
 
 // This implements the JIT Compilation Interface.
 // See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html
@@ -69,10 +70,10 @@
   uint64_t first_entry;
 };
 
-JitDebug::JitDebug(std::shared_ptr<Memory>& memory) : memory_(memory) {}
+JitDebug::JitDebug(std::shared_ptr<Memory>& memory) : Global(memory) {}
 
 JitDebug::JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
-    : memory_(memory), search_libs_(search_libs) {}
+    : Global(memory, search_libs) {}
 
 JitDebug::~JitDebug() {
   for (auto* elf : elf_list_) {
@@ -141,8 +142,8 @@
   return code.next;
 }
 
-void JitDebug::SetArch(ArchEnum arch) {
-  switch (arch) {
+void JitDebug::ProcessArch() {
+  switch (arch()) {
     case ARCH_X86:
       read_descriptor_func_ = &JitDebug::ReadDescriptor32;
       read_entry_func_ = &JitDebug::ReadEntry32Pack;
@@ -165,6 +166,11 @@
   }
 }
 
+bool JitDebug::ReadVariableData(uint64_t ptr) {
+  entry_addr_ = (this->*read_descriptor_func_)(ptr);
+  return entry_addr_ != 0;
+}
+
 void JitDebug::Init(Maps* maps) {
   if (initialized_) {
     return;
@@ -172,37 +178,7 @@
   // 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;
-      }
-    }
-  }
+  FindAndReadVariable(maps, "__jit_debug_descriptor");
 }
 
 Elf* JitDebug::GetElf(Maps* maps, uint64_t pc) {
@@ -226,7 +202,7 @@
     entry_addr_ = (this->*read_entry_func_)(&start, &size);
 
     Elf* elf = new Elf(new MemoryRange(memory_, start, size, 0));
-    elf->Init(true);
+    elf->Init();
     if (!elf->valid()) {
       // The data is not formatted in a way we understand, do not attempt
       // to process any other entries.
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
new file mode 100644
index 0000000..5d81200
--- /dev/null
+++ b/libunwindstack/LocalUnwinder.cpp
@@ -0,0 +1,145 @@
+/*
+ * 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());
+  ArchEnum arch = regs->Arch();
+
+  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_, arch);
+    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;
+
+    bool finished = false;
+    if (elf->StepIfSignalHandler(rel_pc, regs.get(), process_memory_.get())) {
+      step_pc = rel_pc;
+    } else if (!elf->Step(step_pc, regs.get(), process_memory_.get(), &finished)) {
+      finished = true;
+    }
+
+    // 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 (finished || frame_info->size() == max_frames ||
+        (cur_pc == regs->pc() && cur_sp == regs->sp())) {
+      break;
+    }
+    adjust_pc = true;
+  }
+  return num_frames != 0;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 39378a3..5b30a4d 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -22,13 +22,43 @@
 #include <mutex>
 #include <string>
 
+#include <android-base/stringprintf.h>
+
 #include <unwindstack/Elf.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
+
+#include "MemoryFileAtOffset.h"
+#include "MemoryRange.h"
 
 namespace unwindstack {
 
+bool MapInfo::InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory) {
+  // One last attempt, see if the previous map is read-only with the
+  // same name and stretches across this map.
+  if (prev_map == nullptr || prev_map->flags != PROT_READ) {
+    return false;
+  }
+
+  uint64_t map_size = end - prev_map->end;
+  if (!memory->Init(name, prev_map->offset, map_size)) {
+    return false;
+  }
+
+  uint64_t max_size;
+  if (!Elf::GetInfo(memory, &max_size) || max_size < map_size) {
+    return false;
+  }
+
+  if (!memory->Init(name, prev_map->offset, max_size)) {
+    return false;
+  }
+
+  elf_offset = offset - prev_map->offset;
+  elf_start_offset = prev_map->offset;
+  return true;
+}
+
 Memory* MapInfo::GetFileMemory() {
   std::unique_ptr<MemoryFileAtOffset> memory(new MemoryFileAtOffset);
   if (offset == 0) {
@@ -38,8 +68,12 @@
     return nullptr;
   }
 
-  // There are two possibilities when the offset is non-zero.
-  // - There is an elf file embedded in a file.
+  // These are the possibilities when the offset is non-zero.
+  // - There is an elf file embedded in a file, and the offset is the
+  //   the start of the elf in the file.
+  // - There is an elf file embedded in a file, and the offset is the
+  //   the start of the executable part of the file. The actual start
+  //   of the elf is in the read-only segment preceeding this map.
   // - 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
@@ -53,29 +87,49 @@
     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();
+  // Check if the start of this map is an embedded elf.
+  uint64_t max_size = 0;
+  if (Elf::GetInfo(memory.get(), &max_size)) {
+    elf_start_offset = offset;
+    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();
+      }
+      elf_start_offset = 0;
+      return nullptr;
     }
-    return nullptr;
+    return memory.release();
   }
 
-  if (max_size > map_size) {
-    if (memory->Init(name, offset, max_size)) {
-      return memory.release();
+  // No elf at offset, try to init as if the whole file is an elf.
+  if (memory->Init(name, 0) && Elf::IsValidElf(memory.get())) {
+    elf_offset = offset;
+    // Need to check how to set the elf start offset. If this map is not
+    // the r-x map of a r-- map, then use the real offset value. Otherwise,
+    // use 0.
+    if (prev_map == nullptr || prev_map->offset != 0 || prev_map->flags != PROT_READ ||
+        prev_map->name != name) {
+      elf_start_offset = offset;
     }
-    // Try to reinit using the default map_size.
-    if (memory->Init(name, offset, map_size)) {
-      return memory.release();
-    }
-    return nullptr;
+    return memory.release();
   }
-  return memory.release();
+
+  // See if the map previous to this one contains a read-only map
+  // that represents the real start of the elf data.
+  if (InitFileMemoryFromPreviousReadOnlyMap(memory.get())) {
+    return memory.release();
+  }
+
+  // Failed to find elf at start of file or at read-only map, return
+  // file object from the current map.
+  if (memory->Init(name, offset, map_size)) {
+    return memory.release();
+  }
+  return nullptr;
 }
 
 Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
@@ -98,51 +152,116 @@
     }
   }
 
-  // If the map isn't readable, don't bother trying to read from process memory.
-  if (!(flags & PROT_READ)) {
+  if (process_memory == nullptr) {
     return nullptr;
   }
-  return new MemoryRange(process_memory, start, end - start, 0);
+
+  // Need to verify that this elf is valid. It's possible that
+  // only part of the elf file to be mapped into memory is in the executable
+  // map. In this case, there will be another read-only map that includes the
+  // first part of the elf file. This is done if the linker rosegment
+  // option is used.
+  std::unique_ptr<MemoryRange> memory(new MemoryRange(process_memory, start, end - start, 0));
+  if (Elf::IsValidElf(memory.get())) {
+    memory_backed_elf = true;
+    return memory.release();
+  }
+
+  // Find the read-only map by looking at the previous map. The linker
+  // doesn't guarantee that this invariant will always be true. However,
+  // if that changes, there is likely something else that will change and
+  // break something.
+  if (offset == 0 || name.empty() || prev_map == nullptr || prev_map->name != name ||
+      prev_map->offset >= offset) {
+    return nullptr;
+  }
+
+  // Make sure that relative pc values are corrected properly.
+  elf_offset = offset - prev_map->offset;
+  // Use this as the elf start offset, otherwise, you always get offsets into
+  // the r-x section, which is not quite the right information.
+  elf_start_offset = prev_map->offset;
+
+  MemoryRanges* ranges = new MemoryRanges;
+  ranges->Insert(
+      new MemoryRange(process_memory, prev_map->start, prev_map->end - prev_map->start, 0));
+  ranges->Insert(new MemoryRange(process_memory, start, end - start, elf_offset));
+
+  memory_backed_elf = true;
+  return ranges;
 }
 
-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_);
+Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch) {
+  {
+    // 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();
+    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();
+      }
+    }
+
+    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();
+    if (elf->valid() && expected_arch != elf->arch()) {
+      // Make the elf invalid, mismatch between arch and expected arch.
+      elf->Invalidate();
+    }
+
+    if (locked) {
+      Elf::CacheAdd(this);
+      Elf::CacheUnlock();
+    }
   }
 
-  Memory* memory = CreateMemory(process_memory);
-  if (locked) {
-    if (Elf::CacheAfterCreateMemory(this)) {
-      delete memory;
-      Elf::CacheUnlock();
-      return elf.get();
+  if (!elf->valid()) {
+    elf_start_offset = offset;
+  } else if (prev_map != nullptr && elf_start_offset != offset &&
+             prev_map->offset == elf_start_offset && prev_map->name == name) {
+    // If there is a read-only map then a read-execute map that represents the
+    // same elf object, make sure the previous map is using the same elf
+    // object if it hasn't already been set.
+    std::lock_guard<std::mutex> guard(prev_map->mutex_);
+    if (prev_map->elf.get() == nullptr) {
+      prev_map->elf = elf;
+      prev_map->memory_backed_elf = memory_backed_elf;
     }
   }
-  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();
 }
 
+bool MapInfo::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
+  {
+    // Make sure no other thread is trying to update this elf object.
+    std::lock_guard<std::mutex> guard(mutex_);
+    if (elf == nullptr) {
+      return false;
+    }
+  }
+  // No longer need the lock, once the elf object is created, it is not deleted
+  // until this object is deleted.
+  return elf->GetFunctionName(addr, name, func_offset);
+}
+
 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)) {
@@ -172,4 +291,61 @@
   return cur_load_bias;
 }
 
+MapInfo::~MapInfo() {
+  uintptr_t id = build_id.load();
+  if (id != 0) {
+    delete reinterpret_cast<std::string*>(id);
+  }
+}
+
+std::string MapInfo::GetBuildID() {
+  uintptr_t id = build_id.load();
+  if (id != 0) {
+    return *reinterpret_cast<std::string*>(id);
+  }
+
+  // No need to lock, at worst if multiple threads do this at the same
+  // time it should be detected and only one thread should win and
+  // save the data.
+  std::unique_ptr<std::string> cur_build_id(new std::string);
+
+  // Now need to see if the elf object exists.
+  // Make sure no other thread is trying to add the elf to this map.
+  mutex_.lock();
+  Elf* elf_obj = elf.get();
+  mutex_.unlock();
+  if (elf_obj != nullptr) {
+    *cur_build_id = elf_obj->GetBuildID();
+  } else {
+    // This will only work if we can get the file associated with this memory.
+    // If this is only available in memory, then the section name information
+    // is not present and we will not be able to find the build id info.
+    std::unique_ptr<Memory> memory(GetFileMemory());
+    if (memory != nullptr) {
+      *cur_build_id = Elf::GetBuildID(memory.get());
+    }
+  }
+
+  id = reinterpret_cast<uintptr_t>(cur_build_id.get());
+  uintptr_t expected_id = 0;
+  if (build_id.compare_exchange_weak(expected_id, id)) {
+    // Value saved, so make sure the memory is not freed.
+    cur_build_id.release();
+  }
+  return *reinterpret_cast<std::string*>(id);
+}
+
+std::string MapInfo::GetPrintableBuildID() {
+  std::string raw_build_id = GetBuildID();
+  if (raw_build_id.empty()) {
+    return "";
+  }
+  std::string printable_build_id;
+  for (const char& c : raw_build_id) {
+    // Use %hhx to avoid sign extension on abis that have signed chars.
+    printable_build_id += android::base::StringPrintf("%02hhx", c);
+  }
+  return printable_build_id;
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 4c16212..5da73e4 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -19,12 +19,15 @@
 #include <inttypes.h>
 #include <stdint.h>
 #include <stdio.h>
+#include <string.h>
 #include <sys/mman.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include <android-base/unique_fd.h>
+#include <procinfo/process_map.h>
 
+#include <algorithm>
 #include <cctype>
 #include <memory>
 #include <string>
@@ -44,9 +47,9 @@
   size_t last = maps_.size();
   while (first < last) {
     size_t index = (first + last) / 2;
-    MapInfo* cur = maps_[index];
+    const auto& cur = maps_[index];
     if (pc >= cur->start && pc < cur->end) {
-      return cur;
+      return cur.get();
     } else if (pc < cur->start) {
       last = index;
     } else {
@@ -56,190 +59,127 @@
   return nullptr;
 }
 
-// Assumes that line does not end in '\n'.
-static MapInfo* InternalParseLine(const char* line) {
-  // Do not use a sscanf implementation since it is not performant.
-
-  // Example linux /proc/<pid>/maps lines:
-  // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /system/lib/libcomposer.so
-  char* str;
-  const char* old_str = line;
-  uint64_t start = strtoull(old_str, &str, 16);
-  if (old_str == str || *str++ != '-') {
-    return nullptr;
-  }
-
-  old_str = str;
-  uint64_t end = strtoull(old_str, &str, 16);
-  if (old_str == str || !std::isspace(*str++)) {
-    return nullptr;
-  }
-
-  while (std::isspace(*str)) {
-    str++;
-  }
-
-  // Parse permissions data.
-  if (*str == '\0') {
-    return nullptr;
-  }
-  uint16_t flags = 0;
-  if (*str == 'r') {
-    flags |= PROT_READ;
-  } else if (*str != '-') {
-    return nullptr;
-  }
-  str++;
-  if (*str == 'w') {
-    flags |= PROT_WRITE;
-  } else if (*str != '-') {
-    return nullptr;
-  }
-  str++;
-  if (*str == 'x') {
-    flags |= PROT_EXEC;
-  } else if (*str != '-') {
-    return nullptr;
-  }
-  str++;
-  if (*str != 'p' && *str != 's') {
-    return nullptr;
-  }
-  str++;
-
-  if (!std::isspace(*str++)) {
-    return nullptr;
-  }
-
-  old_str = str;
-  uint64_t offset = strtoull(old_str, &str, 16);
-  if (old_str == str || !std::isspace(*str)) {
-    return nullptr;
-  }
-
-  // Ignore the 00:00 values.
-  old_str = str;
-  (void)strtoull(old_str, &str, 16);
-  if (old_str == str || *str++ != ':') {
-    return nullptr;
-  }
-  if (std::isspace(*str)) {
-    return nullptr;
-  }
-
-  // Skip the inode.
-  old_str = str;
-  (void)strtoull(str, &str, 16);
-  if (old_str == str || !std::isspace(*str++)) {
-    return nullptr;
-  }
-
-  // Skip decimal digit.
-  old_str = str;
-  (void)strtoull(old_str, &str, 10);
-  if (old_str == str || (!std::isspace(*str) && *str != '\0')) {
-    return nullptr;
-  }
-
-  while (std::isspace(*str)) {
-    str++;
-  }
-  if (*str == '\0') {
-    return new MapInfo(start, end, offset, flags, "");
-  }
-
-  // Save the name data.
-  std::string name(str);
-
-  // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
-  if (name.substr(0, 5) == "/dev/" && name.substr(5, 7) != "ashmem/") {
-    flags |= MAPS_FLAGS_DEVICE_MAP;
-  }
-  return new MapInfo(start, end, offset, flags, name);
-}
-
 bool Maps::Parse() {
-  int fd = open(GetMapsFile().c_str(), O_RDONLY | O_CLOEXEC);
-  if (fd == -1) {
-    return false;
-  }
-
-  bool return_value = true;
-  char buffer[2048];
-  size_t leftover = 0;
-  while (true) {
-    ssize_t bytes = read(fd, &buffer[leftover], 2048 - leftover);
-    if (bytes == -1) {
-      return_value = false;
-      break;
-    }
-    if (bytes == 0) {
-      break;
-    }
-    bytes += leftover;
-    char* line = buffer;
-    while (bytes > 0) {
-      char* newline = static_cast<char*>(memchr(line, '\n', bytes));
-      if (newline == nullptr) {
-        memmove(buffer, line, bytes);
-        break;
-      }
-      *newline = '\0';
-
-      MapInfo* map_info = InternalParseLine(line);
-      if (map_info == nullptr) {
-        return_value = false;
-        break;
-      }
-      maps_.push_back(map_info);
-
-      bytes -= newline - line + 1;
-      line = newline + 1;
-    }
-    leftover = bytes;
-  }
-  close(fd);
-  return return_value;
+  return android::procinfo::ReadMapFile(
+      GetMapsFile(),
+      [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t, 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_.emplace_back(
+            new MapInfo(maps_.empty() ? nullptr : maps_.back().get(), start, end, pgoff,
+                        flags, name));
+      });
 }
 
 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);
+  auto map_info =
+      std::make_unique<MapInfo>(maps_.empty() ? nullptr : maps_.back().get(), start, end, offset,
+                                flags, name);
   map_info->load_bias = load_bias;
-  maps_.push_back(map_info);
+  maps_.emplace_back(std::move(map_info));
 }
 
-Maps::~Maps() {
-  for (auto& map : maps_) {
-    delete map;
+void Maps::Sort() {
+  std::sort(maps_.begin(), maps_.end(),
+            [](const std::unique_ptr<MapInfo>& a, const std::unique_ptr<MapInfo>& b) {
+              return a->start < b->start; });
+
+  // Set the prev_map values on the info objects.
+  MapInfo* prev_map = nullptr;
+  for (const auto& map_info : maps_) {
+    map_info->prev_map = prev_map;
+    prev_map = map_info.get();
   }
 }
 
 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 {
-      line = std::string(start_of_line, end_of_line - start_of_line);
-      end_of_line++;
-    }
-
-    MapInfo* map_info = InternalParseLine(line.c_str());
-    if (map_info == nullptr) {
-      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, ino_t, 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_.emplace_back(
+            new MapInfo(maps_.empty() ? nullptr : maps_.back().get(), start, end, pgoff,
+                        flags, name));
+      });
 }
 
 const std::string RemoteMaps::GetMapsFile() const {
   return "/proc/" + std::to_string(pid_) + "/maps";
 }
 
+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()) {
+    maps_.resize(last_map_idx);
+    return false;
+  }
+
+  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++) {
+    auto& 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++) {
+      auto& 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;
+        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_.emplace_back(std::move(info));
+      maps_[old_map_idx] = nullptr;
+      total_entries--;
+    }
+    if (search_map_idx >= last_map_idx) {
+      break;
+    }
+  }
+
+  // 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_.emplace_back(std::move(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;
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index beb2aad..a66cd5b 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -16,6 +16,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <string.h>
 #include <sys/mman.h>
 #include <sys/ptrace.h>
 #include <sys/stat.h>
@@ -31,6 +32,14 @@
 #include <unwindstack/Memory.h>
 
 #include "Check.h"
+#include "MemoryBuffer.h"
+#include "MemoryCache.h"
+#include "MemoryFileAtOffset.h"
+#include "MemoryLocal.h"
+#include "MemoryOffline.h"
+#include "MemoryOfflineBuffer.h"
+#include "MemoryRange.h"
+#include "MemoryRemote.h"
 
 namespace unwindstack {
 
@@ -167,6 +176,16 @@
   return false;
 }
 
+std::unique_ptr<Memory> Memory::CreateFileMemory(const std::string& path, uint64_t offset) {
+  auto memory = std::make_unique<MemoryFileAtOffset>();
+
+  if (memory->Init(path, offset)) {
+    return memory;
+  }
+
+  return nullptr;
+}
+
 std::shared_ptr<Memory> Memory::CreateProcessMemory(pid_t pid) {
   if (pid == getpid()) {
     return std::shared_ptr<Memory>(new MemoryLocal());
@@ -174,6 +193,18 @@
   return std::shared_ptr<Memory>(new MemoryRemote(pid));
 }
 
+std::shared_ptr<Memory> Memory::CreateProcessMemoryCached(pid_t pid) {
+  if (pid == getpid()) {
+    return std::shared_ptr<Memory>(new MemoryCache(new MemoryLocal()));
+  }
+  return std::shared_ptr<Memory>(new MemoryCache(new MemoryRemote(pid)));
+}
+
+std::shared_ptr<Memory> Memory::CreateOfflineMemory(const uint8_t* data, uint64_t start,
+                                                    uint64_t end) {
+  return std::shared_ptr<Memory>(new MemoryOfflineBuffer(data, start, end));
+}
+
 size_t MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
   if (addr >= raw_.size()) {
     return 0;
@@ -316,6 +347,18 @@
   return memory_->Read(read_addr, dst, read_length);
 }
 
+void MemoryRanges::Insert(MemoryRange* memory) {
+  maps_.emplace(memory->offset() + memory->length(), memory);
+}
+
+size_t MemoryRanges::Read(uint64_t addr, void* dst, size_t size) {
+  auto entry = maps_.upper_bound(addr);
+  if (entry != maps_.end()) {
+    return entry->second->Read(addr, dst, size);
+  }
+  return 0;
+}
+
 bool MemoryOffline::Init(const std::string& file, uint64_t offset) {
   auto memory_file = std::make_shared<MemoryFileAtOffset>();
   if (!memory_file->Init(file, offset)) {
@@ -386,4 +429,50 @@
   return 0;
 }
 
+size_t MemoryCache::Read(uint64_t addr, void* dst, size_t size) {
+  // Only bother caching and looking at the cache if this is a small read for now.
+  if (size > 64) {
+    return impl_->Read(addr, dst, size);
+  }
+
+  uint64_t addr_page = addr >> kCacheBits;
+  auto entry = cache_.find(addr_page);
+  uint8_t* cache_dst;
+  if (entry != cache_.end()) {
+    cache_dst = entry->second;
+  } else {
+    cache_dst = cache_[addr_page];
+    if (!impl_->ReadFully(addr_page << kCacheBits, cache_dst, kCacheSize)) {
+      // Erase the entry.
+      cache_.erase(addr_page);
+      return impl_->Read(addr, dst, size);
+    }
+  }
+  size_t max_read = ((addr_page + 1) << kCacheBits) - addr;
+  if (size <= max_read) {
+    memcpy(dst, &cache_dst[addr & kCacheMask], size);
+    return size;
+  }
+
+  // The read crossed into another cached entry, since a read can only cross
+  // into one extra cached page, duplicate the code rather than looping.
+  memcpy(dst, &cache_dst[addr & kCacheMask], max_read);
+  dst = &reinterpret_cast<uint8_t*>(dst)[max_read];
+  addr_page++;
+
+  entry = cache_.find(addr_page);
+  if (entry != cache_.end()) {
+    cache_dst = entry->second;
+  } else {
+    cache_dst = cache_[addr_page];
+    if (!impl_->ReadFully(addr_page << kCacheBits, cache_dst, kCacheSize)) {
+      // Erase the entry.
+      cache_.erase(addr_page);
+      return impl_->Read(addr_page << kCacheBits, dst, size - max_read) + max_read;
+    }
+  }
+  memcpy(dst, cache_dst, size - max_read);
+  return size;
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/MemoryBuffer.h b/libunwindstack/MemoryBuffer.h
new file mode 100644
index 0000000..3fe4bbb
--- /dev/null
+++ b/libunwindstack/MemoryBuffer.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_BUFFER_H
+#define _LIBUNWINDSTACK_MEMORY_BUFFER_H
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+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_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MEMORY_BUFFER_H
diff --git a/libunwindstack/MemoryCache.h b/libunwindstack/MemoryCache.h
new file mode 100644
index 0000000..769d907
--- /dev/null
+++ b/libunwindstack/MemoryCache.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_CACHE_H
+#define _LIBUNWINDSTACK_MEMORY_CACHE_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryCache : public Memory {
+ public:
+  MemoryCache(Memory* memory) : impl_(memory) {}
+  virtual ~MemoryCache() = default;
+
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+  void Clear() override { cache_.clear(); }
+
+ private:
+  constexpr static size_t kCacheBits = 12;
+  constexpr static size_t kCacheMask = (1 << kCacheBits) - 1;
+  constexpr static size_t kCacheSize = 1 << kCacheBits;
+  std::unordered_map<uint64_t, uint8_t[kCacheSize]> cache_;
+
+  std::unique_ptr<Memory> impl_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MEMORY_CACHE_H
diff --git a/libunwindstack/MemoryFileAtOffset.h b/libunwindstack/MemoryFileAtOffset.h
new file mode 100644
index 0000000..d136eb4
--- /dev/null
+++ b/libunwindstack/MemoryFileAtOffset.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_FILE_AT_OFFSET_H
+#define _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
+
+#include <stdint.h>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+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() override;
+
+ protected:
+  size_t size_ = 0;
+  size_t offset_ = 0;
+  uint8_t* data_ = nullptr;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
diff --git a/libunwindstack/MemoryLocal.h b/libunwindstack/MemoryLocal.h
new file mode 100644
index 0000000..29aaf12
--- /dev/null
+++ b/libunwindstack/MemoryLocal.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_LOCAL_H
+#define _LIBUNWINDSTACK_MEMORY_LOCAL_H
+
+#include <stdint.h>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryLocal : public Memory {
+ public:
+  MemoryLocal() = default;
+  virtual ~MemoryLocal() = default;
+
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MEMORY_LOCAL_H
diff --git a/libunwindstack/MemoryOffline.h b/libunwindstack/MemoryOffline.h
new file mode 100644
index 0000000..789f1a2
--- /dev/null
+++ b/libunwindstack/MemoryOffline.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_OFFLINE_H
+#define _LIBUNWINDSTACK_MEMORY_OFFLINE_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Memory.h>
+
+#include "MemoryRange.h"
+
+namespace unwindstack {
+
+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 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_OFFLINE_H
diff --git a/libunwindstack/MemoryOfflineBuffer.h b/libunwindstack/MemoryOfflineBuffer.h
new file mode 100644
index 0000000..64c49a1
--- /dev/null
+++ b/libunwindstack/MemoryOfflineBuffer.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_OFFLINE_BUFFER_H
+#define _LIBUNWINDSTACK_MEMORY_OFFLINE_BUFFER_H
+
+#include <stdint.h>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+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_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MEMORY_OFFLINE_BUFFER_H
diff --git a/libunwindstack/MemoryRange.h b/libunwindstack/MemoryRange.h
new file mode 100644
index 0000000..3b4ab5c
--- /dev/null
+++ b/libunwindstack/MemoryRange.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_RANGE_H
+#define _LIBUNWINDSTACK_MEMORY_RANGE_H
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// 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;
+
+  uint64_t offset() { return offset_; }
+  uint64_t length() { return length_; }
+
+ private:
+  std::shared_ptr<Memory> memory_;
+  uint64_t begin_;
+  uint64_t length_;
+  uint64_t offset_;
+};
+
+class MemoryRanges : public Memory {
+ public:
+  MemoryRanges() = default;
+  virtual ~MemoryRanges() = default;
+
+  void Insert(MemoryRange* memory);
+
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+  std::map<uint64_t, std::unique_ptr<MemoryRange>> maps_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MEMORY_RANGE_H
diff --git a/libunwindstack/MemoryRemote.h b/libunwindstack/MemoryRemote.h
new file mode 100644
index 0000000..db367d6
--- /dev/null
+++ b/libunwindstack/MemoryRemote.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_REMOTE_H
+#define _LIBUNWINDSTACK_MEMORY_REMOTE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <atomic>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+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_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MEMORY_REMOTE_H
diff --git a/libunwindstack/RegsArm.cpp b/libunwindstack/RegsArm.cpp
index de22bde..885dc94 100644
--- a/libunwindstack/RegsArm.cpp
+++ b/libunwindstack/RegsArm.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <stdint.h>
+#include <string.h>
 
 #include <functional>
 
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
index a68f6e0..e9787aa 100644
--- a/libunwindstack/RegsArm64.cpp
+++ b/libunwindstack/RegsArm64.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <stdint.h>
+#include <string.h>
 
 #include <functional>
 
diff --git a/libunwindstack/RegsInfo.h b/libunwindstack/RegsInfo.h
index 47825f5..e445a91 100644
--- a/libunwindstack/RegsInfo.h
+++ b/libunwindstack/RegsInfo.h
@@ -25,11 +25,13 @@
 
 template <typename AddressType>
 struct RegsInfo {
+  static constexpr size_t MAX_REGISTERS = 64;
+
   RegsInfo(RegsImpl<AddressType>* regs) : regs(regs) {}
 
   RegsImpl<AddressType>* regs = nullptr;
   uint64_t saved_reg_map = 0;
-  AddressType saved_regs[64];
+  AddressType saved_regs[MAX_REGISTERS];
 
   inline AddressType Get(uint32_t reg) {
     if (IsSaved(reg)) {
@@ -39,23 +41,23 @@
   }
 
   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.
+    if (reg >= MAX_REGISTERS) {
+      // This should never happen since all currently supported
+      // architectures have < 64 total registers.
       abort();
     }
-    saved_reg_map |= 1 << reg;
+    saved_reg_map |= 1ULL << 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.
+    if (reg > MAX_REGISTERS) {
+      // This should never happen since all currently supported
+      // architectures have < 64 total registers.
       abort();
     }
-    return saved_reg_map & (1 << reg);
+    return saved_reg_map & (1ULL << reg);
   }
 
   inline uint16_t Total() { return regs->total_regs(); }
diff --git a/libunwindstack/RegsMips.cpp b/libunwindstack/RegsMips.cpp
index 2e6908c..f330fe0 100644
--- a/libunwindstack/RegsMips.cpp
+++ b/libunwindstack/RegsMips.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <stdint.h>
+#include <string.h>
 
 #include <functional>
 
@@ -134,7 +135,7 @@
   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))) {
+  if (!elf_memory->ReadFully(rel_pc, &data, sizeof(data))) {
     return false;
   }
 
@@ -159,7 +160,7 @@
 
   // 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))) {
+  if (!process_memory->ReadFully(regs_[MIPS_REG_SP] + offset, values, sizeof(values))) {
     return false;
   }
 
diff --git a/libunwindstack/RegsMips64.cpp b/libunwindstack/RegsMips64.cpp
index 0b835a1..3f67d92 100644
--- a/libunwindstack/RegsMips64.cpp
+++ b/libunwindstack/RegsMips64.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <stdint.h>
+#include <string.h>
 
 #include <functional>
 
diff --git a/libunwindstack/RegsX86_64.cpp b/libunwindstack/RegsX86_64.cpp
index ebad3f4..74cd1cb 100644
--- a/libunwindstack/RegsX86_64.cpp
+++ b/libunwindstack/RegsX86_64.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <stdint.h>
+#include <string.h>
 
 #include <functional>
 
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
index 25def40..e3c15a2 100644
--- a/libunwindstack/Symbols.cpp
+++ b/libunwindstack/Symbols.cpp
@@ -17,6 +17,7 @@
 #include <elf.h>
 #include <stdint.h>
 
+#include <algorithm>
 #include <string>
 
 #include <unwindstack/Memory.h>
@@ -54,10 +55,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) {
@@ -81,9 +79,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.
@@ -134,8 +129,8 @@
 }
 
 // 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*);
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
index 7d239c1..7fcd067 100644
--- a/libunwindstack/Symbols.h
+++ b/libunwindstack/Symbols.h
@@ -44,8 +44,7 @@
   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);
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 98ab30f..c95f852 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -25,10 +25,15 @@
 #include <algorithm>
 
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <demangle.h>
 
 #include <unwindstack/Elf.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
 #include <unwindstack/Unwinder.h>
 
 #if !defined(NO_LIBDEXFILE_SUPPORT)
@@ -58,10 +63,16 @@
   if (info != nullptr) {
     frame->map_start = info->start;
     frame->map_end = info->end;
-    frame->map_offset = info->offset;
+    // Since this is a dex file frame, the elf_start_offset is not set
+    // by any of the normal code paths. Use the offset of the map since
+    // that matches the actual offset.
+    frame->map_elf_start_offset = info->offset;
+    frame->map_exact_offset = info->offset;
     frame->map_load_bias = info->load_bias;
     frame->map_flags = info->flags;
-    frame->map_name = info->name;
+    if (resolve_names_) {
+      frame->map_name = info->name;
+    }
     frame->rel_pc = dex_pc - info->start;
   } else {
     frame->rel_pc = dex_pc;
@@ -82,8 +93,8 @@
 #endif
 }
 
-void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t func_pc,
-                           uint64_t pc_adjustment) {
+FrameData* Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc,
+                                 uint64_t pc_adjustment) {
   size_t frame_num = frames_.size();
   frames_.resize(frame_num + 1);
   FrameData* frame = &frames_.at(frame_num);
@@ -93,21 +104,26 @@
   frame->pc = regs_->pc() - pc_adjustment;
 
   if (map_info == nullptr) {
-    return;
+    // Nothing else to update.
+    return nullptr;
   }
 
-  frame->map_name = map_info->name;
-  frame->map_offset = map_info->offset;
+  if (resolve_names_) {
+    frame->map_name = map_info->name;
+    if (embedded_soname_ && map_info->elf_start_offset != 0 && !frame->map_name.empty()) {
+      std::string soname = elf->GetSoname();
+      if (!soname.empty()) {
+        frame->map_name += '!' + soname;
+      }
+    }
+  }
+  frame->map_elf_start_offset = map_info->elf_start_offset;
+  frame->map_exact_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;
-  }
+  return frame;
 }
 
 static bool ShouldStop(const std::vector<std::string>* map_suffixes_to_ignore,
@@ -129,35 +145,48 @@
   frames_.clear();
   last_error_.code = ERROR_NONE;
   last_error_.address = 0;
+  elf_from_memory_not_file_ = false;
+
+  ArchEnum arch = regs_->Arch();
 
   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 rel_pc;
     uint64_t pc_adjustment = 0;
     uint64_t step_pc;
+    uint64_t rel_pc;
     Elf* elf;
     if (map_info == nullptr) {
-      rel_pc = regs_->pc();
-      step_pc = rel_pc;
+      step_pc = regs_->pc();
+      rel_pc = step_pc;
       last_error_.code = ERROR_INVALID_MAP;
     } else {
       if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
         break;
       }
-      elf = map_info->GetElf(process_memory_, true);
-      rel_pc = elf->GetRelPc(regs_->pc(), map_info);
+      elf = map_info->GetElf(process_memory_, arch);
+      // If this elf is memory backed, and there is a valid file, then set
+      // an indicator that we couldn't open the file.
+      if (!elf_from_memory_not_file_ && map_info->memory_backed_elf && !map_info->name.empty() &&
+          map_info->name[0] != '[' && !android::base::StartsWith(map_info->name, "/memfd:")) {
+        elf_from_memory_not_file_ = 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 = rel_pc - pc_adjustment;
+      step_pc -= pc_adjustment;
 
       // If the pc is in an invalid elf file, try and get an Elf object
       // using the jit debug information.
@@ -172,6 +201,7 @@
       }
     }
 
+    FrameData* frame = nullptr;
     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()) {
@@ -180,25 +210,29 @@
         FillInDexFrame();
         // Clear the dex pc so that we don't repeat this frame later.
         regs_->set_dex_pc(0);
+
+        // Make sure there is enough room for the real frame.
+        if (frames_.size() == max_frames_) {
+          last_error_.code = ERROR_MAX_FRAMES_EXCEEDED;
+          break;
+        }
       }
 
-      FillInFrame(map_info, elf, rel_pc, step_pc, pc_adjustment);
+      frame = FillInFrame(map_info, elf, rel_pc, pc_adjustment);
 
       // Once a frame is added, stop skipping frames.
       initial_map_names_to_skip = nullptr;
     }
     adjust_pc = true;
 
-    bool stepped;
+    bool stepped = false;
     bool in_device_map = false;
-    if (map_info == nullptr) {
-      stepped = false;
-    } else {
+    bool finished = false;
+    if (map_info != nullptr) {
       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());
@@ -206,23 +240,47 @@
           // 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 (elf->StepIfSignalHandler(rel_pc, regs_, process_memory_.get())) {
+            stepped = true;
+            if (frame != nullptr) {
+              // Need to adjust the relative pc because the signal handler
+              // pc should not be adjusted.
+              frame->rel_pc = rel_pc;
+              frame->pc += pc_adjustment;
+              step_pc = rel_pc;
+            }
+          } else if (elf->Step(step_pc, regs_, process_memory_.get(), &finished)) {
+            stepped = true;
           }
+          elf->GetLastError(&last_error_);
         }
       }
     }
 
+    if (frame != nullptr) {
+      if (!resolve_names_ ||
+          !elf->GetFunctionName(step_pc, &frame->function_name, &frame->function_offset)) {
+        frame->function_name = "";
+        frame->function_offset = 0;
+      }
+    }
+
+    if (finished) {
+      break;
+    }
+
     if (!stepped) {
       if (return_address_attempt) {
-        // Remove the speculative frame.
-        frames_.pop_back();
+        // Only remove the speculative frame if there are more than two frames
+        // or the pc in the first frame is in a valid map.
+        // This allows for a case where the code jumps into the middle of
+        // nowhere, but there is no other unwind information after that.
+        if (frames_.size() > 2 || (frames_.size() > 0 && maps_->Find(frames_[0].pc) != nullptr)) {
+          // 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
@@ -250,26 +308,14 @@
   }
 }
 
-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 Unwinder::FormatFrame(const FrameData& frame) {
   std::string data;
-
-  if (is32bit) {
+  if (regs_->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>";
@@ -278,16 +324,36 @@
   } else {
     data += android::base::StringPrintf("  <anonymous:%" PRIx64 ">", frame.map_start);
   }
+
+  if (frame.map_elf_start_offset != 0) {
+    data += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", frame.map_elf_start_offset);
+  }
+
   if (!frame.function_name.empty()) {
-    data += " (" + frame.function_name;
+    data += " (" + demangle(frame.function_name.c_str());
     if (frame.function_offset != 0) {
       data += android::base::StringPrintf("+%" PRId64, frame.function_offset);
     }
     data += ')';
   }
+
+  MapInfo* map_info = maps_->Find(frame.map_start);
+  if (map_info != nullptr && display_build_id_) {
+    std::string build_id = map_info->GetPrintableBuildID();
+    if (!build_id.empty()) {
+      data += " (BuildId: " + build_id + ')';
+    }
+  }
   return data;
 }
 
+std::string Unwinder::FormatFrame(size_t frame_num) {
+  if (frame_num >= frames_.size()) {
+    return "";
+  }
+  return FormatFrame(frames_[frame_num]);
+}
+
 void Unwinder::SetJitDebug(JitDebug* jit_debug, ArchEnum arch) {
   jit_debug->SetArch(arch);
   jit_debug_ = jit_debug;
@@ -300,4 +366,29 @@
 }
 #endif
 
+bool UnwinderFromPid::Init(ArchEnum arch) {
+  if (pid_ == getpid()) {
+    maps_ptr_.reset(new LocalMaps());
+  } else {
+    maps_ptr_.reset(new RemoteMaps(pid_));
+  }
+  if (!maps_ptr_->Parse()) {
+    return false;
+  }
+  maps_ = maps_ptr_.get();
+
+  process_memory_ = Memory::CreateProcessMemoryCached(pid_);
+
+  jit_debug_ptr_.reset(new JitDebug(process_memory_));
+  jit_debug_ = jit_debug_ptr_.get();
+  SetJitDebug(jit_debug_, arch);
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+  dex_files_ptr_.reset(new DexFiles(process_memory_));
+  dex_files_ = dex_files_ptr_.get();
+  SetDexFiles(dex_files_, arch);
+#endif
+
+  return true;
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/benchmarks/unwind_benchmarks.cpp b/libunwindstack/benchmarks/unwind_benchmarks.cpp
new file mode 100644
index 0000000..de9137a
--- /dev/null
+++ b/libunwindstack/benchmarks/unwind_benchmarks.cpp
@@ -0,0 +1,145 @@
+/*
+ * 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 <memory>
+
+#include <benchmark/benchmark.h>
+
+#include <android-base/strings.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+#include <unwindstack/Unwinder.h>
+
+size_t Call6(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+  std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
+  unwindstack::RegsGetLocal(regs.get());
+  unwindstack::Unwinder unwinder(32, maps, regs.get(), process_memory);
+  unwinder.Unwind();
+  return unwinder.NumFrames();
+}
+
+size_t Call5(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+  return Call6(process_memory, maps);
+}
+
+size_t Call4(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+  return Call5(process_memory, maps);
+}
+
+size_t Call3(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+  return Call4(process_memory, maps);
+}
+
+size_t Call2(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+  return Call3(process_memory, maps);
+}
+
+size_t Call1(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+  return Call2(process_memory, maps);
+}
+
+static void BM_uncached_unwind(benchmark::State& state) {
+  auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid());
+  unwindstack::LocalMaps maps;
+  if (!maps.Parse()) {
+    state.SkipWithError("Failed to parse local maps.");
+  }
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(Call1(process_memory, &maps));
+  }
+}
+BENCHMARK(BM_uncached_unwind);
+
+static void BM_cached_unwind(benchmark::State& state) {
+  auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
+  unwindstack::LocalMaps maps;
+  if (!maps.Parse()) {
+    state.SkipWithError("Failed to parse local maps.");
+  }
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(Call1(process_memory, &maps));
+  }
+}
+BENCHMARK(BM_cached_unwind);
+
+static void Initialize(benchmark::State& state, unwindstack::Maps& maps,
+                       unwindstack::MapInfo** build_id_map_info) {
+  if (!maps.Parse()) {
+    state.SkipWithError("Failed to parse local maps.");
+    return;
+  }
+
+  // Find the libc.so share library and use that for benchmark purposes.
+  *build_id_map_info = nullptr;
+  for (auto& map_info : maps) {
+    if (map_info->offset == 0 && map_info->GetBuildID() != "") {
+      *build_id_map_info = map_info.get();
+      break;
+    }
+  }
+
+  if (*build_id_map_info == nullptr) {
+    state.SkipWithError("Failed to find a map with a BuildID.");
+  }
+}
+
+static void BM_get_build_id_from_elf(benchmark::State& state) {
+  unwindstack::LocalMaps maps;
+  unwindstack::MapInfo* build_id_map_info;
+  Initialize(state, maps, &build_id_map_info);
+
+  unwindstack::Elf* elf = build_id_map_info->GetElf(std::shared_ptr<unwindstack::Memory>(),
+                                                    unwindstack::Regs::CurrentArch());
+  if (!elf->valid()) {
+    state.SkipWithError("Cannot get valid elf from map.");
+  }
+
+  for (auto _ : state) {
+    uintptr_t id = build_id_map_info->build_id;
+    if (id != 0) {
+      delete reinterpret_cast<std::string*>(id);
+      build_id_map_info->build_id = 0;
+    }
+    benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
+  }
+}
+BENCHMARK(BM_get_build_id_from_elf);
+
+static void BM_get_build_id_from_file(benchmark::State& state) {
+  unwindstack::LocalMaps maps;
+  unwindstack::MapInfo* build_id_map_info;
+  Initialize(state, maps, &build_id_map_info);
+
+  for (auto _ : state) {
+    uintptr_t id = build_id_map_info->build_id;
+    if (id != 0) {
+      delete reinterpret_cast<std::string*>(id);
+      build_id_map_info->build_id = 0;
+    }
+    benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
+  }
+}
+BENCHMARK(BM_get_build_id_from_file);
+
+BENCHMARK_MAIN();
diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h
index 26f5d35..67a9640 100644
--- a/libunwindstack/include/unwindstack/DexFiles.h
+++ b/libunwindstack/include/unwindstack/DexFiles.h
@@ -25,28 +25,28 @@
 #include <unordered_map>
 #include <vector>
 
+#include <unwindstack/Global.h>
+#include <unwindstack/Memory.h>
+
 namespace unwindstack {
 
 // Forward declarations.
 class DexFile;
 class Maps;
 struct MapInfo;
-class Memory;
 enum ArchEnum : uint8_t;
 
-class DexFiles {
+class DexFiles : public Global {
  public:
   explicit DexFiles(std::shared_ptr<Memory>& memory);
   DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
-  ~DexFiles();
+  virtual ~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);
 
@@ -60,12 +60,13 @@
 
   bool ReadEntry64();
 
-  std::shared_ptr<Memory> memory_;
-  std::vector<std::string> search_libs_;
+  bool ReadVariableData(uint64_t ptr_offset) override;
+
+  void ProcessArch() override;
 
   std::mutex lock_;
   bool initialized_ = false;
-  std::unordered_map<uint64_t, DexFile*> files_;
+  std::unordered_map<uint64_t, std::unique_ptr<DexFile>> files_;
 
   uint64_t entry_addr_ = 0;
   uint64_t (DexFiles::*read_entry_ptr_func_)(uint64_t) = nullptr;
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index 209c54a..e9942de 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -43,7 +43,12 @@
 
   class iterator : public std::iterator<std::bidirectional_iterator_tag, DwarfFde*> {
    public:
-    iterator(DwarfSection* section, size_t index) : section_(section), index_(index) {}
+    iterator(DwarfSection* section, size_t index) : index_(index) {
+      section->GetFdes(&fdes_);
+      if (index_ == static_cast<size_t>(-1)) {
+        index_ = fdes_.size();
+      }
+    }
 
     iterator& operator++() {
       index_++;
@@ -65,32 +70,31 @@
     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_); }
+    const DwarfFde* operator*() {
+      if (index_ > fdes_.size()) return nullptr;
+      return fdes_[index_];
+    }
 
    private:
-    DwarfSection* section_ = nullptr;
+    std::vector<const DwarfFde*> fdes_;
     size_t index_ = 0;
   };
 
   iterator begin() { return iterator(this, 0); }
-  iterator end() { return iterator(this, fde_count_); }
+  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) = 0;
+  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 GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) = 0;
+  virtual bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) = 0;
 
-  virtual bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) = 0;
+  virtual void GetFdes(std::vector<const DwarfFde*>* fdes) = 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 const DwarfFde* GetFdeFromPc(uint64_t pc) = 0;
 
   virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) = 0;
 
@@ -109,7 +113,6 @@
   uint32_t cie32_value_ = 0;
   uint64_t cie64_value_ = 0;
 
-  uint64_t fde_count_ = 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_;
@@ -119,52 +122,73 @@
 template <typename AddressType>
 class DwarfSectionImpl : public DwarfSection {
  public:
-  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;
-  };
-
   DwarfSectionImpl(Memory* memory) : DwarfSection(memory) {}
   virtual ~DwarfSectionImpl() = default;
 
-  bool Init(uint64_t offset, uint64_t size) override;
+  const DwarfCie* GetCieFromOffset(uint64_t offset);
 
-  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
-
-  const DwarfFde* GetFdeFromIndex(size_t index) override;
+  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;
 
-  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;
+  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);
 
-  bool GetCieInfo(uint8_t* segment_size, uint8_t* encoding);
+  uint64_t load_bias_ = 0;
+  uint64_t entries_offset_ = 0;
+  uint64_t entries_end_ = 0;
+  uint64_t pc_offset_ = 0;
+};
 
-  bool AddFdeInfo(uint64_t entry_offset, uint8_t segment_size, uint8_t encoding);
+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_;
 
-  bool CreateSortedFdeList();
+  DwarfSectionImplNoHdr(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+  virtual ~DwarfSectionImplNoHdr() = default;
 
-  std::vector<FdeInfo> fdes_;
-  uint64_t entries_offset_;
-  uint64_t entries_end_;
+  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
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index f4cdbda..56bf318 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -53,11 +53,13 @@
   Elf(Memory* memory) : memory_(memory) {}
   virtual ~Elf() = default;
 
-  bool Init(bool init_gnu_debugdata);
+  bool Init();
 
   void InitGnuDebugdata();
 
-  bool GetSoname(std::string* name);
+  void Invalidate();
+
+  std::string GetSoname();
 
   bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
 
@@ -65,11 +67,14 @@
 
   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);
+  bool StepIfSignalHandler(uint64_t rel_pc, Regs* regs, Memory* process_memory);
+
+  bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
 
   ElfInterface* CreateInterfaceFromMemory(Memory* memory);
 
+  std::string GetBuildID();
+
   uint64_t GetLoadBias() { return load_bias_; }
 
   bool IsValidPc(uint64_t pc);
@@ -94,10 +99,12 @@
 
   static bool IsValidElf(Memory* memory);
 
-  static void GetInfo(Memory* memory, bool* valid, uint64_t* size);
+  static bool GetInfo(Memory* memory, uint64_t* size);
 
   static uint64_t GetLoadBias(Memory* memory);
 
+  static std::string GetBuildID(Memory* memory);
+
   static void SetCachingEnabled(bool enable);
   static bool CachingEnabled() { return cache_enabled_; }
 
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 3a221bc..dbd917d 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -54,17 +54,17 @@
 
   virtual bool Init(uint64_t* load_bias) = 0;
 
-  virtual void InitHeaders() = 0;
+  virtual void InitHeaders(uint64_t load_bias) = 0;
 
-  virtual bool GetSoname(std::string* name) = 0;
+  virtual std::string GetSoname() = 0;
 
-  virtual bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
-                               uint64_t* offset) = 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, uint64_t load_bias, Regs* regs, Memory* process_memory,
-                    bool* finished);
+  virtual std::string GetBuildID() = 0;
+
+  virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
 
   virtual bool IsValidPc(uint64_t pc);
 
@@ -87,6 +87,8 @@
   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_; }
+  uint64_t gnu_build_id_offset() { return gnu_build_id_offset_; }
+  uint64_t gnu_build_id_size() { return gnu_build_id_size_; }
 
   DwarfSection* eh_frame() { return eh_frame_.get(); }
   DwarfSection* debug_frame() { return debug_frame_.get(); }
@@ -98,34 +100,39 @@
   template <typename EhdrType, typename PhdrType>
   static uint64_t GetLoadBias(Memory* memory);
 
+  template <typename EhdrType, typename ShdrType, typename NhdrType>
+  static std::string ReadBuildIDFromMemory(Memory* memory);
+
  protected:
   template <typename AddressType>
-  void InitHeadersWithTemplate();
+  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);
+  void ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias);
 
   template <typename EhdrType, typename ShdrType>
-  bool ReadSectionHeaders(const EhdrType& ehdr);
+  void ReadSectionHeaders(const EhdrType& ehdr);
 
   template <typename DynType>
-  bool GetSonameWithTemplate(std::string* soname);
+  std::string GetSonameWithTemplate();
 
   template <typename SymType>
-  bool GetFunctionNameWithTemplate(uint64_t addr, uint64_t load_bias, std::string* name,
-                                   uint64_t* func_offset);
+  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 bool HandleType(uint64_t, uint32_t, uint64_t) { return false; }
+  virtual void HandleUnknownType(uint32_t, uint64_t, uint64_t) {}
 
   template <typename EhdrType>
   static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);
 
+  template <typename NhdrType>
+  std::string ReadBuildID();
+
   Memory* memory_;
   std::unordered_map<uint64_t, LoadInfo> pt_loads_;
 
@@ -146,6 +153,9 @@
   uint64_t gnu_debugdata_offset_ = 0;
   uint64_t gnu_debugdata_size_ = 0;
 
+  uint64_t gnu_build_id_offset_ = 0;
+  uint64_t gnu_build_id_size_ = 0;
+
   uint8_t soname_type_ = SONAME_UNKNOWN;
   std::string soname_;
 
@@ -169,21 +179,22 @@
     return ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(load_bias);
   }
 
-  void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint32_t>(); }
-
-  bool GetSoname(std::string* soname) override {
-    return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(soname);
+  void InitHeaders(uint64_t load_bias) override {
+    ElfInterface::InitHeadersWithTemplate<uint32_t>(load_bias);
   }
 
-  bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
-                       uint64_t* func_offset) override {
-    return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, load_bias, name, func_offset);
+  std::string GetSoname() override { return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(); }
+
+  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);
   }
 
+  std::string GetBuildID() override { return ElfInterface::ReadBuildID<Elf32_Nhdr>(); }
+
   static void GetMaxSize(Memory* memory, uint64_t* size) {
     GetMaxSizeWithTemplate<Elf32_Ehdr>(memory, size);
   }
@@ -198,21 +209,22 @@
     return ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(load_bias);
   }
 
-  void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint64_t>(); }
-
-  bool GetSoname(std::string* soname) override {
-    return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(soname);
+  void InitHeaders(uint64_t load_bias) override {
+    ElfInterface::InitHeadersWithTemplate<uint64_t>(load_bias);
   }
 
-  bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
-                       uint64_t* func_offset) override {
-    return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, load_bias, name, func_offset);
+  std::string GetSoname() override { return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(); }
+
+  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);
   }
 
+  std::string GetBuildID() override { return ElfInterface::ReadBuildID<Elf64_Nhdr>(); }
+
   static void GetMaxSize(Memory* memory, uint64_t* size) {
     GetMaxSizeWithTemplate<Elf64_Ehdr>(memory, size);
   }
diff --git a/libunwindstack/include/unwindstack/Error.h b/libunwindstack/include/unwindstack/Error.h
index 6ed0e0f..72ec454 100644
--- a/libunwindstack/include/unwindstack/Error.h
+++ b/libunwindstack/include/unwindstack/Error.h
@@ -29,6 +29,7 @@
   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.
+  ERROR_INVALID_ELF,          // Unwind in an invalid elf.
 };
 
 struct ErrorData {
diff --git a/libunwindstack/include/unwindstack/Global.h b/libunwindstack/include/unwindstack/Global.h
new file mode 100644
index 0000000..a7e6c15
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Global.h
@@ -0,0 +1,63 @@
+/*
+ * 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_GLOBAL_H
+#define _LIBUNWINDSTACK_GLOBAL_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Maps;
+struct MapInfo;
+
+class Global {
+ public:
+  explicit Global(std::shared_ptr<Memory>& memory);
+  Global(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
+  virtual ~Global() = default;
+
+  void SetArch(ArchEnum arch);
+
+  ArchEnum arch() { return arch_; }
+
+ protected:
+  uint64_t GetVariableOffset(MapInfo* info, const std::string& variable);
+  void FindAndReadVariable(Maps* maps, const char* variable);
+
+  virtual bool ReadVariableData(uint64_t offset) = 0;
+
+  virtual void ProcessArch() = 0;
+
+  ArchEnum arch_ = ARCH_UNKNOWN;
+
+  std::shared_ptr<Memory> memory_;
+  std::vector<std::string> search_libs_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_GLOBAL_H
diff --git a/libunwindstack/include/unwindstack/JitDebug.h b/libunwindstack/include/unwindstack/JitDebug.h
index 0bcd0b0..8b7b4b5 100644
--- a/libunwindstack/include/unwindstack/JitDebug.h
+++ b/libunwindstack/include/unwindstack/JitDebug.h
@@ -24,35 +24,27 @@
 #include <string>
 #include <vector>
 
+#include <unwindstack/Global.h>
+#include <unwindstack/Memory.h>
+
 namespace unwindstack {
 
 // Forward declarations.
 class Elf;
 class Maps;
-class Memory;
 enum ArchEnum : uint8_t;
 
-class JitDebug {
+class JitDebug : public Global {
  public:
   explicit JitDebug(std::shared_ptr<Memory>& memory);
   JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
-  ~JitDebug();
+  virtual ~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;
 
@@ -62,6 +54,16 @@
   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);
+
+  bool ReadVariableData(uint64_t ptr_offset) override;
+
+  void ProcessArch() override;
+
+  uint64_t entry_addr_ = 0;
+  bool initialized_ = false;
+  std::vector<Elf*> elf_list_;
+
+  std::mutex lock_;
 };
 
 }  // namespace unwindstack
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/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index a57fe68..13ce10f 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -28,20 +28,30 @@
 
 namespace unwindstack {
 
-// Forward declarations.
-class Memory;
+class MemoryFileAtOffset;
 
 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 std::string& name)
+  MapInfo(MapInfo* map_info, 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() = default;
+        prev_map(map_info),
+        load_bias(static_cast<uint64_t>(-1)),
+        build_id(0) {}
+  MapInfo(MapInfo* map_info, 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),
+        prev_map(map_info),
+        load_bias(static_cast<uint64_t>(-1)),
+        build_id(0) {}
+  ~MapInfo();
 
   uint64_t start = 0;
   uint64_t end = 0;
@@ -50,25 +60,46 @@
   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.
+  // no elf signature found at that offset.
   uint64_t elf_offset = 0;
+  // This value is the offset from the map in memory that is the start
+  // of the elf. This is not equal to offset when the linker splits
+  // shared libraries into a read-only and read-execute map.
+  uint64_t elf_start_offset = 0;
+
+  MapInfo* prev_map = nullptr;
 
   std::atomic_uint64_t load_bias;
 
+  // This is a pointer to a new'd std::string.
+  // Using an atomic value means that we don't need to lock and will
+  // make it easier to move to a fine grained lock in the future.
+  std::atomic_uintptr_t build_id;
+
+  // Set to true if the elf file data is coming from memory.
+  bool memory_backed_elf = false;
+
   // This function guarantees it will never return nullptr.
-  Elf* GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata = false);
+  Elf* GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch);
 
   uint64_t GetLoadBias(const std::shared_ptr<Memory>& process_memory);
 
+  Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
+
+  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
+
+  // Returns the raw build id read from the elf data.
+  std::string GetBuildID();
+
+  // Returns the printable version of the build id (hex dump of raw data).
+  std::string GetPrintableBuildID();
+
  private:
   MapInfo(const MapInfo&) = delete;
   void operator=(const MapInfo&) = delete;
 
   Memory* GetFileMemory();
-
-  Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
+  bool InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory);
 
   // Protect the creation of the elf object.
   std::mutex mutex_;
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index 17a2d28..1784394 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -20,6 +20,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -30,11 +31,23 @@
 // 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:
+  virtual ~Maps() = default;
+
   Maps() = default;
-  virtual ~Maps();
+
+  // Maps are not copyable but movable, because they own pointers to MapInfo
+  // objects.
+  Maps(const Maps&) = delete;
+  Maps& operator=(const Maps&) = delete;
+  Maps(Maps&&) = default;
+  Maps& operator=(Maps&&) = default;
 
   MapInfo* Find(uint64_t pc);
 
@@ -45,11 +58,13 @@
   void Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name,
            uint64_t load_bias);
 
-  typedef std::vector<MapInfo*>::iterator iterator;
+  void Sort();
+
+  typedef std::vector<std::unique_ptr<MapInfo>>::iterator iterator;
   iterator begin() { return maps_.begin(); }
   iterator end() { return maps_.end(); }
 
-  typedef std::vector<MapInfo*>::const_iterator const_iterator;
+  typedef std::vector<std::unique_ptr<MapInfo>>::const_iterator const_iterator;
   const_iterator begin() const { return maps_.begin(); }
   const_iterator end() const { return maps_.end(); }
 
@@ -57,11 +72,11 @@
 
   MapInfo* Get(size_t index) {
     if (index >= maps_.size()) return nullptr;
-    return maps_[index];
+    return maps_[index].get();
   }
 
  protected:
-  std::vector<MapInfo*> maps_;
+  std::vector<std::unique_ptr<MapInfo>> maps_;
 };
 
 class RemoteMaps : public Maps {
@@ -81,6 +96,19 @@
   virtual ~LocalMaps() = default;
 };
 
+class LocalUpdatableMaps : public Maps {
+ public:
+  LocalUpdatableMaps() : Maps() {}
+  virtual ~LocalUpdatableMaps() = default;
+
+  bool Reparse();
+
+  const std::string GetMapsFile() const override;
+
+ private:
+  std::vector<std::unique_ptr<MapInfo>> saved_maps_;
+};
+
 class BufferMaps : public Maps {
  public:
   BufferMaps(const char* buffer) : buffer_(buffer) {}
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index c0c07f4..3106564 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -21,10 +21,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <atomic>
 #include <memory>
 #include <string>
-#include <vector>
 
 namespace unwindstack {
 
@@ -34,25 +32,19 @@
   virtual ~Memory() = default;
 
   static std::shared_ptr<Memory> CreateProcessMemory(pid_t pid);
+  static std::shared_ptr<Memory> CreateProcessMemoryCached(pid_t pid);
+  static std::shared_ptr<Memory> CreateOfflineMemory(const uint8_t* data, uint64_t start,
+                                                     uint64_t end);
+  static std::unique_ptr<Memory> CreateFileMemory(const std::string& path, uint64_t offset);
 
   virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
 
+  virtual void Clear() {}
+
   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 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 ReadFully(offset, field, size);
-  }
-
   inline bool Read32(uint64_t addr, uint32_t* dst) {
     return ReadFully(addr, dst, sizeof(uint32_t));
   }
@@ -62,123 +54,6 @@
   }
 };
 
-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
index 878ced3..1c2a81c 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -18,6 +18,7 @@
 #define _LIBUNWINDSTACK_REGS_H
 
 #include <stdint.h>
+#include <unistd.h>
 
 #include <functional>
 #include <string>
diff --git a/libunwindstack/include/unwindstack/RegsGetLocal.h b/libunwindstack/include/unwindstack/RegsGetLocal.h
index 81c0af3..f0b5e3a 100644
--- a/libunwindstack/include/unwindstack/RegsGetLocal.h
+++ b/libunwindstack/include/unwindstack/RegsGetLocal.h
@@ -33,8 +33,7 @@
 
 #if defined(__arm__)
 
-inline __always_inline void RegsGetLocal(Regs* regs) {
-  void* reg_data = regs->RawData();
+inline __attribute__((__always_inline__)) void AsmGetRegs(void* reg_data) {
   asm volatile(
       ".align 2\n"
       "bx pc\n"
@@ -55,8 +54,7 @@
 
 #elif defined(__aarch64__)
 
-inline __always_inline void RegsGetLocal(Regs* regs) {
-  void* reg_data = regs->RawData();
+inline __attribute__((__always_inline__)) void AsmGetRegs(void* reg_data) {
   asm volatile(
       "1:\n"
       "stp x0, x1, [%[base], #0]\n"
@@ -87,11 +85,12 @@
 
 extern "C" void AsmGetRegs(void* regs);
 
-inline void RegsGetLocal(Regs* regs) {
+#endif
+
+inline __attribute__((__always_inline__)) void RegsGetLocal(Regs* regs) {
   AsmGetRegs(regs->RawData());
 }
 
-#endif
 
 }  // namespace unwindstack
 
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 56b0581..52b3578 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -24,7 +24,9 @@
 #include <string>
 #include <vector>
 
+#include <unwindstack/DexFiles.h>
 #include <unwindstack/Error.h>
+#include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
@@ -32,9 +34,7 @@
 namespace unwindstack {
 
 // Forward declarations.
-class DexFiles;
 class Elf;
-class JitDebug;
 enum ArchEnum : uint8_t;
 
 struct FrameData {
@@ -48,7 +48,13 @@
   uint64_t function_offset = 0;
 
   std::string map_name;
-  uint64_t map_offset = 0;
+  // The offset from the first map representing the frame. When there are
+  // two maps (read-only and read-execute) this will be the offset from
+  // the read-only map. When there is only one map, this will be the
+  // same as the actual offset of the map and match map_exact_offset.
+  uint64_t map_elf_start_offset = 0;
+  // The actual offset from the map where the pc lies.
+  uint64_t map_exact_offset = 0;
   uint64_t map_start = 0;
   uint64_t map_end = 0;
   uint64_t map_load_bias = 0;
@@ -61,7 +67,12 @@
       : max_frames_(max_frames), maps_(maps), regs_(regs), process_memory_(process_memory) {
     frames_.reserve(max_frames);
   }
-  ~Unwinder() = default;
+  Unwinder(size_t max_frames, Maps* maps, std::shared_ptr<Memory> process_memory)
+      : max_frames_(max_frames), maps_(maps), process_memory_(process_memory) {
+    frames_.reserve(max_frames);
+  }
+
+  virtual ~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);
@@ -70,26 +81,46 @@
 
   const std::vector<FrameData>& frames() { return frames_; }
 
+  std::vector<FrameData> ConsumeFrames() {
+    std::vector<FrameData> frames = std::move(frames_);
+    frames_.clear();
+    return frames;
+  }
+
   std::string FormatFrame(size_t frame_num);
-  static std::string FormatFrame(const FrameData& frame, bool is32bit);
+  std::string FormatFrame(const FrameData& frame);
 
   void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
 
+  void SetRegs(Regs* regs) { regs_ = regs; }
+  Maps* GetMaps() { return maps_; }
+  std::shared_ptr<Memory>& GetProcessMemory() { return process_memory_; }
+
   // 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; }
 
+  // Enable/disable soname printing the soname for a map name if the elf is
+  // embedded in a file. This is enabled by default.
+  // NOTE: This does nothing unless resolving names is enabled.
+  void SetEmbeddedSoname(bool embedded_soname) { embedded_soname_ = embedded_soname; }
+
+  void SetDisplayBuildID(bool display_build_id) { display_build_id_ = display_build_id; }
+
 #if !defined(NO_LIBDEXFILE_SUPPORT)
   void SetDexFiles(DexFiles* dex_files, ArchEnum arch);
 #endif
 
+  bool elf_from_memory_not_file() { return elf_from_memory_not_file_; }
+
   ErrorCode LastErrorCode() { return last_error_.code; }
   uint64_t LastErrorAddress() { return last_error_.address; }
 
- private:
+ protected:
+  Unwinder(size_t max_frames) : max_frames_(max_frames) { frames_.reserve(max_frames); }
+
   void FillInDexFrame();
-  void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t func_pc,
-                   uint64_t pc_adjustment);
+  FrameData* FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t pc_adjustment);
 
   size_t max_frames_;
   Maps* maps_;
@@ -101,9 +132,30 @@
   DexFiles* dex_files_ = nullptr;
 #endif
   bool resolve_names_ = true;
+  bool embedded_soname_ = true;
+  bool display_build_id_ = false;
+  // True if at least one elf file is coming from memory and not the related
+  // file. This is only true if there is an actual file backing up the elf.
+  bool elf_from_memory_not_file_ = false;
   ErrorData last_error_;
 };
 
+class UnwinderFromPid : public Unwinder {
+ public:
+  UnwinderFromPid(size_t max_frames, pid_t pid) : Unwinder(max_frames), pid_(pid) {}
+  virtual ~UnwinderFromPid() = default;
+
+  bool Init(ArchEnum arch);
+
+ private:
+  pid_t pid_;
+  std::unique_ptr<Maps> maps_ptr_;
+  std::unique_ptr<JitDebug> jit_debug_ptr_;
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+  std::unique_ptr<DexFiles> dex_files_ptr_;
+#endif
+};
+
 }  // namespace unwindstack
 
 #endif  // _LIBUNWINDSTACK_UNWINDER_H
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
index 8d6d00d..7b1dd92 100644
--- a/libunwindstack/tests/ArmExidxDecodeTest.cpp
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -36,8 +36,6 @@
 class ArmExidxDecodeTest : public ::testing::TestWithParam<std::string> {
  protected:
   void Init(Memory* process_memory = nullptr) {
-    TearDown();
-
     if (process_memory == nullptr) {
       process_memory = &process_memory_;
     }
@@ -50,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);
     }
@@ -60,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();
   }
 
@@ -77,7 +81,7 @@
 
   MemoryFake elf_memory_;
   MemoryFake process_memory_;
-  bool log_;
+  ArmLogType log_;
 };
 
 TEST_P(ArmExidxDecodeTest, vsp_incr) {
@@ -86,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) {
@@ -126,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) {
@@ -166,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());
 }
@@ -182,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());
@@ -222,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);
@@ -233,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]);
@@ -255,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());
 }
@@ -292,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());
 }
@@ -319,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]);
@@ -384,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]);
@@ -451,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());
@@ -466,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());
@@ -477,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());
@@ -494,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());
@@ -524,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());
@@ -541,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());
@@ -557,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());
@@ -580,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]);
@@ -634,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);
@@ -663,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) {
@@ -678,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) {
@@ -705,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) {
@@ -743,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) {
@@ -782,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) {
@@ -823,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) {
@@ -864,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) {
@@ -905,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) {
@@ -945,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) {
@@ -1047,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);
@@ -1080,20 +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_SUITE_P(, ArmExidxDecodeTest,
+                         ::testing::Values("logging", "register_logging", "no_logging"));
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ArmExidxExtractTest.cpp b/libunwindstack/tests/ArmExidxExtractTest.cpp
index 8d0f0e5..79c799c 100644
--- a/libunwindstack/tests/ArmExidxExtractTest.cpp
+++ b/libunwindstack/tests/ArmExidxExtractTest.cpp
@@ -301,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);
 
@@ -316,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);
 
@@ -330,7 +330,7 @@
   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);
 
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index 0b02c5b..0149a42 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -20,45 +20,35 @@
 
 #include <unordered_map>
 
-#include <android-base/test_utils.h>
-
+#include <android-base/file.h>
+#include <gtest/gtest.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"));
+  EXPECT_TRUE(DexFileFromFile::Create(0, "/file/does/not/exist") == nullptr);
 }
 
 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)));
+  ASSERT_EQ(size_t{10}, static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, 10))));
 
   // Header too small.
-  DexFileFromFile dex_file;
-  ASSERT_FALSE(dex_file.Open(0, tf.path));
+  EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) == nullptr);
 
   // 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));
+  ASSERT_EQ(sizeof(kDexData) - 1,
+            static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData) - 1))));
+  EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) == nullptr);
 }
 
 TEST(DexFileTest, from_file_open) {
@@ -68,8 +58,7 @@
   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));
+  EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) != nullptr);
 }
 
 TEST(DexFileTest, from_file_open_non_zero_offset) {
@@ -80,35 +69,31 @@
   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));
+  EXPECT_TRUE(DexFileFromFile::Create(0x100, tf.path) != nullptr);
 }
 
 TEST(DexFileTest, from_memory_fail_too_small_for_header) {
   MemoryFake memory;
 
-  memory.SetMemory(0x1000, kDexData, sizeof(art::DexFile::Header) - 1);
-  DexFileFromMemory dex_file;
+  memory.SetMemory(0x1000, kDexData, 10);
 
-  ASSERT_FALSE(dex_file.Open(0x1000, &memory));
+  EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") == nullptr);
 }
 
 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));
+  EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") == nullptr);
 }
 
 TEST(DexFileTest, from_memory_open) {
   MemoryFake memory;
 
   memory.SetMemory(0x1000, kDexData, sizeof(kDexData));
-  DexFileFromMemory dex_file;
 
-  ASSERT_TRUE(dex_file.Open(0x1000, &memory));
+  EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") != nullptr);
 }
 
 TEST(DexFileTest, create_using_file) {
@@ -120,9 +105,8 @@
             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);
+  MapInfo info(nullptr, 0, 0x10000, 0, 0x5, tf.path);
+  EXPECT_TRUE(DexFile::Create(0x500, &memory, &info) != nullptr);
 }
 
 TEST(DexFileTest, create_using_file_non_zero_start) {
@@ -134,9 +118,8 @@
             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);
+  MapInfo info(nullptr, 0x100, 0x10000, 0, 0x5, tf.path);
+  EXPECT_TRUE(DexFile::Create(0x600, &memory, &info) != nullptr);
 }
 
 TEST(DexFileTest, create_using_file_non_zero_offset) {
@@ -148,25 +131,22 @@
             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);
+  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, tf.path);
+  EXPECT_TRUE(DexFile::Create(0x400, &memory, &info) != 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);
+  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
+  EXPECT_TRUE(DexFile::Create(0x4000, &memory, &info) != 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);
+  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
+  EXPECT_TRUE(DexFile::Create(0x4000, &memory, &info) != nullptr);
 }
 
 TEST(DexFileTest, create_using_memory_file_is_malformed) {
@@ -178,43 +158,46 @@
 
   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));
+  MapInfo info(nullptr, 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);
+  dex_file = DexFile::Create(0x4000, &memory, &info);
+  EXPECT_TRUE(dex_file == nullptr);
 }
 
 TEST(DexFileTest, get_method) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+  MapInfo info(nullptr, 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;
-  dex_file->GetMethodInformation(0x102, &method, &method_offset);
+  ASSERT_TRUE(dex_file->GetMethodInformation(0x102, &method, &method_offset));
   EXPECT_EQ("Main.<init>", method);
   EXPECT_EQ(2U, method_offset);
 
-  method = "not_in_a_method";
-  method_offset = 0x123;
-  dex_file->GetMethodInformation(0x100000, &method, &method_offset);
-  EXPECT_EQ("not_in_a_method", method);
-  EXPECT_EQ(0x123U, method_offset);
+  ASSERT_TRUE(dex_file->GetMethodInformation(0x118, &method, &method_offset));
+  EXPECT_EQ("Main.main", method);
+  EXPECT_EQ(0U, method_offset);
+}
+
+TEST(DexFileTest, get_method_empty) {
+  MemoryFake memory;
+  memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
+  MapInfo info(nullptr, 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));
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
index d029bb0..1ea9e5c 100644
--- a/libunwindstack/tests/DexFilesTest.cpp
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -36,57 +36,54 @@
 
 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);
+  void CreateFakeElf(MapInfo* map_info) {
     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);
+  }
+
+  void Init(ArchEnum arch) {
+    dex_files_.reset(new DexFiles(process_memory_));
+    dex_files_->SetArch(arch);
+
+    maps_.reset(
+        new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf\n"
+                       "4000-6000 r--s 00000000 00:00 0 /fake/elf\n"
+                       "6000-8000 -wxs 00000000 00:00 0 /fake/elf\n"
+                       "a000-c000 r--p 00000000 00:00 0 /fake/elf2\n"
+                       "c000-f000 rw-p 00001000 00:00 0 /fake/elf2\n"
+                       "f000-11000 r--p 00000000 00:00 0 /fake/elf3\n"
+                       "100000-110000 rw-p 0001000 00:00 0 /fake/elf3\n"
+                       "200000-210000 rw-p 0002000 00:00 0 /fake/elf3\n"
+                       "300000-400000 rw-p 0003000 00:00 0 /fake/elf3\n"));
+    ASSERT_TRUE(maps_->Parse());
+
+    // Global variable in a section that is not readable.
+    MapInfo* map_info = maps_->Get(kMapGlobalNonReadable);
+    ASSERT_TRUE(map_info != nullptr);
+    CreateFakeElf(map_info);
 
     // 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);
+    CreateFakeElf(map_info);
 
     // 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);
+    CreateFakeElf(map_info);
+  }
+
+  void SetUp() override {
+    memory_ = new MemoryFake;
+    process_memory_.reset(memory_);
+
+    Init(ARCH_ARM);
   }
 
   void WriteDescriptor32(uint64_t addr, uint32_t head);
@@ -95,9 +92,10 @@
   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 kMapGlobalNonReadable = 2;
+  static constexpr size_t kMapGlobalSetToZero = 3;
   static constexpr size_t kMapGlobal = 5;
+  static constexpr size_t kMapGlobalRw = 6;
   static constexpr size_t kMapDexFileEntries = 7;
   static constexpr size_t kMapDexFiles = 8;
 
@@ -168,11 +166,12 @@
 }
 
 TEST_F(DexFilesTest, get_method_information_64) {
+  Init(ARCH_ARM64);
+
   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);
@@ -198,11 +197,12 @@
 }
 
 TEST_F(DexFilesTest, get_method_information_not_first_entry_64) {
+  Init(ARCH_ARM64);
+
   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);
@@ -256,6 +256,9 @@
   map_info->name = "/system/lib/libart.so";
   dex_files_.reset(new DexFiles(process_memory_, libs));
   dex_files_->SetArch(ARCH_ARM);
+  // Set the rw map to the same name or this will not scan this entry.
+  map_info = maps_->Get(kMapGlobalRw);
+  map_info->name = "/system/lib/libart.so";
   // Make sure that clearing out copy of the libs doesn't affect the
   // DexFiles object.
   libs.clear();
@@ -271,7 +274,7 @@
   MapInfo* info = maps_->Get(kMapDexFiles);
 
   // First global variable found, but value is zero.
-  WriteDescriptor32(0xc800, 0);
+  WriteDescriptor32(0xa800, 0);
 
   WriteDescriptor32(0xf800, 0x200000);
   WriteEntry32(0x200000, 0, 0, 0x300000);
@@ -286,25 +289,26 @@
   dex_files_->SetArch(ARCH_ARM);
   method_name = "fail";
   method_offset = 0x123;
-  WriteDescriptor32(0xc800, 0x100000);
+  WriteDescriptor32(0xa800, 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) {
+  Init(ARCH_ARM64);
+
   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(0xa800, 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);
@@ -314,7 +318,7 @@
   dex_files_->SetArch(ARCH_ARM64);
   method_name = "fail";
   method_offset = 0x123;
-  WriteDescriptor64(0xc800, 0x100000);
+  WriteDescriptor64(0xa800, 0x100000);
   dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
   EXPECT_EQ("fail", method_name);
   EXPECT_EQ(0x123U, method_offset);
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
index b17ca33..9dd0cdd 100644
--- a/libunwindstack/tests/DwarfCfaLogTest.cpp
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -66,7 +66,7 @@
   DwarfCie cie_;
   DwarfFde fde_;
 };
-TYPED_TEST_CASE_P(DwarfCfaLogTest);
+TYPED_TEST_SUITE_P(DwarfCfaLogTest);
 
 // NOTE: All class variable references have to be prefaced with this->.
 
@@ -79,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());
@@ -90,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";
@@ -101,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";
@@ -111,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";
@@ -122,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";
@@ -132,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";
@@ -143,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";
@@ -154,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";
@@ -165,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";
@@ -175,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"
@@ -188,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";
@@ -198,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"
@@ -228,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";
@@ -240,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";
@@ -252,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"
@@ -260,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"
@@ -283,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"
@@ -306,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"
@@ -329,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";
@@ -355,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";
@@ -366,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";
@@ -376,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";
@@ -387,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";
@@ -397,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";
@@ -408,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"
@@ -419,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"
@@ -431,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"
@@ -447,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"
@@ -458,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"
@@ -470,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"
@@ -482,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"
@@ -494,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"
@@ -505,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"
@@ -517,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"
@@ -526,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"
@@ -537,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"
@@ -549,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"
@@ -558,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"
@@ -570,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"
@@ -582,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"
@@ -614,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());
@@ -624,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"
@@ -652,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());
@@ -661,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"
@@ -672,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"
@@ -684,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"
@@ -696,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"
@@ -708,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"
@@ -737,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());
@@ -746,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"
@@ -757,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"
@@ -769,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"
@@ -780,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"
@@ -792,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"
@@ -803,17 +763,17 @@
   ASSERT_EQ("", GetFakeLogBuf());
 }
 
-REGISTER_TYPED_TEST_CASE_P(DwarfCfaLogTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
-                           cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
-                           cfa_advance_loc, cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4,
-                           cfa_undefined, cfa_same, cfa_register, cfa_state,
-                           cfa_state_cfa_offset_restore, cfa_def_cfa, cfa_def_cfa_sf,
-                           cfa_def_cfa_register, cfa_def_cfa_offset, cfa_def_cfa_offset_sf,
-                           cfa_def_cfa_expression, cfa_expression, cfa_val_offset,
-                           cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
-                           cfa_gnu_negative_offset_extended, cfa_register_override);
+REGISTER_TYPED_TEST_SUITE_P(DwarfCfaLogTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
+                            cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
+                            cfa_advance_loc, cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4,
+                            cfa_undefined, cfa_same, cfa_register, cfa_state,
+                            cfa_state_cfa_offset_restore, cfa_def_cfa, cfa_def_cfa_sf,
+                            cfa_def_cfa_register, cfa_def_cfa_offset, cfa_def_cfa_offset_sf,
+                            cfa_def_cfa_expression, cfa_expression, cfa_val_offset,
+                            cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
+                            cfa_gnu_negative_offset_extended, cfa_register_override);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaLogTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaLogTest, DwarfCfaLogTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfCfaLogTest, DwarfCfaLogTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
index 7395b04..dd71490 100644
--- a/libunwindstack/tests/DwarfCfaTest.cpp
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -64,7 +64,7 @@
   DwarfCie cie_;
   DwarfFde fde_;
 };
-TYPED_TEST_CASE_P(DwarfCfaTest);
+TYPED_TEST_SUITE_P(DwarfCfaTest);
 
 // NOTE: All test class variables need to be referenced as this->.
 
@@ -952,16 +952,17 @@
   ASSERT_EQ("", GetFakeLogBuf());
 }
 
-REGISTER_TYPED_TEST_CASE_P(DwarfCfaTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
-                           cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
-                           cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4, cfa_undefined,
-                           cfa_same, cfa_register, cfa_state, cfa_state_cfa_offset_restore,
-                           cfa_def_cfa, cfa_def_cfa_sf, cfa_def_cfa_register, cfa_def_cfa_offset,
-                           cfa_def_cfa_offset_sf, cfa_def_cfa_expression, cfa_expression,
-                           cfa_val_offset, cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
-                           cfa_gnu_negative_offset_extended, cfa_register_override);
+REGISTER_TYPED_TEST_SUITE_P(DwarfCfaTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
+                            cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
+                            cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4, cfa_undefined,
+                            cfa_same, cfa_register, cfa_state, cfa_state_cfa_offset_restore,
+                            cfa_def_cfa, cfa_def_cfa_sf, cfa_def_cfa_register, cfa_def_cfa_offset,
+                            cfa_def_cfa_offset_sf, cfa_def_cfa_expression, cfa_expression,
+                            cfa_val_offset, cfa_val_offset_sf, cfa_val_expression,
+                            cfa_gnu_args_size, cfa_gnu_negative_offset_extended,
+                            cfa_register_override);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaTest, DwarfCfaTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfCfaTest, DwarfCfaTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index c28a41e..2b36f17 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -16,7 +16,8 @@
 
 #include <stdint.h>
 
-#include <gmock/gmock.h>
+#include <vector>
+
 #include <gtest/gtest.h>
 
 #include <unwindstack/DwarfError.h>
@@ -30,442 +31,377 @@
 namespace unwindstack {
 
 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->entries_offset_ = offset; }
-  void TestSetEndOffset(uint64_t offset) { this->entries_end_ = 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];
-  }
-};
-
-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);
+TYPED_TEST_SUITE_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_->LastErrorCode());
+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, Init32_do_not_fail_on_bad_next_entry) {
-  // 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, 0);
-  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(2U, this->debug_frame_->TestGetFdeCount());
-}
-
-TYPED_TEST_P(DwarfDebugFrameTest, Init64) {
-  // 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');
-
-  // 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);
-
-  // 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');
-
-  // 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);
-}
-
-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');
-
-  // 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);
-
-  ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->LastErrorCode());
-}
-
-TYPED_TEST_P(DwarfDebugFrameTest, Init64_do_not_fail_on_bad_next_entry) {
-  // 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');
-
-  // 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);
-
-  // CIE 64 information.
-  this->memory_.SetData32(0x5300, 0xffffffff);
-  this->memory_.SetData64(0x5304, 0);
-  this->memory_.SetData64(0x530c, 0xffffffffffffffffULL);
-  this->memory_.SetData8(0x5314, 1);
-  this->memory_.SetData8(0x5315, '\0');
-
-  // 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(2U, this->debug_frame_->TestGetFdeCount());
-}
-
-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);
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0);
-  this->memory_.SetData16(0x5108, 0x1500);
-  this->memory_.SetData16(0x510a, 0x200);
-
-  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);
-}
-
-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);
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0);
-  this->memory_.SetData16(0x5108, 0x1500);
-  this->memory_.SetData16(0x510a, 0x200);
-
-  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);
-}
-
-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);
+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);
   }
+}
 
-  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_->LastErrorCode());
-
-  this->debug_frame_->TestSetFdeCount(9);
-  ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
-  // 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_->LastErrorCode());
+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);
   }
+}
 
-  // Even number of elements.
-  this->debug_frame_->TestSetFdeCount(10);
-  info.start = 0xa000;
-  info.end = 0xaff0;
-  info.offset = 0x5120;
-  this->debug_frame_->TestPushFdeInfo(info);
+static void SetFourFdes32(MemoryFake* memory) {
+  SetCie32(memory, 0x5000, 0xfc, std::vector<uint8_t>{1, '\0', 0, 0, 1});
 
-  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_->LastErrorCode());
-  }
+  // 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.
+  SetCie64(memory, 0x5000, 0xf4, std::vector<uint8_t>{1, '\0', 0, 0, 1});
+
+  // FDE 64 information.
+  SetFde64(memory, 0x5100, 0xf4, 0, 0x1500, 0x200);
+  SetFde64(memory, 0x5200, 0xf4, 0, 0x2500, 0x300);
+
+  // CIE 64 information.
+  SetCie64(memory, 0x5300, 0xf4, std::vector<uint8_t>{1, '\0', 0, 0, 1});
+
+  // FDE 64 information.
+  SetFde64(memory, 0x5400, 0xf4, 0x300, 0x3500, 0x400);
+  SetFde64(memory, 0x5500, 0xf4, 0x300, 0x4500, 0x500);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes64) {
+  SetFourFdes64(&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(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, GetFdes64_after_GetFdeFromPc) {
+  SetFourFdes64(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x2600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x2500U, fde->pc_start);
+  EXPECT_EQ(0x2800U, 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, GetFdes64_not_in_section) {
+  SetFourFdes64(&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, GetFdeFromPc64) {
+  SetFourFdes64(&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, 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);
@@ -492,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);
@@ -535,13 +455,376 @@
   EXPECT_EQ(0x20U, fde->cie->return_address_register);
 }
 
-REGISTER_TYPED_TEST_CASE_P(DwarfDebugFrameTest, Init32, Init32_fde_not_following_cie,
-                           Init32_do_not_fail_on_bad_next_entry, Init64,
-                           Init64_do_not_fail_on_bad_next_entry, 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, GetCieFromOffset32_version5) {
+  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{5, '\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, 5, 10, DW_EH_PE_sdata4, 0x181, 0x10, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version5) {
+  SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{5, '\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, 5, 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>{6, '\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>{6, '\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_SUITE_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, GetCieFromOffset32_version5, GetCieFromOffset64_version5,
+    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);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index a73db65..4792fb5 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -16,7 +16,6 @@
 
 #include <stdint.h>
 
-#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include <unwindstack/DwarfError.h>
@@ -30,50 +29,32 @@
 namespace unwindstack {
 
 template <typename TypeParam>
-class MockDwarfEhFrame : public DwarfEhFrame<TypeParam> {
- public:
-  MockDwarfEhFrame(Memory* memory) : DwarfEhFrame<TypeParam>(memory) {}
-  ~MockDwarfEhFrame() = default;
-
-  void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
-  void TestSetOffset(uint64_t offset) { this->entries_offset_ = offset; }
-  void TestSetEndOffset(uint64_t offset) { this->entries_end_ = offset; }
-  void TestPushFdeInfo(const typename DwarfEhFrame<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 DwarfEhFrame<TypeParam>::FdeInfo* info) {
-    *info = this->fdes_[index];
-  }
-};
-
-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);
+TYPED_TEST_SUITE_P(DwarfEhFrameTest);
 
 // NOTE: All test class variables need to be referenced as this->.
 
-TYPED_TEST_P(DwarfEhFrameTest, Init32) {
+// Only verify different cie/fde format. All other DwarfSection corner
+// cases are tested in DwarfDebugFrameTest.cpp.
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeCieFromOffset32) {
   // CIE 32 information.
   this->memory_.SetData32(0x5000, 0xfc);
+  // Indicates this is a cie for eh_frame.
   this->memory_.SetData32(0x5004, 0);
-  this->memory_.SetData8(0x5008, 1);
-  this->memory_.SetData8(0x5009, '\0');
+  this->memory_.SetMemory(0x5008, std::vector<uint8_t>{1, '\0', 16, 32, 1});
 
   // FDE 32 information.
   this->memory_.SetData32(0x5100, 0xfc);
@@ -81,379 +62,72 @@
   this->memory_.SetData32(0x5108, 0x1500);
   this->memory_.SetData32(0x510c, 0x200);
 
-  this->memory_.SetData32(0x5200, 0xfc);
-  this->memory_.SetData32(0x5204, 0x204);
-  this->memory_.SetData32(0x5208, 0x2500);
-  this->memory_.SetData32(0x520c, 0x300);
+  const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x5100);
+  ASSERT_TRUE(fde != nullptr);
+  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);
 
-  // CIE 32 information.
-  this->memory_.SetData32(0x5300, 0xfc);
-  this->memory_.SetData32(0x5304, 0);
-  this->memory_.SetData8(0x5308, 1);
-  this->memory_.SetData8(0x5309, '\0');
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5400, 0xfc);
-  this->memory_.SetData32(0x5404, 0x104);
-  this->memory_.SetData32(0x5408, 0x3500);
-  this->memory_.SetData32(0x540c, 0x400);
-
-  this->memory_.SetData32(0x5500, 0xfc);
-  this->memory_.SetData32(0x5504, 0x204);
-  this->memory_.SetData32(0x5508, 0x4500);
-  this->memory_.SetData32(0x550c, 0x500);
-
-  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(4U, this->eh_frame_->TestGetFdeCount());
-
-  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
-
-  this->eh_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x6608U, info.start);
-  EXPECT_EQ(0x6808U, info.end);
-
-  this->eh_frame_->TestGetFdeInfo(1, &info);
-  EXPECT_EQ(0x5200U, info.offset);
-  EXPECT_EQ(0x7708U, info.start);
-  EXPECT_EQ(0x7a08U, info.end);
-
-  this->eh_frame_->TestGetFdeInfo(2, &info);
-  EXPECT_EQ(0x5400U, info.offset);
-  EXPECT_EQ(0x8908U, info.start);
-  EXPECT_EQ(0x8d08U, info.end);
-
-  this->eh_frame_->TestGetFdeInfo(3, &info);
-  EXPECT_EQ(0x5500U, info.offset);
-  EXPECT_EQ(0x9a08U, info.start);
-  EXPECT_EQ(0x9f08U, info.end);
+  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, Init32_fde_not_following_cie) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0);
-  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->eh_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->LastErrorCode());
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, Init64) {
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeCieFromOffset64) {
   // CIE 64 information.
   this->memory_.SetData32(0x5000, 0xffffffff);
-  this->memory_.SetData64(0x5004, 0xf4);
+  this->memory_.SetData64(0x5004, 0xfc);
+  // Indicates this is a cie for eh_frame.
   this->memory_.SetData64(0x500c, 0);
-  this->memory_.SetData8(0x5014, 1);
-  this->memory_.SetData8(0x5015, '\0');
+  this->memory_.SetMemory(0x5014, std::vector<uint8_t>{1, '\0', 16, 32, 1});
 
   // FDE 64 information.
   this->memory_.SetData32(0x5100, 0xffffffff);
-  this->memory_.SetData64(0x5104, 0xf4);
+  this->memory_.SetData64(0x5104, 0xfc);
   this->memory_.SetData64(0x510c, 0x10c);
   this->memory_.SetData64(0x5114, 0x1500);
   this->memory_.SetData64(0x511c, 0x200);
 
-  this->memory_.SetData32(0x5200, 0xffffffff);
-  this->memory_.SetData64(0x5204, 0xf4);
-  this->memory_.SetData64(0x520c, 0x20c);
-  this->memory_.SetData64(0x5214, 0x2500);
-  this->memory_.SetData64(0x521c, 0x300);
-
-  // CIE 64 information.
-  this->memory_.SetData32(0x5300, 0xffffffff);
-  this->memory_.SetData64(0x5304, 0xf4);
-  this->memory_.SetData64(0x530c, 0);
-  this->memory_.SetData8(0x5314, 1);
-  this->memory_.SetData8(0x5315, '\0');
-
-  // FDE 64 information.
-  this->memory_.SetData32(0x5400, 0xffffffff);
-  this->memory_.SetData64(0x5404, 0xf4);
-  this->memory_.SetData64(0x540c, 0x10c);
-  this->memory_.SetData64(0x5414, 0x3500);
-  this->memory_.SetData64(0x541c, 0x400);
-
-  this->memory_.SetData32(0x5500, 0xffffffff);
-  this->memory_.SetData64(0x5504, 0xf4);
-  this->memory_.SetData64(0x550c, 0x20c);
-  this->memory_.SetData64(0x5514, 0x4500);
-  this->memory_.SetData64(0x551c, 0x500);
-
-  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(4U, this->eh_frame_->TestGetFdeCount());
-
-  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
-
-  this->eh_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x6618U, info.start);
-  EXPECT_EQ(0x6818U, info.end);
-
-  this->eh_frame_->TestGetFdeInfo(1, &info);
-  EXPECT_EQ(0x5200U, info.offset);
-  EXPECT_EQ(0x7718U, info.start);
-  EXPECT_EQ(0x7a18U, info.end);
-
-  this->eh_frame_->TestGetFdeInfo(2, &info);
-  EXPECT_EQ(0x5400U, info.offset);
-  EXPECT_EQ(0x8918U, info.start);
-  EXPECT_EQ(0x8d18U, info.end);
-
-  this->eh_frame_->TestGetFdeInfo(3, &info);
-  EXPECT_EQ(0x5500U, info.offset);
-  EXPECT_EQ(0x9a18U, info.start);
-  EXPECT_EQ(0x9f18U, info.end);
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, Init64_fde_not_following_cie) {
-  // CIE 64 information.
-  this->memory_.SetData32(0x5000, 0xffffffff);
-  this->memory_.SetData64(0x5004, 0xf4);
-  this->memory_.SetData64(0x500c, 0);
-  this->memory_.SetData8(0x5014, 1);
-  this->memory_.SetData8(0x5015, '\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);
-
-  ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->LastErrorCode());
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, Init_version1) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0);
-  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);
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0x104);
-  this->memory_.SetData16(0x5108, 0x1500);
-  this->memory_.SetData16(0x510a, 0x200);
-
-  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200));
-  ASSERT_EQ(1U, this->eh_frame_->TestGetFdeCount());
-
-  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
-  this->eh_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x6606U, info.start);
-  EXPECT_EQ(0x6806U, info.end);
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, Init_version4) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0);
-  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);
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0x104);
-  this->memory_.SetData16(0x5108, 0x1500);
-  this->memory_.SetData16(0x510a, 0x200);
-
-  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200));
-  ASSERT_EQ(1U, this->eh_frame_->TestGetFdeCount());
-
-  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
-  this->eh_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x6606U, info.start);
-  EXPECT_EQ(0x6806U, info.end);
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc) {
-  typename DwarfEhFrame<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->eh_frame_->TestPushFdeInfo(info);
-  }
-
-  this->eh_frame_->TestSetFdeCount(0);
-  uint64_t fde_offset;
-  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
-
-  this->eh_frame_->TestSetFdeCount(9);
-  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
-  // Odd number of elements.
-  for (size_t i = 0; i < 9; i++) {
-    TypeParam pc = 0x1000 * (i + 1);
-    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
-        << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
-        << "Failed at index " << i;
-    ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
-  }
-
-  // Even number of elements.
-  this->eh_frame_->TestSetFdeCount(10);
-  info.start = 0xa000;
-  info.end = 0xaff0;
-  info.offset = 0x5120;
-  this->eh_frame_->TestPushFdeInfo(info);
-
-  for (size_t i = 0; i < 10; i++) {
-    TypeParam pc = 0x1000 * (i + 1);
-    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
-        << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
-        << "Failed at index " << i;
-    ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
-  }
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetCieFde32) {
-  this->eh_frame_->TestSetOffset(0x4000);
-
-  // 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);
-
-  // 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);
+  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(0x1d008U, fde->pc_start);
-  EXPECT_EQ(0x1d108U, fde->pc_end);
-  EXPECT_EQ(0xf000U, 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_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_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);
 }
 
-TYPED_TEST_P(DwarfEhFrameTest, GetCieFde64) {
-  this->eh_frame_->TestSetOffset(0x2000);
-
-  // 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);
-
-  // 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);
-}
-
-REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, Init32, Init32_fde_not_following_cie, Init64,
-                           Init64_fde_not_following_cie, Init_version1, Init_version4,
-                           GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
+REGISTER_TYPED_TEST_SUITE_P(DwarfEhFrameTest, GetFdeCieFromOffset32, GetFdeCieFromOffset64);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index 4240419..78608e3 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -30,10 +30,10 @@
 namespace unwindstack {
 
 template <typename TypeParam>
-class MockDwarfEhFrameWithHdr : public DwarfEhFrameWithHdr<TypeParam> {
+class TestDwarfEhFrameWithHdr : public DwarfEhFrameWithHdr<TypeParam> {
  public:
-  MockDwarfEhFrameWithHdr(Memory* memory) : DwarfEhFrameWithHdr<TypeParam>(memory) {}
-  ~MockDwarfEhFrameWithHdr() = default;
+  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; }
@@ -64,16 +64,16 @@
  protected:
   void SetUp() override {
     memory_.Clear();
-    eh_frame_ = new MockDwarfEhFrameWithHdr<TypeParam>(&memory_);
+    eh_frame_ = new TestDwarfEhFrameWithHdr<TypeParam>(&memory_);
     ResetLogs();
   }
 
   void TearDown() override { delete eh_frame_; }
 
   MemoryFake memory_;
-  MockDwarfEhFrameWithHdr<TypeParam>* eh_frame_ = nullptr;
+  TestDwarfEhFrameWithHdr<TypeParam>* eh_frame_ = nullptr;
 };
-TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest);
+TYPED_TEST_SUITE_P(DwarfEhFrameWithHdrTest);
 
 // NOTE: All test class variables need to be referenced as this->.
 
@@ -83,7 +83,7 @@
   this->memory_.SetData16(0x1004, 0x500);
   this->memory_.SetData32(0x1006, 126);
 
-  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100));
+  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());
@@ -95,21 +95,128 @@
   EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
   EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
 
+  // Verify a zero table entry size fails to init.
+  this->memory_.SetData8(0x1003, 0x1);
+  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->LastErrorCode());
+  // Reset the value back to the original.
+  this->memory_.SetData8(0x1003, DW_EH_PE_sdata4);
+
   // Verify a zero fde count fails to init.
   this->memory_.SetData32(0x1006, 0);
-  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+  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));
+  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));
+  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);
@@ -123,6 +230,7 @@
   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);
@@ -134,8 +242,8 @@
 
   auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
   ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x1380U, info->pc);
-  EXPECT_EQ(0x1540U, info->offset);
+  EXPECT_EQ(0x340U, info->pc);
+  EXPECT_EQ(0x500U, info->offset);
 }
 
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_read_datarel) {
@@ -175,9 +283,8 @@
   EXPECT_EQ(0x500U, info->offset);
 }
 
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetBinary_verify) {
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_verify) {
   this->eh_frame_->TestSetTableEntrySize(0x10);
-  this->eh_frame_->TestSetFdeCount(10);
 
   typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo info;
   for (size_t i = 0; i < 10; i++) {
@@ -187,105 +294,42 @@
   }
 
   uint64_t fde_offset;
-  EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x100, &fde_offset, 10));
+  this->eh_frame_->TestSetFdeCount(10);
+  EXPECT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
   // 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++) {
+    SCOPED_TRACE(testing::Message() << "Failed at index " << 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;
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc, &fde_offset));
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset));
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset));
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
   }
+
   // Odd number of elements.
+  this->eh_frame_->TestSetFdeCount(9);
   for (size_t i = 0; i < 9; i++) {
+    SCOPED_TRACE(testing::Message() << "Failed at index " << 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;
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc, &fde_offset));
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset));
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset));
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
   }
 }
 
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetBinary_index_fail) {
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_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());
+  EXPECT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset));
 }
 
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_fail_fde_count) {
@@ -296,7 +340,7 @@
   ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
 }
 
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_binary_search) {
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_search) {
   this->eh_frame_->TestSetTableEntrySize(16);
   this->eh_frame_->TestSetFdeCount(10);
 
@@ -316,35 +360,11 @@
   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_.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_.SetMemory(0xf008, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
 
   // FDE 32 information.
   this->memory_.SetData32(0x14000, 0x20);
@@ -381,11 +401,7 @@
   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_.SetMemory(0x6014, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
 
   // FDE 64 information.
   this->memory_.SetData32(0x8000, 0xffffffff);
@@ -430,16 +446,14 @@
   ASSERT_EQ(nullptr, this->eh_frame_->GetFdeFromPc(0x800));
 }
 
-REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest, Init, 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);
+REGISTER_TYPED_TEST_SUITE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias, GetFdes,
+                            GetFdeInfoFromIndex_expect_cache_fail, GetFdeInfoFromIndex_read_pcrel,
+                            GetFdeInfoFromIndex_read_datarel, GetFdeInfoFromIndex_cached,
+                            GetFdeOffsetFromPc_verify, GetFdeOffsetFromPc_index_fail,
+                            GetFdeOffsetFromPc_fail_fde_count, GetFdeOffsetFromPc_search,
+                            GetCieFde32, GetCieFde64, GetFdeFromPc_fde_not_found);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameWithHdrTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameWithHdrTest, DwarfEhFrameWithHdrTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfEhFrameWithHdrTest, DwarfEhFrameWithHdrTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfMemoryTest.cpp b/libunwindstack/tests/DwarfMemoryTest.cpp
index f12d2fe..650e965 100644
--- a/libunwindstack/tests/DwarfMemoryTest.cpp
+++ b/libunwindstack/tests/DwarfMemoryTest.cpp
@@ -54,6 +54,8 @@
   void ReadEncodedValue_overflow();
   template <typename AddressType>
   void ReadEncodedValue_high_bit_set();
+  template <typename AddressType>
+  void ReadEncodedValue_all();
 
   MemoryFake memory_;
   std::unique_ptr<DwarfMemory> dwarf_mem_;
@@ -457,6 +459,27 @@
   ReadEncodedValue_high_bit_set<uint64_t>();
 }
 
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_all() {
+  MemoryFakeAlwaysReadZero memory;
+  DwarfMemory dwarf_mem(&memory);
+
+  for (size_t i = 0; i <= 0xff; i++) {
+    uint64_t value;
+    if (dwarf_mem.ReadEncodedValue<AddressType>(static_cast<uint8_t>(i), &value)) {
+      ASSERT_EQ(0U, value);
+    }
+  }
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_all_uint32_t) {
+  ReadEncodedValue_all<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_all_uint64_t) {
+  ReadEncodedValue_all<uint64_t>();
+}
+
 TEST_F(DwarfMemoryTest, AdjustEncodedValue_absptr) {
   uint64_t value = 0x1234;
   ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x00, &value));
diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp
index 3f09dd8..f4ade5d 100644
--- a/libunwindstack/tests/DwarfOpLogTest.cpp
+++ b/libunwindstack/tests/DwarfOpLogTest.cpp
@@ -48,7 +48,7 @@
   std::unique_ptr<DwarfMemory> mem_;
   std::unique_ptr<DwarfOp<TypeParam>> op_;
 };
-TYPED_TEST_CASE_P(DwarfOpLogTest);
+TYPED_TEST_SUITE_P(DwarfOpLogTest);
 
 TYPED_TEST_P(DwarfOpLogTest, multiple_ops) {
   // Multi operation opcodes.
@@ -65,9 +65,9 @@
   ASSERT_EQ(expected, lines);
 }
 
-REGISTER_TYPED_TEST_CASE_P(DwarfOpLogTest, multiple_ops);
+REGISTER_TYPED_TEST_SUITE_P(DwarfOpLogTest, multiple_ops);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfOpLogTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpLogTest, DwarfOpLogTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfOpLogTest, DwarfOpLogTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
index d424d5f..0898ec0 100644
--- a/libunwindstack/tests/DwarfOpTest.cpp
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -48,7 +48,7 @@
   std::unique_ptr<DwarfMemory> mem_;
   std::unique_ptr<DwarfOp<TypeParam>> op_;
 };
-TYPED_TEST_CASE_P(DwarfOpTest);
+TYPED_TEST_SUITE_P(DwarfOpTest);
 
 TYPED_TEST_P(DwarfOpTest, decode) {
   // Memory error.
@@ -1571,15 +1571,16 @@
   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);
+REGISTER_TYPED_TEST_SUITE_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);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfOpTest, DwarfOpTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index c340291..b386ef4 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -16,7 +16,6 @@
 
 #include <stdint.h>
 
-#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include <unwindstack/DwarfError.h>
@@ -31,42 +30,27 @@
 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(GetCieOffsetFromFde32, uint64_t(uint32_t));
+  uint64_t GetCieOffsetFromFde32(uint32_t) { return 0; }
 
-  MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
+  uint64_t GetCieOffsetFromFde64(uint64_t) { return 0; }
 
-  MOCK_METHOD1(AdjustPcFromFde, uint64_t(uint64_t));
-
-  void TestSetCie32Value(uint32_t value32) { this->cie32_value_ = value32; }
-
-  void TestSetCie64Value(uint64_t value64) { this->cie64_value_ = value64; }
-
-  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_.code = DWARF_ERROR_NONE; }
 };
 
@@ -75,21 +59,41 @@
  protected:
   void SetUp() override {
     memory_.Clear();
-    section_ = new MockDwarfSectionImpl<TypeParam>(&memory_);
+    section_ = new TestDwarfSectionImpl<TypeParam>(&memory_);
     ResetLogs();
-    section_->TestSetCie32Value(static_cast<uint32_t>(-1));
-    section_->TestSetCie64Value(static_cast<uint64_t>(-1));
   }
 
   void TearDown() override { delete section_; }
 
   MemoryFake memory_;
-  MockDwarfSectionImpl<TypeParam>* section_ = nullptr;
+  TestDwarfSectionImpl<TypeParam>* section_ = nullptr;
 };
-TYPED_TEST_CASE_P(DwarfSectionImplTest);
+TYPED_TEST_SUITE_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};
   RegsImplFake<TypeParam> regs(10);
@@ -487,334 +491,6 @@
   EXPECT_EQ(0x80000000U, 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_->LastErrorCode());
-  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
-  this->section_->TestClearError();
-  ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
-  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
-}
-
-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);
-
-  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_->LastErrorCode());
-
-  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_->LastErrorCode());
-
-  this->memory_.SetData8(0x5008, 0x2);
-  this->section_->TestClearError();
-  ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->LastErrorCode());
-
-  this->memory_.SetData8(0x5008, 0x5);
-  this->section_->TestClearError();
-  ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->LastErrorCode());
-}
-
-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);
-
-  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, 0xffffffffffffffffULL);
-  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);
-
-  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);
-
-  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});
-
-  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});
-
-  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_->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, 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_, 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_, 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_, 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_, 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;
@@ -884,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"
@@ -895,20 +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_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, 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_SUITE_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);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfSectionImplTest, DwarfSectionImplTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index 071d2df..d754fcc 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -30,24 +30,18 @@
   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_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));
@@ -57,112 +51,60 @@
 
 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(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(false));
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(nullptr));
 
   bool finished;
-  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr, &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));
 
   bool finished;
-  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr, &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));
 
   bool finished;
-  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr, &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, ::testing::_))
+  EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
       .WillOnce(::testing::Return(true));
 
   bool finished;
-  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
+  ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
 }
 
 static bool MockGetCfaLocationInfo(::testing::Unused, const DwarfFde* fde,
@@ -173,64 +115,53 @@
 }
 
 TEST_F(DwarfSectionTest, Step_cache) {
-  MockDwarfSection mock_section(&memory_);
-
   DwarfCie cie{};
   DwarfFde fde{};
   fde.pc_start = 0x500;
   fde.pc_end = 0x2000;
   fde.cie = &cie;
 
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
-
-  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
+  EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
       .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
 
   MemoryFake process;
-  EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+  EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
       .WillRepeatedly(::testing::Return(true));
 
   bool finished;
-  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
-  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
-  ASSERT_TRUE(mock_section.Step(0x1500, nullptr, &process, &finished));
+  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) {
-  MockDwarfSection mock_section(&memory_);
-
   DwarfCie cie{};
   DwarfFde fde0{};
   fde0.pc_start = 0x1000;
   fde0.pc_end = 0x2000;
   fde0.cie = &cie;
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde0));
-  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde0, ::testing::_))
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde0));
+  EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde0, ::testing::_))
       .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
 
   MemoryFake process;
-  EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+  EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
       .WillRepeatedly(::testing::Return(true));
 
   bool finished;
-  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
+  ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
 
   DwarfFde fde1{};
   fde1.pc_start = 0x500;
   fde1.pc_end = 0x800;
   fde1.cie = &cie;
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x600, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde1));
-  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x600, &fde1, ::testing::_))
+  EXPECT_CALL(*section_, GetFdeFromPc(0x600)).WillOnce(::testing::Return(&fde1));
+  EXPECT_CALL(*section_, GetCfaLocationInfo(0x600, &fde1, ::testing::_))
       .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
 
-  ASSERT_TRUE(mock_section.Step(0x600, nullptr, &process, &finished));
-  ASSERT_TRUE(mock_section.Step(0x700, nullptr, &process, &finished));
+  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
index 89331ea..5735858 100644
--- a/libunwindstack/tests/ElfCacheTest.cpp
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -18,7 +18,6 @@
 #include <unistd.h>
 
 #include <android-base/file.h>
-#include <android-base/test_utils.h>
 
 #include <gtest/gtest.h>
 
@@ -32,7 +31,7 @@
 
 class ElfCacheTest : public ::testing::Test {
  protected:
-  static void SetUpTestCase() { memory_.reset(new MemoryFake); }
+  static void SetUpTestSuite() { memory_.reset(new MemoryFake); }
 
   void SetUp() override { Elf::SetCachingEnabled(true); }
 
@@ -79,12 +78,12 @@
 
   uint64_t start = 0x1000;
   uint64_t end = 0x20000;
-  MapInfo info1(start, end, 0, 0x5, tf.path);
-  MapInfo info2(start, end, 0, 0x5, tf.path);
+  MapInfo info1(nullptr, start, end, 0, 0x5, tf.path);
+  MapInfo info2(nullptr, start, end, 0, 0x5, tf.path);
 
-  Elf* elf1 = info1.GetElf(memory_, true);
+  Elf* elf1 = info1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf1->valid());
-  Elf* elf2 = info2.GetElf(memory_, true);
+  Elf* elf2 = info2.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf2->valid());
 
   if (cache_enabled) {
@@ -120,22 +119,22 @@
   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);
+  MapInfo info0_1(nullptr, start, end, 0, 0x5, tf.path);
+  MapInfo info0_2(nullptr, 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);
+  MapInfo info100_1(nullptr, start, end, 0x100, 0x5, tf.path);
+  MapInfo info100_2(nullptr, 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);
+  MapInfo info200_1(nullptr, start, end, 0x200, 0x5, tf.path);
+  MapInfo info200_2(nullptr, 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);
+  MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
+  MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
 
-  Elf* elf0_1 = info0_1.GetElf(memory_, true);
+  Elf* elf0_1 = info0_1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf0_1->valid());
   EXPECT_EQ(ARCH_ARM, elf0_1->arch());
-  Elf* elf0_2 = info0_2.GetElf(memory_, true);
+  Elf* elf0_2 = info0_2.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf0_2->valid());
   EXPECT_EQ(ARCH_ARM, elf0_2->arch());
   EXPECT_EQ(0U, info0_1.elf_offset);
@@ -146,10 +145,10 @@
     EXPECT_NE(elf0_1, elf0_2);
   }
 
-  Elf* elf100_1 = info100_1.GetElf(memory_, true);
+  Elf* elf100_1 = info100_1.GetElf(memory_, ARCH_X86);
   ASSERT_TRUE(elf100_1->valid());
   EXPECT_EQ(ARCH_X86, elf100_1->arch());
-  Elf* elf100_2 = info100_2.GetElf(memory_, true);
+  Elf* elf100_2 = info100_2.GetElf(memory_, ARCH_X86);
   ASSERT_TRUE(elf100_2->valid());
   EXPECT_EQ(ARCH_X86, elf100_2->arch());
   EXPECT_EQ(0U, info100_1.elf_offset);
@@ -160,10 +159,10 @@
     EXPECT_NE(elf100_1, elf100_2);
   }
 
-  Elf* elf200_1 = info200_1.GetElf(memory_, true);
+  Elf* elf200_1 = info200_1.GetElf(memory_, ARCH_X86_64);
   ASSERT_TRUE(elf200_1->valid());
   EXPECT_EQ(ARCH_X86_64, elf200_1->arch());
-  Elf* elf200_2 = info200_2.GetElf(memory_, true);
+  Elf* elf200_2 = info200_2.GetElf(memory_, ARCH_X86_64);
   ASSERT_TRUE(elf200_2->valid());
   EXPECT_EQ(ARCH_X86_64, elf200_2->arch());
   EXPECT_EQ(0U, info200_1.elf_offset);
@@ -174,10 +173,10 @@
     EXPECT_NE(elf200_1, elf200_2);
   }
 
-  Elf* elf300_1 = info300_1.GetElf(memory_, true);
+  Elf* elf300_1 = info300_1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf300_1->valid());
   EXPECT_EQ(ARCH_ARM, elf300_1->arch());
-  Elf* elf300_2 = info300_2.GetElf(memory_, true);
+  Elf* elf300_2 = info300_2.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf300_2->valid());
   EXPECT_EQ(ARCH_ARM, elf300_2->arch());
   EXPECT_EQ(0x300U, info300_1.elf_offset);
@@ -217,15 +216,15 @@
   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);
+  MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
+  MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
+  MapInfo info400_1(nullptr, start, end, 0x400, 0x5, tf.path);
+  MapInfo info400_2(nullptr, start, end, 0x400, 0x5, tf.path);
 
-  Elf* elf300_1 = info300_1.GetElf(memory_, true);
+  Elf* elf300_1 = info300_1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf300_1->valid());
   EXPECT_EQ(ARCH_ARM, elf300_1->arch());
-  Elf* elf300_2 = info300_2.GetElf(memory_, true);
+  Elf* elf300_2 = info300_2.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf300_2->valid());
   EXPECT_EQ(ARCH_ARM, elf300_2->arch());
   EXPECT_EQ(0x300U, info300_1.elf_offset);
@@ -236,10 +235,10 @@
     EXPECT_NE(elf300_1, elf300_2);
   }
 
-  Elf* elf400_1 = info400_1.GetElf(memory_, true);
+  Elf* elf400_1 = info400_1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf400_1->valid());
   EXPECT_EQ(ARCH_ARM, elf400_1->arch());
-  Elf* elf400_2 = info400_2.GetElf(memory_, true);
+  Elf* elf400_2 = info400_2.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf400_2->valid());
   EXPECT_EQ(ARCH_ARM, elf400_2->arch());
   EXPECT_EQ(0x400U, info400_1.elf_offset);
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
index 66207db..3d5ddd6 100644
--- a/libunwindstack/tests/ElfFake.cpp
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -32,7 +32,7 @@
 std::deque<FunctionData> ElfInterfaceFake::functions_;
 std::deque<StepData> ElfInterfaceFake::steps_;
 
-bool ElfInterfaceFake::GetFunctionName(uint64_t, uint64_t, std::string* name, uint64_t* offset) {
+bool ElfInterfaceFake::GetFunctionName(uint64_t, std::string* name, uint64_t* offset) {
   if (functions_.empty()) {
     return false;
   }
@@ -52,7 +52,7 @@
   return true;
 }
 
-bool ElfInterfaceFake::Step(uint64_t, uint64_t, Regs* regs, Memory*, bool* finished) {
+bool ElfInterfaceFake::Step(uint64_t, Regs* regs, Memory*, bool* finished) {
   if (steps_.empty()) {
     return false;
   }
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index e232986..bd3083c 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -67,18 +67,24 @@
   virtual ~ElfInterfaceFake() = default;
 
   bool Init(uint64_t*) override { return false; }
-  void InitHeaders() override {}
-  bool GetSoname(std::string*) override { return false; }
+  void InitHeaders(uint64_t) override {}
+  std::string GetSoname() override { return fake_soname_; }
 
-  bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override;
+  bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
   bool GetGlobalVariable(const std::string&, uint64_t*) override;
+  std::string GetBuildID() override { return fake_build_id_; }
 
-  bool Step(uint64_t, uint64_t, Regs*, Memory*, bool*) override;
+  bool Step(uint64_t, Regs*, Memory*, bool*) override;
 
   void FakeSetGlobalVariable(const std::string& global, uint64_t offset) {
     globals_[global] = offset;
   }
 
+  void FakeSetBuildID(std::string& build_id) { fake_build_id_ = build_id; }
+  void FakeSetBuildID(const char* build_id) { fake_build_id_ = build_id; }
+
+  void FakeSetSoname(const char* soname) { fake_soname_ = soname; }
+
   static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
   static void FakePushStepData(const StepData data) { steps_.push_back(data); }
 
@@ -93,6 +99,8 @@
 
  private:
   std::unordered_map<std::string, uint64_t> globals_;
+  std::string fake_build_id_;
+  std::string fake_soname_;
 
   static std::deque<FunctionData> functions_;
   static std::deque<StepData> steps_;
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index 70a52ad..43c6a97 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -242,59 +242,21 @@
   ASSERT_EQ(0xa020U, entries[4]);
 }
 
-TEST_F(ElfInterfaceArmTest, HandleType_not_arm_exidx) {
+TEST_F(ElfInterfaceArmTest, HandleUnknownType_arm_exidx) {
   ElfInterfaceArmFake interface(&memory_);
 
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_NULL, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOAD, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_DYNAMIC, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_INTERP, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_NOTE, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_SHLIB, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_PHDR, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_TLS, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOOS, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_HIOS, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOPROC, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_HIPROC, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_EH_FRAME, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_STACK, 0));
-}
-
-TEST_F(ElfInterfaceArmTest, HandleType_arm_exidx) {
-  ElfInterfaceArmFake interface(&memory_);
-
-  Elf32_Phdr phdr;
   interface.FakeSetStartOffset(0x1000);
   interface.FakeSetTotalEntries(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, 0));
-  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, 0));
+  // 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, 0));
+  interface.HandleUnknownType(0x70000001, 0x2000, 320);
   ASSERT_EQ(0x2000U, interface.start_offset());
-  ASSERT_EQ(320U, interface.total_entries());
-
-  // Non-zero load bias.
-  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0x1000));
-  ASSERT_EQ(0x1000U, interface.start_offset());
-  ASSERT_EQ(320U, interface.total_entries());
+  ASSERT_EQ(40U, interface.total_entries());
 }
 
 TEST_F(ElfInterfaceArmTest, StepExidx) {
@@ -302,7 +264,7 @@
 
   // FindEntry fails.
   bool finished;
-  ASSERT_FALSE(interface.StepExidx(0x7000, 0, nullptr, nullptr, &finished));
+  ASSERT_FALSE(interface.StepExidx(0x7000, nullptr, nullptr, &finished));
   EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
 
   // ExtractEntry should fail.
@@ -316,18 +278,18 @@
   regs[ARM_REG_LR] = 0x20000;
   regs.set_sp(regs[ARM_REG_SP]);
   regs.set_pc(0x1234);
-  ASSERT_FALSE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  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, 0, &regs, &process_memory_, &finished));
+  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, 0, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
   ASSERT_FALSE(finished);
   ASSERT_EQ(0x1000U, regs.sp());
@@ -336,11 +298,13 @@
   ASSERT_EQ(0x20000U, regs[ARM_REG_PC]);
 
   // Load bias is non-zero.
-  ASSERT_TRUE(interface.StepExidx(0x8000, 0x1000, &regs, &process_memory_, &finished));
+  interface.set_load_bias(0x1000);
+  ASSERT_TRUE(interface.StepExidx(0x8000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
 
   // Pc too small.
-  ASSERT_FALSE(interface.StepExidx(0x8000, 0x9000, &regs, &process_memory_, &finished));
+  interface.set_load_bias(0x9000);
+  ASSERT_FALSE(interface.StepExidx(0x8000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
 }
 
@@ -362,7 +326,7 @@
 
   // Everything should pass.
   bool finished;
-  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
   ASSERT_FALSE(finished);
   ASSERT_EQ(0x10004U, regs.sp());
@@ -386,7 +350,7 @@
   regs.set_pc(0x1234);
 
   bool finished;
-  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
   ASSERT_TRUE(finished);
   ASSERT_EQ(0x10000U, regs.sp());
@@ -409,7 +373,7 @@
   regs.set_pc(0x1234);
 
   bool finished;
-  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
   ASSERT_TRUE(finished);
   ASSERT_EQ(0x10000U, regs.sp());
@@ -436,7 +400,7 @@
   regs.set_pc(0x1234);
 
   bool finished;
-  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
   ASSERT_TRUE(finished);
   ASSERT_EQ(0U, regs.pc());
@@ -449,7 +413,7 @@
   regs.set_sp(regs[ARM_REG_SP]);
   regs.set_pc(0x1234);
 
-  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
   ASSERT_TRUE(finished);
   ASSERT_EQ(0U, regs.pc());
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index bf97e30..cdc927a 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -97,9 +97,15 @@
   template <typename ElfType>
   void InitHeadersDebugFrameFail();
 
+  template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
+  void InitProgramHeadersMalformed();
+
   template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
   void InitSectionHeadersMalformed();
 
+  template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+  void InitSectionHeadersMalformedSymData();
+
   template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType>
   void InitSectionHeaders(uint64_t entry_size);
 
@@ -110,14 +116,28 @@
   void InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
                uint64_t sym_offset, const char* name);
 
+  template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+  void BuildID();
+
+  template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+  void BuildIDTwoNotes();
+
+  template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+  void BuildIDSectionTooSmallForName();
+
+  template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+  void BuildIDSectionTooSmallForDesc();
+
+  template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+  void BuildIDSectionTooSmallForHeader();
+
   MemoryFake memory_;
 };
 
 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;
@@ -132,15 +152,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;
@@ -172,15 +190,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;
@@ -241,15 +257,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;
@@ -312,15 +326,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;
@@ -371,17 +383,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;
@@ -444,18 +454,16 @@
 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.
@@ -480,8 +488,7 @@
 
 template <typename Ehdr, typename Phdr, typename Shdr, typename Dyn>
 void ElfInterfaceTest::SonameInit(SonameTestEnum test_type) {
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Ehdr ehdr = {};
   ehdr.e_shoff = 0x200;
   ehdr.e_shnum = 2;
   ehdr.e_shentsize = sizeof(Shdr);
@@ -490,8 +497,7 @@
   ehdr.e_phentsize = sizeof(Phdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  Shdr shdr;
-  memset(&shdr, 0, sizeof(shdr));
+  Shdr shdr = {};
   shdr.sh_type = SHT_STRTAB;
   if (test_type == SONAME_MISSING_MAP) {
     shdr.sh_addr = 0x20100;
@@ -501,8 +507,7 @@
   shdr.sh_offset = 0x10000;
   memory_.SetMemory(0x200 + sizeof(shdr), &shdr, sizeof(shdr));
 
-  Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
+  Phdr phdr = {};
   phdr.p_type = PT_DYNAMIC;
   phdr.p_offset = 0x2000;
   phdr.p_memsz = sizeof(Dyn) * 3;
@@ -550,9 +555,7 @@
   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());
+  ASSERT_EQ("fake_soname.so", elf->GetSoname());
 }
 
 TEST_F(ElfInterfaceTest, elf32_soname) {
@@ -573,8 +576,7 @@
   ASSERT_TRUE(elf->Init(&load_bias));
   EXPECT_EQ(0U, load_bias);
 
-  std::string name;
-  ASSERT_FALSE(elf->GetSoname(&name));
+  ASSERT_EQ("", elf->GetSoname());
 }
 
 TEST_F(ElfInterfaceTest, elf32_soname_after_dt_null) {
@@ -595,8 +597,7 @@
   ASSERT_TRUE(elf->Init(&load_bias));
   EXPECT_EQ(0U, load_bias);
 
-  std::string name;
-  ASSERT_FALSE(elf->GetSoname(&name));
+  ASSERT_EQ("", elf->GetSoname());
 }
 
 TEST_F(ElfInterfaceTest, elf32_soname_size) {
@@ -619,8 +620,7 @@
   ASSERT_TRUE(elf->Init(&load_bias));
   EXPECT_EQ(0U, load_bias);
 
-  std::string name;
-  ASSERT_FALSE(elf->GetSoname(&name));
+  ASSERT_EQ("", elf->GetSoname());
 }
 
 TEST_F(ElfInterfaceTest, elf32_soname_missing_map) {
@@ -647,7 +647,7 @@
   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);
@@ -672,15 +672,14 @@
 
   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);
@@ -694,62 +693,34 @@
   InitHeadersDebugFrame<ElfInterface64Fake>();
 }
 
-template <typename ElfType>
-void ElfInterfaceTest::InitHeadersEhFrameFail() {
-  ElfType elf(&memory_);
+template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitProgramHeadersMalformed() {
+  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
 
-  elf.FakeSetEhFrameOffset(0x1000);
-  elf.FakeSetEhFrameSize(0x100);
-  elf.FakeSetDebugFrameOffset(0);
-  elf.FakeSetDebugFrameSize(0);
+  Ehdr ehdr = {};
+  ehdr.e_phoff = 0x100;
+  ehdr.e_phnum = 3;
+  ehdr.e_phentsize = sizeof(Phdr);
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  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);
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
 }
 
-TEST_F(ElfInterfaceTest, init_headers_eh_frame32_fail) {
-  InitHeadersEhFrameFail<ElfInterface32Fake>();
+TEST_F(ElfInterfaceTest, init_program_headers_malformed32) {
+  InitProgramHeadersMalformed<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, init_headers_eh_frame64_fail) {
-  InitHeadersEhFrameFail<ElfInterface64Fake>();
-}
-
-template <typename ElfType>
-void ElfInterfaceTest::InitHeadersDebugFrameFail() {
-  ElfType elf(&memory_);
-
-  elf.FakeSetEhFrameOffset(0);
-  elf.FakeSetEhFrameSize(0);
-  elf.FakeSetDebugFrameOffset(0x1000);
-  elf.FakeSetDebugFrameSize(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<ElfInterface32Fake>();
-}
-
-TEST_F(ElfInterfaceTest, init_headers_debug_frame64_fail) {
-  InitHeadersDebugFrameFail<ElfInterface64Fake>();
+TEST_F(ElfInterfaceTest, init_program_headers_malformed64) {
+  InitProgramHeadersMalformed<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>();
 }
 
 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);
@@ -768,23 +739,95 @@
   InitSectionHeadersMalformed<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
 }
 
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersMalformedSymData() {
+  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+  uint64_t offset = 0x1000;
+
+  Ehdr ehdr = {};
+  ehdr.e_shoff = offset;
+  ehdr.e_shnum = 5;
+  ehdr.e_shentsize = sizeof(Shdr);
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  offset += ehdr.e_shentsize;
+
+  Shdr shdr = {};
+  shdr.sh_type = SHT_SYMTAB;
+  shdr.sh_link = 4;
+  shdr.sh_addr = 0x5000;
+  shdr.sh_offset = 0x5000;
+  shdr.sh_entsize = 0x100;
+  shdr.sh_size = shdr.sh_entsize * 10;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_DYNSYM;
+  shdr.sh_link = 10;
+  shdr.sh_addr = 0x6000;
+  shdr.sh_offset = 0x6000;
+  shdr.sh_entsize = 0x100;
+  shdr.sh_size = shdr.sh_entsize * 10;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_DYNSYM;
+  shdr.sh_link = 2;
+  shdr.sh_addr = 0x6000;
+  shdr.sh_offset = 0x6000;
+  shdr.sh_entsize = 0x100;
+  shdr.sh_size = shdr.sh_entsize * 10;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  // The string data for the entries.
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 0x20000;
+  shdr.sh_offset = 0xf000;
+  shdr.sh_size = 0x1000;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  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());
+  EXPECT_EQ(0U, elf->gnu_debugdata_size());
+
+  std::string name;
+  uint64_t name_offset;
+  ASSERT_FALSE(elf->GetFunctionName(0x90010, &name, &name_offset));
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_malformed_symdata32) {
+  InitSectionHeadersMalformedSymData<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_malformed_symdata64) {
+  InitSectionHeadersMalformedSymData<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
+}
+
 template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType>
 void ElfInterfaceTest::InitSectionHeaders(uint64_t entry_size) {
   std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
 
   uint64_t offset = 0x1000;
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Ehdr ehdr = {};
   ehdr.e_shoff = offset;
-  ehdr.e_shnum = 10;
+  ehdr.e_shnum = 5;
   ehdr.e_shentsize = entry_size;
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
   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;
@@ -833,10 +876,10 @@
   // Look in the first symbol table.
   std::string name;
   uint64_t name_offset;
-  ASSERT_TRUE(elf->GetFunctionName(0x90010, 0, &name, &name_offset));
+  ASSERT_TRUE(elf->GetFunctionName(0x90010, &name, &name_offset));
   EXPECT_EQ("function_one", name);
   EXPECT_EQ(16U, name_offset);
-  ASSERT_TRUE(elf->GetFunctionName(0xd0020, 0, &name, &name_offset));
+  ASSERT_TRUE(elf->GetFunctionName(0xd0020, &name, &name_offset));
   EXPECT_EQ("function_two", name);
   EXPECT_EQ(32U, name_offset);
 }
@@ -863,18 +906,16 @@
 
   uint64_t offset = 0x2000;
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Ehdr ehdr = {};
   ehdr.e_shoff = offset;
-  ehdr.e_shnum = 10;
+  ehdr.e_shnum = 7;
   ehdr.e_shentsize = sizeof(Shdr);
   ehdr.e_shstrndx = 2;
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
   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;
@@ -927,10 +968,19 @@
   memory_.SetMemory(offset, &shdr, sizeof(shdr));
   offset += ehdr.e_shentsize;
 
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_NOTE;
+  shdr.sh_name = 0x500;
+  shdr.sh_offset = 0xb000;
+  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"));
+  memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
@@ -943,6 +993,8 @@
   EXPECT_EQ(0x800U, elf->eh_frame_size());
   EXPECT_EQ(0xa000U, elf->eh_frame_hdr_offset());
   EXPECT_EQ(0xf00U, elf->eh_frame_hdr_size());
+  EXPECT_EQ(0xb000U, elf->gnu_build_id_offset());
+  EXPECT_EQ(0xf00U, elf->gnu_build_id_size());
 }
 
 TEST_F(ElfInterfaceTest, init_section_headers_offsets32) {
@@ -956,15 +1008,13 @@
 TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load) {
   std::unique_ptr<ElfInterface> elf(new ElfInterface32(&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_LOAD;
   phdr.p_vaddr = 0;
   phdr.p_memsz = 0x10000;
@@ -984,15 +1034,13 @@
 TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load_non_zero_load_bias) {
   std::unique_ptr<ElfInterface> elf(new ElfInterface32(&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_LOAD;
   phdr.p_vaddr = 0x2000;
   phdr.p_memsz = 0x10000;
@@ -1017,16 +1065,14 @@
 
   uint64_t sh_offset = 0x100;
 
-  Elf32_Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  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;
-  memset(&shdr, 0, sizeof(shdr));
+  Elf32_Shdr shdr = {};
   shdr.sh_type = SHT_NULL;
   memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
 
@@ -1051,11 +1097,7 @@
   // CIE 32.
   memory_.SetData32(0x600, 0xfc);
   memory_.SetData32(0x604, 0xffffffff);
-  memory_.SetData8(0x608, 1);
-  memory_.SetData8(0x609, '\0');
-  memory_.SetData8(0x60a, 0x4);
-  memory_.SetData8(0x60b, 0x4);
-  memory_.SetData8(0x60c, 0x1);
+  memory_.SetMemory(0x608, std::vector<uint8_t>{1, '\0', 4, 4, 1});
 
   // FDE 32.
   memory_.SetData32(0x700, 0xfc);
@@ -1065,7 +1107,7 @@
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  elf->InitHeaders();
+  elf->InitHeaders(0);
   EXPECT_EQ(0U, load_bias);
   EXPECT_FALSE(elf->IsValidPc(0));
   EXPECT_FALSE(elf->IsValidPc(0x20ff));
@@ -1080,16 +1122,14 @@
 
   uint64_t sh_offset = 0x100;
 
-  Elf32_Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  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;
-  memset(&shdr, 0, sizeof(shdr));
+  Elf32_Shdr shdr = {};
   shdr.sh_type = SHT_NULL;
   memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
 
@@ -1114,11 +1154,7 @@
   // CIE 32.
   memory_.SetData32(0x600, 0xfc);
   memory_.SetData32(0x604, 0);
-  memory_.SetData8(0x608, 1);
-  memory_.SetData8(0x609, '\0');
-  memory_.SetData8(0x60a, 0x4);
-  memory_.SetData8(0x60b, 0x4);
-  memory_.SetData8(0x60c, 0x1);
+  memory_.SetMemory(0x608, std::vector<uint8_t>{1, '\0', 4, 4, 1});
 
   // FDE 32.
   memory_.SetData32(0x700, 0xfc);
@@ -1128,7 +1164,7 @@
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  elf->InitHeaders();
+  elf->InitHeaders(0);
   EXPECT_EQ(0U, load_bias);
   EXPECT_FALSE(elf->IsValidPc(0));
   EXPECT_FALSE(elf->IsValidPc(0x27ff));
@@ -1138,4 +1174,325 @@
   EXPECT_FALSE(elf->IsValidPc(0x2a00));
 }
 
+template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+void ElfInterfaceTest::BuildID() {
+  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+  uint64_t offset = 0x2000;
+
+  Ehdr ehdr = {};
+  ehdr.e_shoff = offset;
+  ehdr.e_shnum = 3;
+  ehdr.e_shentsize = sizeof(Shdr);
+  ehdr.e_shstrndx = 2;
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  offset += ehdr.e_shentsize;
+
+  char note_section[128];
+  Nhdr note_header = {};
+  note_header.n_namesz = 4;  // "GNU"
+  note_header.n_descsz = 7;  // "BUILDID"
+  note_header.n_type = NT_GNU_BUILD_ID;
+  memcpy(&note_section, &note_header, sizeof(note_header));
+  size_t note_offset = sizeof(note_header);
+  // The note information contains the GNU and trailing '\0'.
+  memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
+  note_offset += sizeof("GNU");
+  // This part of the note does not contain any trailing '\0'.
+  memcpy(&note_section[note_offset], "BUILDID", 7);
+  note_offset += 8;
+
+  Shdr shdr = {};
+  shdr.sh_type = SHT_NOTE;
+  shdr.sh_name = 0x500;
+  shdr.sh_offset = 0xb000;
+  shdr.sh_size = sizeof(note_section);
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  // The string data for section header names.
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 0x20000;
+  shdr.sh_offset = 0xf000;
+  shdr.sh_size = 0x1000;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+  memory_.SetMemory(0xb000, note_section, sizeof(note_section));
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  ASSERT_EQ("BUILDID", elf->GetBuildID());
+}
+
+template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+void ElfInterfaceTest::BuildIDTwoNotes() {
+  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+  uint64_t offset = 0x2000;
+
+  Ehdr ehdr = {};
+  ehdr.e_shoff = offset;
+  ehdr.e_shnum = 3;
+  ehdr.e_shentsize = sizeof(Shdr);
+  ehdr.e_shstrndx = 2;
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  offset += ehdr.e_shentsize;
+
+  char note_section[128];
+  Nhdr note_header = {};
+  note_header.n_namesz = 8;  // "WRONG" aligned to 4
+  note_header.n_descsz = 7;  // "BUILDID"
+  note_header.n_type = NT_GNU_BUILD_ID;
+  memcpy(&note_section, &note_header, sizeof(note_header));
+  size_t note_offset = sizeof(note_header);
+  memcpy(&note_section[note_offset], "WRONG", sizeof("WRONG"));
+  note_offset += 8;
+  // This part of the note does not contain any trailing '\0'.
+  memcpy(&note_section[note_offset], "BUILDID", 7);
+  note_offset += 8;
+
+  note_header.n_namesz = 4;  // "GNU"
+  note_header.n_descsz = 7;  // "BUILDID"
+  note_header.n_type = NT_GNU_BUILD_ID;
+  memcpy(&note_section[note_offset], &note_header, sizeof(note_header));
+  note_offset += sizeof(note_header);
+  // The note information contains the GNU and trailing '\0'.
+  memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
+  note_offset += sizeof("GNU");
+  // This part of the note does not contain any trailing '\0'.
+  memcpy(&note_section[note_offset], "BUILDID", 7);
+  note_offset += 8;
+
+  Shdr shdr = {};
+  shdr.sh_type = SHT_NOTE;
+  shdr.sh_name = 0x500;
+  shdr.sh_offset = 0xb000;
+  shdr.sh_size = sizeof(note_section);
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  // The string data for section header names.
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 0x20000;
+  shdr.sh_offset = 0xf000;
+  shdr.sh_size = 0x1000;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+  memory_.SetMemory(0xb000, note_section, sizeof(note_section));
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  ASSERT_EQ("BUILDID", elf->GetBuildID());
+}
+
+template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+void ElfInterfaceTest::BuildIDSectionTooSmallForName () {
+  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+  uint64_t offset = 0x2000;
+
+  Ehdr ehdr = {};
+  ehdr.e_shoff = offset;
+  ehdr.e_shnum = 3;
+  ehdr.e_shentsize = sizeof(Shdr);
+  ehdr.e_shstrndx = 2;
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  offset += ehdr.e_shentsize;
+
+  char note_section[128];
+  Nhdr note_header = {};
+  note_header.n_namesz = 4;  // "GNU"
+  note_header.n_descsz = 7;  // "BUILDID"
+  note_header.n_type = NT_GNU_BUILD_ID;
+  memcpy(&note_section, &note_header, sizeof(note_header));
+  size_t note_offset = sizeof(note_header);
+  // The note information contains the GNU and trailing '\0'.
+  memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
+  note_offset += sizeof("GNU");
+  // This part of the note does not contain any trailing '\0'.
+  memcpy(&note_section[note_offset], "BUILDID", 7);
+  note_offset += 8;
+
+  Shdr shdr = {};
+  shdr.sh_type = SHT_NOTE;
+  shdr.sh_name = 0x500;
+  shdr.sh_offset = 0xb000;
+  shdr.sh_size = sizeof(note_header) + 1;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  // The string data for section header names.
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 0x20000;
+  shdr.sh_offset = 0xf000;
+  shdr.sh_size = 0x1000;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+  memory_.SetMemory(0xb000, note_section, sizeof(note_section));
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  ASSERT_EQ("", elf->GetBuildID());
+}
+
+template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+void ElfInterfaceTest::BuildIDSectionTooSmallForDesc () {
+  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+  uint64_t offset = 0x2000;
+
+  Ehdr ehdr = {};
+  ehdr.e_shoff = offset;
+  ehdr.e_shnum = 3;
+  ehdr.e_shentsize = sizeof(Shdr);
+  ehdr.e_shstrndx = 2;
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  offset += ehdr.e_shentsize;
+
+  char note_section[128];
+  Nhdr note_header = {};
+  note_header.n_namesz = 4;  // "GNU"
+  note_header.n_descsz = 7;  // "BUILDID"
+  note_header.n_type = NT_GNU_BUILD_ID;
+  memcpy(&note_section, &note_header, sizeof(note_header));
+  size_t note_offset = sizeof(note_header);
+  // The note information contains the GNU and trailing '\0'.
+  memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
+  note_offset += sizeof("GNU");
+  // This part of the note does not contain any trailing '\0'.
+  memcpy(&note_section[note_offset], "BUILDID", 7);
+  note_offset += 8;
+
+  Shdr shdr = {};
+  shdr.sh_type = SHT_NOTE;
+  shdr.sh_name = 0x500;
+  shdr.sh_offset = 0xb000;
+  shdr.sh_size = sizeof(note_header) + sizeof("GNU") + 1;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  // The string data for section header names.
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 0x20000;
+  shdr.sh_offset = 0xf000;
+  shdr.sh_size = 0x1000;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+  memory_.SetMemory(0xb000, note_section, sizeof(note_section));
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  ASSERT_EQ("", elf->GetBuildID());
+}
+
+template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+void ElfInterfaceTest::BuildIDSectionTooSmallForHeader () {
+  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+  uint64_t offset = 0x2000;
+
+  Ehdr ehdr = {};
+  ehdr.e_shoff = offset;
+  ehdr.e_shnum = 3;
+  ehdr.e_shentsize = sizeof(Shdr);
+  ehdr.e_shstrndx = 2;
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  offset += ehdr.e_shentsize;
+
+  char note_section[128];
+  Nhdr note_header = {};
+  note_header.n_namesz = 4;  // "GNU"
+  note_header.n_descsz = 7;  // "BUILDID"
+  note_header.n_type = NT_GNU_BUILD_ID;
+  memcpy(&note_section, &note_header, sizeof(note_header));
+  size_t note_offset = sizeof(note_header);
+  // The note information contains the GNU and trailing '\0'.
+  memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
+  note_offset += sizeof("GNU");
+  // This part of the note does not contain any trailing '\0'.
+  memcpy(&note_section[note_offset], "BUILDID", 7);
+  note_offset += 8;
+
+  Shdr shdr = {};
+  shdr.sh_type = SHT_NOTE;
+  shdr.sh_name = 0x500;
+  shdr.sh_offset = 0xb000;
+  shdr.sh_size = sizeof(note_header) - 1;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  // The string data for section header names.
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 0x20000;
+  shdr.sh_offset = 0xf000;
+  shdr.sh_size = 0x1000;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+  memory_.SetMemory(0xb000, note_section, sizeof(note_section));
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  ASSERT_EQ("", elf->GetBuildID());
+}
+
+TEST_F(ElfInterfaceTest, build_id32) {
+  BuildID<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id64) {
+  BuildID<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_two_notes32) {
+  BuildIDTwoNotes<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_two_notes64) {
+  BuildIDTwoNotes<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_name32) {
+  BuildIDSectionTooSmallForName<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_name64) {
+  BuildIDSectionTooSmallForName<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_desc32) {
+  BuildIDSectionTooSmallForDesc<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_desc64) {
+  BuildIDSectionTooSmallForDesc<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header32) {
+  BuildIDSectionTooSmallForHeader<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header64) {
+  BuildIDSectionTooSmallForHeader<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index aecbf6d..c432d6d 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -110,7 +110,7 @@
 TEST_F(ElfTest, invalid_memory) {
   Elf elf(memory_);
 
-  ASSERT_FALSE(elf.Init(false));
+  ASSERT_FALSE(elf.Init());
   ASSERT_FALSE(elf.valid());
 }
 
@@ -122,18 +122,22 @@
   // Corrupt the ELF signature.
   memory_->SetData32(0, 0x7f000000);
 
-  ASSERT_FALSE(elf.Init(false));
+  ASSERT_FALSE(elf.Init());
   ASSERT_FALSE(elf.valid());
   ASSERT_TRUE(elf.interface() == nullptr);
 
-  std::string name;
-  ASSERT_FALSE(elf.GetSoname(&name));
+  ASSERT_EQ("", elf.GetSoname());
 
+  std::string name;
   uint64_t func_offset;
   ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
 
+  ASSERT_FALSE(elf.StepIfSignalHandler(0, nullptr, nullptr));
+  EXPECT_EQ(ERROR_INVALID_ELF, elf.GetLastErrorCode());
+
   bool finished;
-  ASSERT_FALSE(elf.Step(0, 0, nullptr, nullptr, &finished));
+  ASSERT_FALSE(elf.Step(0, nullptr, nullptr, &finished));
+  EXPECT_EQ(ERROR_INVALID_ELF, elf.GetLastErrorCode());
 }
 
 TEST_F(ElfTest, elf32_invalid_machine) {
@@ -142,7 +146,7 @@
   InitElf32(EM_PPC);
 
   ResetLogs();
-  ASSERT_FALSE(elf.Init(false));
+  ASSERT_FALSE(elf.Init());
 
   ASSERT_EQ("", GetFakeLogBuf());
   ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86 nor mips: e_machine = 20\n\n",
@@ -155,7 +159,7 @@
   InitElf64(EM_PPC64);
 
   ResetLogs();
-  ASSERT_FALSE(elf.Init(false));
+  ASSERT_FALSE(elf.Init());
 
   ASSERT_EQ("", GetFakeLogBuf());
   ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64 nor mips64: e_machine = 21\n\n",
@@ -167,7 +171,7 @@
 
   InitElf32(EM_ARM);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_ARM), elf.machine_type());
   ASSERT_EQ(ELFCLASS32, elf.class_type());
@@ -179,7 +183,7 @@
 
   InitElf32(EM_MIPS);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_MIPS), elf.machine_type());
   ASSERT_EQ(ELFCLASS32, elf.class_type());
@@ -191,7 +195,7 @@
 
   InitElf32(EM_386);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_386), elf.machine_type());
   ASSERT_EQ(ELFCLASS32, elf.class_type());
@@ -203,7 +207,7 @@
 
   InitElf64(EM_AARCH64);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_AARCH64), elf.machine_type());
   ASSERT_EQ(ELFCLASS64, elf.class_type());
@@ -215,7 +219,7 @@
 
   InitElf64(EM_X86_64);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_X86_64), elf.machine_type());
   ASSERT_EQ(ELFCLASS64, elf.class_type());
@@ -227,41 +231,13 @@
 
   InitElf64(EM_MIPS);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   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) {
-                                                 memory_->SetMemory(offset, ptr, size);
-                                               });
-
-  Elf elf(memory_);
-  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());
-  EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size());
-}
-
-TEST_F(ElfTest, gnu_debugdata_init_fail64) {
-  TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
-                                               [&](uint64_t offset, const void* ptr, size_t size) {
-                                                 memory_->SetMemory(offset, ptr, size);
-                                               });
-
-  Elf elf(memory_);
-  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());
-  EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size());
-}
-
 TEST_F(ElfTest, gnu_debugdata_init32) {
   TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
@@ -269,7 +245,7 @@
                                                });
 
   Elf elf(memory_);
-  ASSERT_TRUE(elf.Init(true));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.interface() != nullptr);
   ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
   EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
@@ -283,7 +259,7 @@
                                                });
 
   Elf elf(memory_);
-  ASSERT_TRUE(elf.Init(true));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.interface() != nullptr);
   ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
   EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
@@ -297,16 +273,11 @@
   elf.FakeSetInterface(interface);
 
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
-  MapInfo map_info(0x1000, 0x2000);
+  MapInfo map_info(nullptr, 0x1000, 0x2000, 0, 0, "");
 
   ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
 
-  elf.FakeSetLoadBias(0x3000);
-  ASSERT_EQ(0x3101U, elf.GetRelPc(0x1101, &map_info));
-
   elf.FakeSetValid(false);
-  elf.FakeSetLoadBias(0);
   ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
 }
 
@@ -328,10 +299,8 @@
   }
 
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
-  bool finished;
-  ASSERT_TRUE(elf.Step(0x3000, 0x1000, &regs, &process_memory, &finished));
-  EXPECT_FALSE(finished);
+  ASSERT_TRUE(elf.StepIfSignalHandler(0x3000, &regs, &process_memory));
+  EXPECT_EQ(ERROR_NONE, elf.GetLastErrorCode());
   EXPECT_EQ(15U, regs.pc());
   EXPECT_EQ(13U, regs.sp());
 }
@@ -342,11 +311,12 @@
   virtual ~ElfInterfaceMock() = default;
 
   bool Init(uint64_t*) override { return false; }
-  void InitHeaders() override {}
-  bool GetSoname(std::string*) override { return false; }
-  bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override { return false; }
+  void InitHeaders(uint64_t) override {}
+  std::string GetSoname() override { return ""; }
+  bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
+  std::string GetBuildID() override { return ""; }
 
-  MOCK_METHOD5(Step, bool(uint64_t, uint64_t, Regs*, Memory*, bool*));
+  MOCK_METHOD4(Step, bool(uint64_t, Regs*, Memory*, bool*));
   MOCK_METHOD2(GetGlobalVariable, bool(const std::string&, uint64_t*));
   MOCK_METHOD1(IsValidPc, bool(uint64_t));
 
@@ -358,7 +328,6 @@
 TEST_F(ElfTest, step_in_interface) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   RegsArm regs;
 
@@ -367,28 +336,10 @@
   MemoryFake process_memory;
 
   bool finished;
-  EXPECT_CALL(*interface, Step(0x1000, 0, &regs, &process_memory, &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, step_in_interface_non_zero_load_bias) {
-  ElfFake elf(memory_);
-  elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0x4000);
-
-  RegsArm regs;
-
-  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
-  elf.FakeSetInterface(interface);
-  MemoryFake process_memory;
-
-  bool finished;
-  EXPECT_CALL(*interface, Step(0x7300, 0x4000, &regs, &process_memory, &finished))
-      .WillOnce(::testing::Return(true));
-
-  ASSERT_TRUE(elf.Step(0x7304, 0x7300, &regs, &process_memory, &finished));
+  ASSERT_TRUE(elf.Step(0x1000, &regs, &process_memory, &finished));
 }
 
 TEST_F(ElfTest, get_global_invalid_elf) {
@@ -403,7 +354,6 @@
 TEST_F(ElfTest, get_global_valid_not_in_interface) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   elf.FakeSetInterface(interface);
@@ -431,10 +381,26 @@
   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);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   elf.FakeSetInterface(interface);
@@ -456,7 +422,6 @@
 TEST_F(ElfTest, get_global_valid_in_gnu_debugdata_dynamic_zero) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   elf.FakeSetInterface(interface);
@@ -470,27 +435,9 @@
   EXPECT_EQ(0x300U, 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_adjust_negative) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   interface->MockSetDynamicOffset(0x400);
@@ -510,7 +457,6 @@
 TEST_F(ElfTest, get_global_valid_dynamic_adjust_positive) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   interface->MockSetDynamicOffset(0x1000);
@@ -530,7 +476,6 @@
 TEST_F(ElfTest, is_valid_pc_elf_invalid) {
   ElfFake elf(memory_);
   elf.FakeSetValid(false);
-  elf.FakeSetLoadBias(0);
 
   EXPECT_FALSE(elf.IsValidPc(0x100));
   EXPECT_FALSE(elf.IsValidPc(0x200));
@@ -539,7 +484,6 @@
 TEST_F(ElfTest, is_valid_pc_interface) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   elf.FakeSetInterface(interface);
@@ -549,25 +493,9 @@
   EXPECT_TRUE(elf.IsValidPc(0x1500));
 }
 
-TEST_F(ElfTest, is_valid_pc_non_zero_load_bias) {
-  ElfFake elf(memory_);
-  elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0x1000);
-
-  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
-  elf.FakeSetInterface(interface);
-
-  EXPECT_CALL(*interface, IsValidPc(0x500)).WillOnce(::testing::Return(true));
-
-  EXPECT_FALSE(elf.IsValidPc(0x100));
-  EXPECT_FALSE(elf.IsValidPc(0x200));
-  EXPECT_TRUE(elf.IsValidPc(0x1500));
-}
-
 TEST_F(ElfTest, is_valid_pc_from_gnu_debugdata) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   elf.FakeSetInterface(interface);
diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp
index c1c45f8..b1ca111 100644
--- a/libunwindstack/tests/JitDebugTest.cpp
+++ b/libunwindstack/tests/JitDebugTest.cpp
@@ -35,27 +35,7 @@
 
 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);
+  void CreateFakeElf(MapInfo* map_info) {
     MemoryFake* memory = new MemoryFake;
     ElfFake* elf = new ElfFake(memory);
     elf->FakeSetValid(true);
@@ -63,26 +43,43 @@
     elf->FakeSetInterface(interface);
     interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
     map_info->elf.reset(elf);
+  }
+
+  void Init(ArchEnum arch) {
+    jit_debug_.reset(new JitDebug(process_memory_));
+    jit_debug_->SetArch(arch);
+
+    maps_.reset(
+        new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf1\n"
+                       "4000-6000 r--s 00000000 00:00 0 /fake/elf1\n"
+                       "6000-8000 -wxs 00000000 00:00 0 /fake/elf1\n"
+                       "a000-c000 --xp 00000000 00:00 0 /fake/elf2\n"
+                       "c000-f000 rw-p 00001000 00:00 0 /fake/elf2\n"
+                       "f000-11000 r--p 00000000 00:00 0 /fake/elf3\n"
+                       "11000-12000 rw-p 00001000 00:00 0 /fake/elf3\n"
+                       "12000-14000 r--p 00000000 00:00 0 /fake/elf4\n"
+                       "100000-110000 rw-p 0001000 00:00 0 /fake/elf4\n"
+                       "200000-210000 rw-p 0002000 00:00 0 /fake/elf4\n"));
+    ASSERT_TRUE(maps_->Parse());
+
+    MapInfo* map_info = maps_->Get(3);
+    ASSERT_TRUE(map_info != nullptr);
+    CreateFakeElf(map_info);
 
     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);
+    CreateFakeElf(map_info);
 
-    map_info = maps_->Get(6);
+    map_info = maps_->Get(7);
     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);
+    CreateFakeElf(map_info);
+  }
+
+  void SetUp() override {
+    memory_ = new MemoryFake;
+    process_memory_.reset(memory_);
+
+    Init(ARCH_ARM);
   }
 
   template <typename EhdrType, typename ShdrType>
@@ -325,6 +322,8 @@
 }
 
 TEST_F(JitDebugTest, get_elf_x86) {
+  Init(ARCH_X86);
+
   CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
 
   WriteDescriptor32(0xf800, 0x200000);
@@ -342,12 +341,13 @@
 }
 
 TEST_F(JitDebugTest, get_elf_64) {
+  Init(ARCH_ARM64);
+
   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);
 
@@ -397,6 +397,8 @@
   // 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";
+  map_info = maps_->Get(6);
+  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.
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/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 866b5b4..5b4ca7c 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -27,13 +27,14 @@
 #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 "ElfTestUtils.h"
 #include "MemoryFake.h"
 
 namespace unwindstack {
@@ -57,17 +58,17 @@
     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());
+  static void SetUpTestSuite() {
+    std::vector<uint8_t> buffer(12288, 0);
     memcpy(buffer.data(), ELFMAG, SELFMAG);
     buffer[EI_CLASS] = ELFCLASS32;
-    ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+    ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), 1024));
 
     memset(buffer.data(), 0, buffer.size());
-    memcpy(&buffer[0x100], ELFMAG, SELFMAG);
-    buffer[0x100 + EI_CLASS] = ELFCLASS64;
-    ASSERT_TRUE(android::base::WriteFully(elf_at_100_.fd, buffer.data(), buffer.size()));
+    memcpy(&buffer[0x1000], ELFMAG, SELFMAG);
+    buffer[0x1000 + EI_CLASS] = ELFCLASS64;
+    buffer[0x2000] = 0xff;
+    ASSERT_TRUE(android::base::WriteFully(elf_at_1000_.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);
@@ -83,18 +84,18 @@
 
   static TemporaryFile elf_;
 
-  static TemporaryFile elf_at_100_;
+  static TemporaryFile elf_at_1000_;
 
   static TemporaryFile elf32_at_map_;
   static TemporaryFile elf64_at_map_;
 };
 TemporaryFile MapInfoCreateMemoryTest::elf_;
-TemporaryFile MapInfoCreateMemoryTest::elf_at_100_;
+TemporaryFile MapInfoCreateMemoryTest::elf_at_1000_;
 TemporaryFile MapInfoCreateMemoryTest::elf32_at_map_;
 TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
 
 TEST_F(MapInfoCreateMemoryTest, end_le_start) {
-  MapInfo info(0x100, 0x100, 0, 0, elf_.path);
+  MapInfo info(nullptr, 0x100, 0x100, 0, 0, elf_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() == nullptr);
@@ -107,16 +108,19 @@
   info.end = 0x101;
   memory.reset(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
+  EXPECT_FALSE(info.memory_backed_elf);
 }
 
 // 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(0x100, 0x200, 0x100, 0, elf_.path);
+  MapInfo info(nullptr, 0x100, 0x200, 0x100, 0, elf_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
+  EXPECT_FALSE(info.memory_backed_elf);
   ASSERT_EQ(0x100U, info.elf_offset);
+  EXPECT_EQ(0x100U, info.elf_start_offset);
 
   // Read the entire file.
   std::vector<uint8_t> buffer(1024);
@@ -128,16 +132,64 @@
   }
 
   ASSERT_FALSE(memory->ReadFully(1024, buffer.data(), 1));
+
+  // Now verify the elf start offset is set correctly based on the previous
+  // info.
+  MapInfo prev_info(nullptr, 0, 0x100, 0x10, 0, "");
+  info.prev_map = &prev_info;
+
+  // No preconditions met, change each one until it should set the elf start
+  // offset to zero.
+  info.elf_offset = 0;
+  info.elf_start_offset = 0;
+  info.memory_backed_elf = false;
+  memory.reset(info.CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() != nullptr);
+  EXPECT_FALSE(info.memory_backed_elf);
+  ASSERT_EQ(0x100U, info.elf_offset);
+  EXPECT_EQ(0x100U, info.elf_start_offset);
+
+  prev_info.offset = 0;
+  info.elf_offset = 0;
+  info.elf_start_offset = 0;
+  info.memory_backed_elf = false;
+  memory.reset(info.CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() != nullptr);
+  EXPECT_FALSE(info.memory_backed_elf);
+  ASSERT_EQ(0x100U, info.elf_offset);
+  EXPECT_EQ(0x100U, info.elf_start_offset);
+
+  prev_info.flags = PROT_READ;
+  info.elf_offset = 0;
+  info.elf_start_offset = 0;
+  info.memory_backed_elf = false;
+  memory.reset(info.CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() != nullptr);
+  EXPECT_FALSE(info.memory_backed_elf);
+  ASSERT_EQ(0x100U, info.elf_offset);
+  EXPECT_EQ(0x100U, info.elf_start_offset);
+
+  prev_info.name = info.name;
+  info.elf_offset = 0;
+  info.elf_start_offset = 0;
+  info.memory_backed_elf = false;
+  memory.reset(info.CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() != nullptr);
+  EXPECT_FALSE(info.memory_backed_elf);
+  ASSERT_EQ(0x100U, info.elf_offset);
+  EXPECT_EQ(0U, info.elf_start_offset);
 }
 
 // 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(0x100, 0x200, 0x100, 0, elf_at_100_.path);
+  MapInfo info(nullptr, 0x100, 0x200, 0x1000, 0, elf_at_1000_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
+  EXPECT_FALSE(info.memory_backed_elf);
   ASSERT_EQ(0U, info.elf_offset);
+  EXPECT_EQ(0x1000U, info.elf_start_offset);
 
   // Read the valid part of the file.
   std::vector<uint8_t> buffer(0x100);
@@ -156,11 +208,13 @@
 // 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);
+  MapInfo info(nullptr, 0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
+  EXPECT_FALSE(info.memory_backed_elf);
   ASSERT_EQ(0U, info.elf_offset);
+  EXPECT_EQ(0x1000U, info.elf_start_offset);
 
   // Verify the memory is a valid elf.
   uint8_t e_ident[SELFMAG + 1];
@@ -172,11 +226,13 @@
 }
 
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
-  MapInfo info(0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
+  MapInfo info(nullptr, 0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
+  EXPECT_FALSE(info.memory_backed_elf);
   ASSERT_EQ(0U, info.elf_offset);
+  EXPECT_EQ(0x2000U, info.elf_start_offset);
 
   // Verify the memory is a valid elf.
   uint8_t e_ident[SELFMAG + 1];
@@ -192,37 +248,36 @@
   // Set up some memory so that a valid local memory object would
   // be returned if the file mapping fails, but the device check is incorrect.
   std::vector<uint8_t> buffer(1024);
-  MapInfo info;
-  info.start = reinterpret_cast<uint64_t>(buffer.data());
-  info.end = info.start + buffer.size();
-  info.offset = 0;
+  uint64_t start = reinterpret_cast<uint64_t>(buffer.data());
+  MapInfo info(nullptr, start, start + buffer.size(), 0, 0x8000, "/dev/something");
 
-  info.flags = 0x8000;
-  info.name = "/dev/something";
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() == nullptr);
 }
 
 TEST_F(MapInfoCreateMemoryTest, process_memory) {
-  MapInfo info;
-  info.start = 0x2000;
-  info.end = 0x3000;
-  info.offset = 0;
+  MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
+
+  Elf32_Ehdr ehdr = {};
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  std::vector<uint8_t> buffer(1024);
+  memcpy(buffer.data(), &ehdr, sizeof(ehdr));
 
   // 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++) {
+  for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
     buffer[i] = i % 256;
   }
   memory_->SetMemory(info.start, buffer.data(), buffer.size());
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
+  EXPECT_TRUE(info.memory_backed_elf);
 
   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(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+  for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
     ASSERT_EQ(i % 256, buffer[i]) << "Failed at byte " << i;
   }
 
@@ -230,4 +285,135 @@
   ASSERT_FALSE(memory->ReadFully(buffer.size(), buffer.data(), 1));
 }
 
+TEST_F(MapInfoCreateMemoryTest, valid_rosegment_zero_offset) {
+  Maps maps;
+  maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
+  maps.Add(0x1000, 0x2600, 0, PROT_READ, "/only/in/memory.so", 0);
+  maps.Add(0x3000, 0x5000, 0x4000, PROT_READ | PROT_EXEC, "/only/in/memory.so", 0);
+
+  Elf32_Ehdr ehdr = {};
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+  memory_->SetMemoryBlock(0x1000 + sizeof(ehdr), 0x1600 - sizeof(ehdr), 0xab);
+
+  // Set the memory in the r-x map.
+  memory_->SetMemoryBlock(0x3000, 0x2000, 0x5d);
+
+  MapInfo* map_info = maps.Find(0x3000);
+  ASSERT_TRUE(map_info != nullptr);
+
+  std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
+  ASSERT_TRUE(mem.get() != nullptr);
+  EXPECT_TRUE(map_info->memory_backed_elf);
+  EXPECT_EQ(0x4000UL, map_info->elf_offset);
+  EXPECT_EQ(0x4000UL, map_info->offset);
+  EXPECT_EQ(0U, map_info->elf_start_offset);
+
+  // Verify that reading values from this memory works properly.
+  std::vector<uint8_t> buffer(0x4000);
+  size_t bytes = mem->Read(0, buffer.data(), buffer.size());
+  ASSERT_EQ(0x1600UL, bytes);
+  ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+  for (size_t i = sizeof(ehdr); i < bytes; i++) {
+    ASSERT_EQ(0xab, buffer[i]) << "Failed at byte " << i;
+  }
+
+  bytes = mem->Read(0x4000, buffer.data(), buffer.size());
+  ASSERT_EQ(0x2000UL, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x5d, buffer[i]) << "Failed at byte " << i;
+  }
+}
+
+TEST_F(MapInfoCreateMemoryTest, valid_rosegment_non_zero_offset) {
+  Maps maps;
+  maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
+  maps.Add(0x1000, 0x2000, 0, PROT_READ, "/only/in/memory.apk", 0);
+  maps.Add(0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, "/only/in/memory.apk", 0);
+  maps.Add(0x3000, 0x4000, 0xa000, PROT_READ, "/only/in/memory.apk", 0);
+  maps.Add(0x4000, 0x5000, 0xb000, PROT_READ | PROT_EXEC, "/only/in/memory.apk", 0);
+
+  Elf32_Ehdr ehdr = {};
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+
+  // Setup an elf at offset 0x1000 in memory.
+  memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+  memory_->SetMemoryBlock(0x1000 + sizeof(ehdr), 0x2000 - sizeof(ehdr), 0x12);
+  memory_->SetMemoryBlock(0x2000, 0x1000, 0x23);
+
+  // Setup an elf at offset 0x3000 in memory..
+  memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
+  memory_->SetMemoryBlock(0x3000 + sizeof(ehdr), 0x4000 - sizeof(ehdr), 0x34);
+  memory_->SetMemoryBlock(0x4000, 0x1000, 0x43);
+
+  MapInfo* map_info = maps.Find(0x4000);
+  ASSERT_TRUE(map_info != nullptr);
+
+  std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
+  ASSERT_TRUE(mem.get() != nullptr);
+  EXPECT_TRUE(map_info->memory_backed_elf);
+  EXPECT_EQ(0x1000UL, map_info->elf_offset);
+  EXPECT_EQ(0xb000UL, map_info->offset);
+  EXPECT_EQ(0xa000UL, map_info->elf_start_offset);
+
+  // Verify that reading values from this memory works properly.
+  std::vector<uint8_t> buffer(0x4000);
+  size_t bytes = mem->Read(0, buffer.data(), buffer.size());
+  ASSERT_EQ(0x1000UL, bytes);
+  ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+  for (size_t i = sizeof(ehdr); i < bytes; i++) {
+    ASSERT_EQ(0x34, buffer[i]) << "Failed at byte " << i;
+  }
+
+  bytes = mem->Read(0x1000, buffer.data(), buffer.size());
+  ASSERT_EQ(0x1000UL, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x43, buffer[i]) << "Failed at byte " << i;
+  }
+}
+
+TEST_F(MapInfoCreateMemoryTest, rosegment_from_file) {
+  Maps maps;
+  maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
+  maps.Add(0x1000, 0x2000, 0x1000, PROT_READ, elf_at_1000_.path, 0);
+  maps.Add(0x2000, 0x3000, 0x2000, PROT_READ | PROT_EXEC, elf_at_1000_.path, 0);
+
+  MapInfo* map_info = maps.Find(0x2000);
+  ASSERT_TRUE(map_info != nullptr);
+
+  // Set up the size
+  Elf64_Ehdr ehdr;
+  ASSERT_EQ(0x1000, lseek(elf_at_1000_.fd, 0x1000, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFully(elf_at_1000_.fd, &ehdr, sizeof(ehdr)));
+
+  // Will not give the elf memory, because the read-only entry does not
+  // extend over the executable segment.
+  std::unique_ptr<Memory> memory(map_info->CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() != nullptr);
+  EXPECT_FALSE(map_info->memory_backed_elf);
+  std::vector<uint8_t> buffer(0x100);
+  EXPECT_EQ(0x2000U, map_info->offset);
+  EXPECT_EQ(0U, map_info->elf_offset);
+  EXPECT_EQ(0U, map_info->elf_start_offset);
+  ASSERT_TRUE(memory->ReadFully(0, buffer.data(), 0x100));
+  EXPECT_EQ(0xffU, buffer[0]);
+
+  // Now init the elf data enough so that the file memory object will be used.
+  ehdr.e_shoff = 0x4000;
+  ehdr.e_shnum = 1;
+  ehdr.e_shentsize = 0x100;
+  ASSERT_EQ(0x1000, lseek(elf_at_1000_.fd, 0x1000, SEEK_SET));
+  ASSERT_TRUE(android::base::WriteFully(elf_at_1000_.fd, &ehdr, sizeof(ehdr)));
+
+  map_info->memory_backed_elf = false;
+  memory.reset(map_info->CreateMemory(process_memory_));
+  EXPECT_FALSE(map_info->memory_backed_elf);
+  EXPECT_EQ(0x2000U, map_info->offset);
+  EXPECT_EQ(0x1000U, map_info->elf_offset);
+  EXPECT_EQ(0x1000U, map_info->elf_start_offset);
+  Elf64_Ehdr ehdr_mem;
+  ASSERT_TRUE(memory->ReadFully(0, &ehdr_mem, sizeof(ehdr_mem)));
+  EXPECT_TRUE(memcmp(&ehdr, &ehdr_mem, sizeof(ehdr)) == 0);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetBuildIDTest.cpp b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
new file mode 100644
index 0000000..16451d1
--- /dev/null
+++ b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <sys/mman.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <memory>
+#include <string>
+#include <thread>
+#include <vector>
+
+#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 MapInfoGetBuildIDTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    tf_.reset(new TemporaryFile);
+
+    memory_ = new MemoryFake;
+    elf_ = new ElfFake(new MemoryFake);
+    elf_interface_ = new ElfInterfaceFake(memory_);
+    elf_->FakeSetInterface(elf_interface_);
+    elf_container_.reset(elf_);
+    map_info_.reset(new MapInfo(nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, tf_->path));
+  }
+
+  void MultipleThreadTest(std::string expected_build_id);
+
+  MemoryFake* memory_;
+  ElfFake* elf_;
+  ElfInterfaceFake* elf_interface_;
+  std::unique_ptr<ElfFake> elf_container_;
+  std::unique_ptr<MapInfo> map_info_;
+  std::unique_ptr<TemporaryFile> tf_;
+};
+
+TEST_F(MapInfoGetBuildIDTest, no_elf_and_no_valid_elf_in_memory) {
+  MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+
+  EXPECT_EQ("", info.GetBuildID());
+  EXPECT_EQ("", info.GetPrintableBuildID());
+}
+
+TEST_F(MapInfoGetBuildIDTest, from_elf) {
+  map_info_->elf.reset(elf_container_.release());
+  elf_interface_->FakeSetBuildID("FAKE_BUILD_ID");
+
+  EXPECT_EQ("FAKE_BUILD_ID", map_info_->GetBuildID());
+  EXPECT_EQ("46414b455f4255494c445f4944", map_info_->GetPrintableBuildID());
+}
+
+TEST_F(MapInfoGetBuildIDTest, from_elf_no_sign_extension) {
+  map_info_->elf.reset(elf_container_.release());
+
+  std::string build_id = {static_cast<char>(0xfa), static_cast<char>(0xab), static_cast<char>(0x12),
+                          static_cast<char>(0x02)};
+  elf_interface_->FakeSetBuildID(build_id);
+
+  EXPECT_EQ("\xFA\xAB\x12\x2", map_info_->GetBuildID());
+  EXPECT_EQ("faab1202", map_info_->GetPrintableBuildID());
+}
+
+void MapInfoGetBuildIDTest::MultipleThreadTest(std::string expected_build_id) {
+  static constexpr size_t kNumConcurrentThreads = 100;
+
+  std::string build_id_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, &build_id_values]() {
+      while (wait)
+        ;
+      build_id_values[i] = map_info_->GetBuildID();
+    });
+    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_build_id, build_id_values[i]) << "Thread " << i << " mismatched.";
+  }
+}
+
+TEST_F(MapInfoGetBuildIDTest, multiple_thread_elf_exists) {
+  map_info_->elf.reset(elf_container_.release());
+  elf_interface_->FakeSetBuildID("FAKE_BUILD_ID");
+
+  MultipleThreadTest("FAKE_BUILD_ID");
+}
+
+static void InitElfData(int fd) {
+  Elf32_Ehdr ehdr;
+  TestInitEhdr(&ehdr, ELFCLASS32, EM_ARM);
+  ehdr.e_shoff = 0x2000;
+  ehdr.e_shnum = 3;
+  ehdr.e_shentsize = sizeof(Elf32_Shdr);
+  ehdr.e_shstrndx = 2;
+  off_t offset = 0;
+  ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(ehdr)), write(fd, &ehdr, sizeof(ehdr)));
+
+  char note_section[128];
+  Elf32_Nhdr note_header = {};
+  note_header.n_namesz = 4;   // "GNU"
+  note_header.n_descsz = 12;  // "ELF_BUILDID"
+  note_header.n_type = NT_GNU_BUILD_ID;
+  memcpy(&note_section, &note_header, sizeof(note_header));
+  size_t note_offset = sizeof(note_header);
+  memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
+  note_offset += sizeof("GNU");
+  memcpy(&note_section[note_offset], "ELF_BUILDID", sizeof("ELF_BUILDID"));
+  note_offset += sizeof("ELF_BUILDID");
+
+  Elf32_Shdr shdr = {};
+  shdr.sh_type = SHT_NOTE;
+  shdr.sh_name = 0x500;
+  shdr.sh_offset = 0xb000;
+  shdr.sh_size = sizeof(note_section);
+  offset += ehdr.e_shoff + sizeof(shdr);
+  ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(shdr)), write(fd, &shdr, sizeof(shdr)));
+
+  // The string data for section header names.
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 0x20000;
+  shdr.sh_offset = 0xf000;
+  shdr.sh_size = 0x1000;
+  offset += sizeof(shdr);
+  ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(shdr)), write(fd, &shdr, sizeof(shdr)));
+
+  offset = 0xf500;
+  ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(".note.gnu.build-id")),
+            write(fd, ".note.gnu.build-id", sizeof(".note.gnu.build-id")));
+
+  offset = 0xb000;
+  ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(note_section)),
+            write(fd, note_section, sizeof(note_section)));
+}
+
+TEST_F(MapInfoGetBuildIDTest, from_memory) {
+  InitElfData(tf_->fd);
+
+  EXPECT_EQ("ELF_BUILDID", map_info_->GetBuildID());
+  EXPECT_EQ("454c465f4255494c444944", map_info_->GetPrintableBuildID());
+}
+
+TEST_F(MapInfoGetBuildIDTest, multiple_thread_elf_exists_in_memory) {
+  InitElfData(tf_->fd);
+
+  MultipleThreadTest("ELF_BUILDID");
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index f599503..d60b8b1 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -29,7 +29,6 @@
 #include <vector>
 
 #include <android-base/file.h>
-#include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
 #include <unwindstack/Elf.h>
@@ -69,83 +68,69 @@
 };
 
 TEST_F(MapInfoGetElfTest, invalid) {
-  MapInfo info(0x1000, 0x2000, 0, PROT_READ, "");
+  MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
 
   // The map is empty, but this should still create an invalid elf object.
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 }
 
 TEST_F(MapInfoGetElfTest, valid32) {
-  MapInfo info(0x3000, 0x4000, 0, PROT_READ, "");
+  MapInfo info(nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
 
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
   memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   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());
+
+  // Now verify that an empty process memory returns an invalid elf object.
+  info.elf.reset();
+  elf = info.GetElf(std::shared_ptr<Memory>(), ARCH_ARM);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_FALSE(elf->valid());
 }
 
 TEST_F(MapInfoGetElfTest, valid64) {
-  MapInfo info(0x8000, 0x9000, 0, PROT_READ, "");
+  MapInfo info(nullptr, 0x8000, 0x9000, 0, PROT_READ, "");
 
   Elf64_Ehdr ehdr;
   TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
   memory_->SetMemory(0x8000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
   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) {
-  MapInfo info(0x4000, 0x8000, 0, PROT_READ, "");
+TEST_F(MapInfoGetElfTest, invalid_arch_mismatch) {
+  MapInfo info(nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
 
-  TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
-                                               [&](uint64_t offset, const void* ptr, size_t size) {
-                                                 memory_->SetMemory(0x4000 + offset, ptr, size);
-                                               });
+  Elf32_Ehdr ehdr;
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_X86);
   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());
-  EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr);
-}
-
-TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
-  MapInfo info(0x6000, 0x8000, 0, PROT_READ, "");
-
-  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());
-  EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr);
+  ASSERT_FALSE(elf->valid());
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
-  MapInfo info(0x2000, 0x3000, 0, PROT_READ, "");
+  MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
 
   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);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
@@ -154,14 +139,14 @@
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
-  MapInfo info(0x5000, 0x8000, 0, PROT_READ, "");
+  MapInfo info(nullptr, 0x5000, 0x8000, 0, PROT_READ, "");
 
   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);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
@@ -170,26 +155,26 @@
 }
 
 TEST_F(MapInfoGetElfTest, end_le_start) {
-  MapInfo info(0x1000, 0x1000, 0, PROT_READ, elf_.path);
+  MapInfo info(nullptr, 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);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 
   info.elf.reset();
   info.end = 0xfff;
-  elf = info.GetElf(process_memory_, false);
+  elf = info.GetElf(process_memory_, ARCH_ARM);
   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);
+  elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
 }
@@ -197,7 +182,7 @@
 // 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);
+  MapInfo info(nullptr, 0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x1000);
   memset(buffer.data(), 0, buffer.size());
@@ -206,7 +191,7 @@
   memcpy(buffer.data(), &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
@@ -226,7 +211,7 @@
 // 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);
+  MapInfo info(nullptr, 0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
@@ -235,7 +220,7 @@
   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);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
@@ -256,7 +241,7 @@
 // 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);
+  MapInfo info(nullptr, 0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
@@ -268,7 +253,7 @@
   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);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
@@ -284,7 +269,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
-  MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
+  MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
@@ -296,7 +281,7 @@
   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);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
@@ -311,29 +296,9 @@
   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");
+  MapInfo info(nullptr, 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.
@@ -341,23 +306,23 @@
   TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_X86_64);
   ehdr.e_shoff = 0x2000;
   ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
-  ehdr.e_shnum = 4;
+  ehdr.e_shnum = 0;
   memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_X86_64);
   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);
+  elf = info.GetElf(process_memory_, ARCH_X86_64);
   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);
+  elf = info.GetElf(process_memory_, ARCH_X86_64);
   ASSERT_TRUE(elf->valid());
 }
 
@@ -368,7 +333,7 @@
   TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_X86_64);
   ehdr.e_shoff = 0x2000;
   ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
-  ehdr.e_shnum = 4;
+  ehdr.e_shnum = 0;
   memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
 
   Elf* elf_in_threads[kNumConcurrentThreads];
@@ -378,12 +343,12 @@
   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, "");
+  MapInfo info(nullptr, 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* elf = info.GetElf(process_memory_, ARCH_X86_64);
       elf_in_threads[i] = elf;
     });
     threads.push_back(thread);
@@ -406,4 +371,35 @@
   }
 }
 
+// Verify that previous maps don't automatically get the same elf object.
+TEST_F(MapInfoGetElfTest, prev_map_elf_not_set) {
+  MapInfo info1(nullptr, 0x1000, 0x2000, 0, PROT_READ, "/not/present");
+  MapInfo info2(&info1, 0x2000, 0x3000, 0, PROT_READ, elf_.path);
+
+  Elf32_Ehdr ehdr;
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  memory_->SetMemory(0x2000, &ehdr, sizeof(ehdr));
+  Elf* elf = info2.GetElf(process_memory_, ARCH_ARM);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_TRUE(elf->valid());
+
+  ASSERT_NE(elf, info1.GetElf(process_memory_, ARCH_ARM));
+}
+
+// Verify that a read-only map followed by a read-execute map will result
+// in the same elf object in both maps.
+TEST_F(MapInfoGetElfTest, read_only_followed_by_read_exec_share_elf) {
+  MapInfo r_info(nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path);
+  MapInfo rw_info(&r_info, 0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, elf_.path);
+
+  Elf32_Ehdr ehdr;
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+  Elf* elf = rw_info.GetElf(process_memory_, ARCH_ARM);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_TRUE(elf->valid());
+
+  ASSERT_EQ(elf, r_info.GetElf(process_memory_, ARCH_ARM));
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
index 7e64a8a..f5ac6cb 100644
--- a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
+++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
@@ -50,7 +50,7 @@
     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, ""));
+    map_info_.reset(new MapInfo(nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
   }
 
   void MultipleThreadTest(uint64_t expected_load_bias);
@@ -63,7 +63,7 @@
 };
 
 TEST_F(MapInfoGetLoadBiasTest, no_elf_and_no_valid_elf_in_memory) {
-  MapInfo info(0x1000, 0x2000, 0, PROT_READ, "");
+  MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
 
   EXPECT_EQ(0U, info.GetLoadBias(process_memory_));
 }
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoTest.cpp
new file mode 100644
index 0000000..e2cbb98
--- /dev/null
+++ b/libunwindstack/tests/MapInfoTest.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+
+#include "ElfFake.h"
+
+namespace unwindstack {
+
+TEST(MapInfoTest, maps_constructor_const_char) {
+  MapInfo prev_map(nullptr, 0, 0, 0, 0, "");
+  MapInfo map_info(&prev_map, 1, 2, 3, 4, "map");
+
+  EXPECT_EQ(&prev_map, map_info.prev_map);
+  EXPECT_EQ(1UL, map_info.start);
+  EXPECT_EQ(2UL, map_info.end);
+  EXPECT_EQ(3UL, map_info.offset);
+  EXPECT_EQ(4UL, map_info.flags);
+  EXPECT_EQ("map", map_info.name);
+  EXPECT_EQ(static_cast<uint64_t>(-1), map_info.load_bias);
+  EXPECT_EQ(0UL, map_info.elf_offset);
+  EXPECT_TRUE(map_info.elf.get() == nullptr);
+}
+
+TEST(MapInfoTest, maps_constructor_string) {
+  std::string name("string_map");
+  MapInfo prev_map(nullptr, 0, 0, 0, 0, "");
+  MapInfo map_info(&prev_map, 1, 2, 3, 4, name);
+
+  EXPECT_EQ(&prev_map, map_info.prev_map);
+  EXPECT_EQ(1UL, map_info.start);
+  EXPECT_EQ(2UL, map_info.end);
+  EXPECT_EQ(3UL, map_info.offset);
+  EXPECT_EQ(4UL, map_info.flags);
+  EXPECT_EQ("string_map", map_info.name);
+  EXPECT_EQ(static_cast<uint64_t>(-1), map_info.load_bias);
+  EXPECT_EQ(0UL, map_info.elf_offset);
+  EXPECT_TRUE(map_info.elf.get() == nullptr);
+}
+
+TEST(MapInfoTest, get_function_name) {
+  ElfFake* elf = new ElfFake(nullptr);
+  ElfInterfaceFake* interface = new ElfInterfaceFake(nullptr);
+  elf->FakeSetInterface(interface);
+  interface->FakePushFunctionData(FunctionData("function", 1000));
+
+  MapInfo map_info(nullptr, 1, 2, 3, 4, "");
+  map_info.elf.reset(elf);
+
+  std::string name;
+  uint64_t offset;
+  ASSERT_TRUE(map_info.GetFunctionName(1000, &name, &offset));
+  EXPECT_EQ("function", name);
+  EXPECT_EQ(1000UL, offset);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 9622ba5..9e7a6ab 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -19,7 +19,6 @@
 
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
-#include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
 #include <unwindstack/Maps.h>
@@ -62,8 +61,28 @@
   ASSERT_EQ(0U, info->load_bias.load());
 }
 
+TEST(MapsTest, map_move) {
+  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));
+
+  Maps maps2 = std::move(maps);
+
+  ASSERT_EQ(3U, maps2.Total());
+  MapInfo* info = maps2.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;
+  MapInfo info(nullptr, 0, 0, 0, 0, "");
 
   VerifyLine("01-02 rwxp 03 04:05 06\n", &info);
   EXPECT_EQ(1U, info.start);
@@ -136,7 +155,7 @@
 }
 
 TEST(MapsTest, verify_large_values) {
-  MapInfo info;
+  MapInfo info(nullptr, 0, 0, 0, 0, "");
 #if defined(__LP64__)
   VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", &info);
   EXPECT_EQ(0xfabcdef012345678UL, info.start);
diff --git a/libunwindstack/tests/MemoryBufferTest.cpp b/libunwindstack/tests/MemoryBufferTest.cpp
index 28e0e76..a6c12aa 100644
--- a/libunwindstack/tests/MemoryBufferTest.cpp
+++ b/libunwindstack/tests/MemoryBufferTest.cpp
@@ -18,9 +18,8 @@
 
 #include <gtest/gtest.h>
 
-#include <unwindstack/Memory.h>
-
 #include "LogFake.h"
+#include "MemoryBuffer.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/MemoryCacheTest.cpp b/libunwindstack/tests/MemoryCacheTest.cpp
new file mode 100644
index 0000000..3bd3e4d
--- /dev/null
+++ b/libunwindstack/tests/MemoryCacheTest.cpp
@@ -0,0 +1,142 @@
+/*
+ * 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 <vector>
+
+#include <gtest/gtest.h>
+
+#include "MemoryCache.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MemoryCacheTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_ = new MemoryFake;
+    memory_cache_.reset(new MemoryCache(memory_));
+
+    memory_->SetMemoryBlock(0x8000, 4096, 0xab);
+    memory_->SetMemoryBlock(0x9000, 4096, 0xde);
+    memory_->SetMemoryBlock(0xa000, 3000, 0x50);
+  }
+
+  MemoryFake* memory_;
+  std::unique_ptr<MemoryCache> memory_cache_;
+
+  constexpr static size_t kMaxCachedSize = 64;
+};
+
+TEST_F(MemoryCacheTest, cached_read) {
+  for (size_t i = 1; i <= kMaxCachedSize; i++) {
+    std::vector<uint8_t> buffer(i);
+    ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+        << "Read failed at size " << i;
+    ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+  }
+
+  // Verify the cached data is used.
+  memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+  for (size_t i = 1; i <= kMaxCachedSize; i++) {
+    std::vector<uint8_t> buffer(i);
+    ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+        << "Read failed at size " << i;
+    ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+  }
+}
+
+TEST_F(MemoryCacheTest, no_cached_read_after_clear) {
+  for (size_t i = 1; i <= kMaxCachedSize; i++) {
+    std::vector<uint8_t> buffer(i);
+    ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+        << "Read failed at size " << i;
+    ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+  }
+
+  // Verify the cached data is not used after a reset.
+  memory_cache_->Clear();
+  memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+  for (size_t i = 1; i <= kMaxCachedSize; i++) {
+    std::vector<uint8_t> buffer(i);
+    ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+        << "Read failed at size " << i;
+    ASSERT_EQ(std::vector<uint8_t>(i, 0xff), buffer) << "Failed at size " << i;
+  }
+}
+
+TEST_F(MemoryCacheTest, cached_read_across_caches) {
+  std::vector<uint8_t> expect(16, 0xab);
+  expect.resize(32, 0xde);
+
+  std::vector<uint8_t> buffer(32);
+  ASSERT_TRUE(memory_cache_->ReadFully(0x8ff0, buffer.data(), 32));
+  ASSERT_EQ(expect, buffer);
+
+  // Verify the cached data is used.
+  memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+  memory_->SetMemoryBlock(0x9000, 4096, 0xff);
+  ASSERT_TRUE(memory_cache_->ReadFully(0x8ff0, buffer.data(), 32));
+  ASSERT_EQ(expect, buffer);
+}
+
+TEST_F(MemoryCacheTest, no_cache_read) {
+  for (size_t i = kMaxCachedSize + 1; i < 2 * kMaxCachedSize; i++) {
+    std::vector<uint8_t> buffer(i);
+    ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+        << "Read failed at size " << i;
+    ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+  }
+
+  // Verify the cached data is not used.
+  memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+  for (size_t i = kMaxCachedSize + 1; i < 2 * kMaxCachedSize; i++) {
+    std::vector<uint8_t> buffer(i);
+    ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+        << "Read failed at size " << i;
+    ASSERT_EQ(std::vector<uint8_t>(i, 0xff), buffer) << "Failed at size " << i;
+  }
+}
+
+TEST_F(MemoryCacheTest, read_for_cache_fail) {
+  std::vector<uint8_t> buffer(kMaxCachedSize);
+  ASSERT_TRUE(memory_cache_->ReadFully(0xa010, buffer.data(), kMaxCachedSize));
+  ASSERT_EQ(std::vector<uint8_t>(kMaxCachedSize, 0x50), buffer);
+
+  // Verify the cached data is not used.
+  memory_->SetMemoryBlock(0xa000, 3000, 0xff);
+  ASSERT_TRUE(memory_cache_->ReadFully(0xa010, buffer.data(), kMaxCachedSize));
+  ASSERT_EQ(std::vector<uint8_t>(kMaxCachedSize, 0xff), buffer);
+}
+
+TEST_F(MemoryCacheTest, read_for_cache_fail_cross) {
+  std::vector<uint8_t> expect(16, 0xde);
+  expect.resize(32, 0x50);
+
+  std::vector<uint8_t> buffer(32);
+  ASSERT_TRUE(memory_cache_->ReadFully(0x9ff0, buffer.data(), 32));
+  ASSERT_EQ(expect, buffer);
+
+  // Verify the cached data is not used for the second half but for the first.
+  memory_->SetMemoryBlock(0xa000, 3000, 0xff);
+  ASSERT_TRUE(memory_cache_->ReadFully(0x9ff0, buffer.data(), 32));
+  expect.resize(16);
+  expect.resize(32, 0xff);
+  ASSERT_EQ(expect, buffer);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryFake.cpp b/libunwindstack/tests/MemoryFake.cpp
index 60936cd..5695dfc 100644
--- a/libunwindstack/tests/MemoryFake.cpp
+++ b/libunwindstack/tests/MemoryFake.cpp
@@ -23,6 +23,17 @@
 
 namespace unwindstack {
 
+void MemoryFake::SetMemoryBlock(uint64_t addr, size_t length, uint8_t value) {
+  for (size_t i = 0; i < length; i++, addr++) {
+    auto entry = data_.find(addr);
+    if (entry != data_.end()) {
+      entry->second = value;
+    } else {
+      data_.insert({addr, value});
+    }
+  }
+}
+
 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++) {
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
index 764a6c3..20610a5 100644
--- a/libunwindstack/tests/MemoryFake.h
+++ b/libunwindstack/tests/MemoryFake.h
@@ -36,6 +36,8 @@
 
   void SetMemory(uint64_t addr, const void* memory, size_t length);
 
+  void SetMemoryBlock(uint64_t addr, size_t length, uint8_t value);
+
   void SetData8(uint64_t addr, uint8_t value) {
     SetMemory(addr, &value, sizeof(value));
   }
diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp
index d7d1ace..4124a49 100644
--- a/libunwindstack/tests/MemoryFileTest.cpp
+++ b/libunwindstack/tests/MemoryFileTest.cpp
@@ -21,7 +21,7 @@
 #include <android-base/file.h>
 #include <gtest/gtest.h>
 
-#include <unwindstack/Memory.h>
+#include "MemoryFileAtOffset.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp
index 5a389d0..c9e5dc0 100644
--- a/libunwindstack/tests/MemoryLocalTest.cpp
+++ b/libunwindstack/tests/MemoryLocalTest.cpp
@@ -22,7 +22,7 @@
 
 #include <gtest/gtest.h>
 
-#include <unwindstack/Memory.h>
+#include "MemoryLocal.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/MemoryOfflineBufferTest.cpp b/libunwindstack/tests/MemoryOfflineBufferTest.cpp
index f022884..9531708 100644
--- a/libunwindstack/tests/MemoryOfflineBufferTest.cpp
+++ b/libunwindstack/tests/MemoryOfflineBufferTest.cpp
@@ -18,9 +18,8 @@
 
 #include <gtest/gtest.h>
 
-#include <unwindstack/Memory.h>
-
 #include "LogFake.h"
+#include "MemoryOfflineBuffer.h"
 
 namespace unwindstack {
 
@@ -31,7 +30,7 @@
     memory_.reset(new MemoryOfflineBuffer(buffer_.data(), kStart, kEnd));
   }
 
-  static void SetUpTestCase() {
+  static void SetUpTestSuite() {
     buffer_.resize(kLength);
     for (size_t i = 0; i < kLength; i++) {
       buffer_[i] = i % 189;
diff --git a/libunwindstack/tests/MemoryOfflineTest.cpp b/libunwindstack/tests/MemoryOfflineTest.cpp
index 14d58e6..d0c441b 100644
--- a/libunwindstack/tests/MemoryOfflineTest.cpp
+++ b/libunwindstack/tests/MemoryOfflineTest.cpp
@@ -19,8 +19,8 @@
 #include <gtest/gtest.h>
 
 #include <android-base/file.h>
-#include <android-base/test_utils.h>
-#include <unwindstack/Memory.h>
+
+#include "MemoryOffline.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index cb1a0c9..2d4f141 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -15,43 +15,45 @@
  */
 
 #include <stdint.h>
-#include <string.h>
 
 #include <memory>
 #include <vector>
 
 #include <gtest/gtest.h>
 
-#include <unwindstack/Memory.h>
-
 #include "MemoryFake.h"
+#include "MemoryRange.h"
 
 namespace unwindstack {
 
-TEST(MemoryRangeTest, read) {
-  std::vector<uint8_t> src(1024);
-  memset(src.data(), 0x4c, 1024);
-  MemoryFake* memory_fake = new MemoryFake;
-  std::shared_ptr<Memory> process_memory(memory_fake);
-  memory_fake->SetMemory(9001, src);
+class MemoryRangeTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    process_memory_.reset();
+    memory_fake_ = new MemoryFake;
+    process_memory_.reset(memory_fake_);
+  }
 
-  MemoryRange range(process_memory, 9001, src.size(), 0);
+  std::shared_ptr<Memory> process_memory_;
+  MemoryFake* memory_fake_ = nullptr;
+};
+
+TEST_F(MemoryRangeTest, read_fully) {
+  memory_fake_->SetMemoryBlock(9000, 2048, 0x4c);
+
+  MemoryRange range(process_memory_, 9001, 1024, 0);
 
   std::vector<uint8_t> dst(1024);
-  ASSERT_TRUE(range.ReadFully(0, dst.data(), src.size()));
-  for (size_t i = 0; i < 1024; i++) {
+  ASSERT_TRUE(range.ReadFully(0, dst.data(), dst.size()));
+  for (size_t i = 0; i < dst.size(); i++) {
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
 }
 
-TEST(MemoryRangeTest, read_near_limit) {
-  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);
+TEST_F(MemoryRangeTest, read_fully_near_limit) {
+  memory_fake_->SetMemoryBlock(0, 8192, 0x4c);
 
-  MemoryRange range(process_memory, 1000, 1024, 0);
+  MemoryRange range(process_memory_, 1000, 1024, 0);
 
   std::vector<uint8_t> dst(1024);
   ASSERT_TRUE(range.ReadFully(1020, dst.data(), 4));
@@ -68,7 +70,7 @@
   ASSERT_TRUE(range.ReadFully(1020, dst.data(), 4));
 }
 
-TEST(MemoryRangeTest, read_overflow) {
+TEST_F(MemoryRangeTest, read_fully_overflow) {
   std::vector<uint8_t> buffer(100);
 
   std::shared_ptr<Memory> process_memory(new MemoryFakeAlwaysReadZero);
@@ -76,19 +78,28 @@
   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);
+TEST_F(MemoryRangeTest, read) {
+  memory_fake_->SetMemoryBlock(0, 4096, 0x4c);
 
-  MemoryRange range(process_memory, 1000, 1024, 0);
+  MemoryRange range(process_memory_, 1000, 1024, 0);
+
   std::vector<uint8_t> dst(1024);
-  ASSERT_EQ(4U, range.Read(1020, dst.data(), 1024));
+  ASSERT_EQ(4U, range.Read(1020, dst.data(), dst.size()));
   for (size_t i = 0; i < 4; i++) {
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
 }
 
+TEST_F(MemoryRangeTest, read_non_zero_offset) {
+  memory_fake_->SetMemoryBlock(1000, 1024, 0x12);
+
+  MemoryRange range(process_memory_, 1000, 1024, 400);
+
+  std::vector<uint8_t> dst(1024);
+  ASSERT_EQ(1024U, range.Read(400, dst.data(), dst.size()));
+  for (size_t i = 0; i < dst.size(); i++) {
+    ASSERT_EQ(0x12U, dst[i]) << "Failed at byte " << i;
+  }
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRangesTest.cpp b/libunwindstack/tests/MemoryRangesTest.cpp
new file mode 100644
index 0000000..e4e9fc4
--- /dev/null
+++ b/libunwindstack/tests/MemoryRangesTest.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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 <vector>
+
+#include <gtest/gtest.h>
+
+#include "MemoryFake.h"
+#include "MemoryRange.h"
+
+namespace unwindstack {
+
+class MemoryRangesTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    MemoryFake* memory = new MemoryFake;
+    process_memory_.reset(memory);
+    memory->SetMemoryBlock(1000, 5000, 0x15);
+    memory->SetMemoryBlock(6000, 12000, 0x26);
+    memory->SetMemoryBlock(14000, 20000, 0x37);
+    memory->SetMemoryBlock(20000, 22000, 0x48);
+
+    ranges_.reset(new MemoryRanges);
+    ranges_->Insert(new MemoryRange(process_memory_, 15000, 100, 4000));
+    ranges_->Insert(new MemoryRange(process_memory_, 10000, 2000, 2000));
+    ranges_->Insert(new MemoryRange(process_memory_, 3000, 1000, 0));
+    ranges_->Insert(new MemoryRange(process_memory_, 19000, 1000, 6000));
+    ranges_->Insert(new MemoryRange(process_memory_, 20000, 1000, 7000));
+  }
+
+  std::shared_ptr<Memory> process_memory_;
+  std::unique_ptr<MemoryRanges> ranges_;
+};
+
+TEST_F(MemoryRangesTest, read) {
+  std::vector<uint8_t> dst(2000);
+  size_t bytes = ranges_->Read(0, dst.data(), dst.size());
+  ASSERT_EQ(1000UL, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x15U, dst[i]) << "Failed at byte " << i;
+  }
+
+  bytes = ranges_->Read(2000, dst.data(), dst.size());
+  ASSERT_EQ(2000UL, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x26U, dst[i]) << "Failed at byte " << i;
+  }
+
+  bytes = ranges_->Read(4000, dst.data(), dst.size());
+  ASSERT_EQ(100UL, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x37U, dst[i]) << "Failed at byte " << i;
+  }
+}
+
+TEST_F(MemoryRangesTest, read_fail) {
+  std::vector<uint8_t> dst(4096);
+  ASSERT_EQ(0UL, ranges_->Read(1000, dst.data(), dst.size()));
+  ASSERT_EQ(0UL, ranges_->Read(5000, dst.data(), dst.size()));
+  ASSERT_EQ(0UL, ranges_->Read(8000, dst.data(), dst.size()));
+}
+
+TEST_F(MemoryRangesTest, read_across_ranges) {
+  // The MemoryRanges object does not support reading across a range,
+  // so this will only read in the first range.
+  std::vector<uint8_t> dst(4096);
+  size_t bytes = ranges_->Read(6000, dst.data(), dst.size());
+  ASSERT_EQ(1000UL, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x37U, dst[i]) << "Failed at byte " << i;
+  }
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index fb56e8a..c90dedc 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -30,7 +30,7 @@
 #include <android-base/file.h>
 #include <gtest/gtest.h>
 
-#include <unwindstack/Memory.h>
+#include "MemoryRemote.h"
 
 #include "MemoryFake.h"
 #include "TestUtils.h"
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
index 4a9ed9f..3655984 100644
--- a/libunwindstack/tests/MemoryTest.cpp
+++ b/libunwindstack/tests/MemoryTest.cpp
@@ -51,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");
 
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index d6ca9b7..207d46e 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -23,6 +23,8 @@
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
 
+#include "Check.h"
+
 namespace unwindstack {
 
 class RegsFake : public Regs {
@@ -47,7 +49,10 @@
 
   void IterateRegisters(std::function<void(const char*, uint64_t)>) override {}
 
-  bool Is32Bit() { return false; }
+  bool Is32Bit() {
+    CHECK(fake_arch_ != ARCH_UNKNOWN);
+    return fake_arch_ == ARCH_ARM || fake_arch_ == ARCH_X86 || fake_arch_ == ARCH_MIPS;
+  }
 
   uint64_t GetPcAdjustment(uint64_t, Elf*) override { return 2; }
 
diff --git a/libunwindstack/tests/RegsInfoTest.cpp b/libunwindstack/tests/RegsInfoTest.cpp
new file mode 100644
index 0000000..a6bc2c5
--- /dev/null
+++ b/libunwindstack/tests/RegsInfoTest.cpp
@@ -0,0 +1,92 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <unwindstack/Regs.h>
+
+#include "RegsFake.h"
+#include "RegsInfo.h"
+
+namespace unwindstack {
+
+TEST(RegsInfoTest, single_uint32_t) {
+  RegsImplFake<uint32_t> regs(10);
+  RegsInfo<uint32_t> info(&regs);
+
+  regs[1] = 0x100;
+  ASSERT_FALSE(info.IsSaved(1));
+  ASSERT_EQ(0x100U, info.Get(1));
+  ASSERT_EQ(10, info.Total());
+
+  uint32_t* value = info.Save(1);
+  ASSERT_EQ(value, &regs[1]);
+  regs[1] = 0x200;
+  ASSERT_TRUE(info.IsSaved(1));
+  ASSERT_EQ(0x100U, info.Get(1));
+  ASSERT_EQ(0x200U, regs[1]);
+}
+
+TEST(RegsInfoTest, single_uint64_t) {
+  RegsImplFake<uint64_t> regs(20);
+  RegsInfo<uint64_t> info(&regs);
+
+  regs[3] = 0x300;
+  ASSERT_FALSE(info.IsSaved(3));
+  ASSERT_EQ(0x300U, info.Get(3));
+  ASSERT_EQ(20, info.Total());
+
+  uint64_t* value = info.Save(3);
+  ASSERT_EQ(value, &regs[3]);
+  regs[3] = 0x400;
+  ASSERT_TRUE(info.IsSaved(3));
+  ASSERT_EQ(0x300U, info.Get(3));
+  ASSERT_EQ(0x400U, regs[3]);
+}
+
+TEST(RegsInfoTest, all) {
+  RegsImplFake<uint64_t> regs(64);
+  RegsInfo<uint64_t> info(&regs);
+
+  for (uint32_t i = 0; i < 64; i++) {
+    regs[i] = i * 0x100;
+    ASSERT_EQ(i * 0x100, info.Get(i)) << "Reg " + std::to_string(i) + " failed.";
+  }
+
+  for (uint32_t i = 0; i < 64; i++) {
+    ASSERT_FALSE(info.IsSaved(i)) << "Reg " + std::to_string(i) + " failed.";
+    uint64_t* reg = info.Save(i);
+    ASSERT_EQ(reg, &regs[i]) << "Reg " + std::to_string(i) + " failed.";
+    *reg = i * 0x1000 + 0x100;
+    ASSERT_EQ(i * 0x1000 + 0x100, regs[i]) << "Reg " + std::to_string(i) + " failed.";
+  }
+
+  for (uint32_t i = 0; i < 64; i++) {
+    ASSERT_TRUE(info.IsSaved(i)) << "Reg " + std::to_string(i) + " failed.";
+    ASSERT_EQ(i * 0x100, info.Get(i)) << "Reg " + std::to_string(i) + " failed.";
+  }
+}
+
+TEST(RegsInfoTest, invalid_register) {
+  RegsImplFake<uint64_t> regs(64);
+  RegsInfo<uint64_t> info(&regs);
+
+  EXPECT_DEATH(info.Save(RegsInfo<uint64_t>::MAX_REGISTERS), "");
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsIterateTest.cpp b/libunwindstack/tests/RegsIterateTest.cpp
index 9a27dbd..7e36953 100644
--- a/libunwindstack/tests/RegsIterateTest.cpp
+++ b/libunwindstack/tests/RegsIterateTest.cpp
@@ -236,7 +236,7 @@
 }
 
 using RegTypes = ::testing::Types<RegsArm, RegsArm64, RegsX86, RegsX86_64, RegsMips, RegsMips64>;
-TYPED_TEST_CASE(RegsIterateTest, RegTypes);
+TYPED_TEST_SUITE(RegsIterateTest, RegTypes);
 
 TYPED_TEST(RegsIterateTest, iterate) {
   std::vector<Register> expected = ExpectedRegisters<TypeParam>();
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 90c3fe6..472d1cf 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -182,7 +182,7 @@
   RegsX86_64 regs_x86_64;
   RegsMips regs_mips;
   RegsMips64 regs_mips64;
-  MapInfo map_info(0x1000, 0x2000);
+  MapInfo map_info(nullptr, 0x1000, 0x2000, 0, 0, "");
   Elf* invalid_elf = new Elf(nullptr);
   map_info.elf.reset(invalid_elf);
 
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index 45a7b58..ae3c349 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -55,7 +55,7 @@
 
   MemoryFake memory_;
 };
-TYPED_TEST_CASE_P(SymbolsTest);
+TYPED_TEST_SUITE_P(SymbolsTest);
 
 TYPED_TEST_P(SymbolsTest, function_bounds_check) {
   Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
@@ -70,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) {
@@ -98,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);
 
@@ -107,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;
@@ -115,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) {
@@ -144,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);
 }
@@ -203,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);
@@ -265,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.
@@ -302,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();
@@ -317,15 +289,15 @@
   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);
 }
@@ -381,20 +353,20 @@
   EXPECT_FALSE(symbols.GetGlobal<TypeParam>(&this->memory_, "function_1", &offset));
 
   std::string name;
-  EXPECT_TRUE(symbols.GetName<TypeParam>(0x10002, 0, &this->memory_, &name, &offset));
+  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, 0, &this->memory_, &name, &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, get_global);
+REGISTER_TYPED_TEST_SUITE_P(SymbolsTest, function_bounds_check, no_symbol, multiple_entries,
+                            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);
+INSTANTIATE_TYPED_TEST_SUITE_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.cpp b/libunwindstack/tests/TestUtils.cpp
new file mode 100644
index 0000000..e76f5f8
--- /dev/null
+++ b/libunwindstack/tests/TestUtils.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <stdint.h>
+
+#include <gtest/gtest.h>
+
+namespace unwindstack {
+
+void TestCheckForLeaks(void (*unwind_func)(void*), void* data) {
+  static constexpr size_t kNumLeakLoops = 200;
+  static constexpr size_t kMaxAllowedLeakBytes = 32 * 1024;
+
+  size_t first_allocated_bytes = 0;
+  size_t last_allocated_bytes = 0;
+  for (size_t i = 0; i < kNumLeakLoops; i++) {
+    unwind_func(data);
+
+    size_t allocated_bytes = mallinfo().uordblks;
+    if (first_allocated_bytes == 0) {
+      first_allocated_bytes = allocated_bytes;
+    } else if (last_allocated_bytes > first_allocated_bytes) {
+      // Check that the memory did not increase too much over the first loop.
+      ASSERT_LE(last_allocated_bytes - first_allocated_bytes, kMaxAllowedLeakBytes);
+    }
+    last_allocated_bytes = allocated_bytes;
+  }
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/TestUtils.h b/libunwindstack/tests/TestUtils.h
index 8c31aa6..a4d7b9b 100644
--- a/libunwindstack/tests/TestUtils.h
+++ b/libunwindstack/tests/TestUtils.h
@@ -50,6 +50,8 @@
   return ready;
 }
 
+void TestCheckForLeaks(void (*unwind_func)(void*), void* data);
+
 }  // namespace unwindstack
 
 #endif  // _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index a187dc3..baada82 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -18,6 +18,7 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/mman.h>
 #include <unistd.h>
 
 #include <gtest/gtest.h>
@@ -34,7 +35,6 @@
 #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>
@@ -42,6 +42,8 @@
 #include <unwindstack/Unwinder.h>
 
 #include "ElfTestUtils.h"
+#include "MemoryOffline.h"
+#include "TestUtils.h"
 
 namespace unwindstack {
 
@@ -60,7 +62,7 @@
     free(cwd_);
   }
 
-  void Init(const char* file_dir, ArchEnum arch) {
+  void Init(const char* file_dir, ArchEnum arch, bool add_stack = true) {
     dir_ = TestGetFileDirectory() + "offline/" + file_dir;
 
     std::string data;
@@ -69,23 +71,25 @@
     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;
+    if (add_stack) {
+      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());
         }
-        AddMemory(stack_name, stack_memory.get());
+        process_memory_.reset(stack_memory.release());
       }
-      process_memory_.reset(stack_memory.release());
     }
 
     switch (arch) {
@@ -202,6 +206,7 @@
 TEST_F(UnwindOfflineTest, pc_straddle_arm) {
   ASSERT_NO_FATAL_FAILURE(Init("straddle_arm/", ARCH_ARM));
 
+  std::unique_ptr<Regs> regs_copy(regs_->Clone());
   Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
   unwinder.Unwind();
 
@@ -209,8 +214,8 @@
   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"
+      "  #01 pc 00006a1b  libbase.so (android::base::DefaultAborter(char const*)+6)\n"
+      "  #02 pc 00007441  libbase.so (android::base::LogMessage::~LogMessage()+748)\n"
       "  #03 pc 00015147  /does/not/exist/libhidlbase.so\n",
       frame_info);
   EXPECT_EQ(0xf31ea9f8U, unwinder.frames()[0].pc);
@@ -221,6 +226,22 @@
   EXPECT_EQ(0xe9c86730U, unwinder.frames()[2].sp);
   EXPECT_EQ(0xf3367147U, unwinder.frames()[3].pc);
   EXPECT_EQ(0xe9c86778U, unwinder.frames()[3].sp);
+
+  // Display build ids now.
+  unwinder.SetRegs(regs_copy.get());
+  unwinder.SetDisplayBuildID(true);
+  unwinder.Unwind();
+
+  frame_info = DumpFrames(unwinder);
+  ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 0001a9f8  libc.so (abort+64) (BuildId: 2dd0d4ba881322a0edabeed94808048c)\n"
+      "  #01 pc 00006a1b  libbase.so (android::base::DefaultAborter(char const*)+6) (BuildId: "
+      "ed43842c239cac1a618e600ea91c4cbd)\n"
+      "  #02 pc 00007441  libbase.so (android::base::LogMessage::~LogMessage()+748) (BuildId: "
+      "ed43842c239cac1a618e600ea91c4cbd)\n"
+      "  #03 pc 00015147  /does/not/exist/libhidlbase.so\n",
+      frame_info);
 }
 
 TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
@@ -233,9 +254,10 @@
   ASSERT_EQ(2U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
   EXPECT_EQ(
       "  #00 pc 0006dc49  libandroid_runtime.so "
-      "(_ZN7android14AndroidRuntime15javaThreadShellEPv+80)\n"
+      "(android::AndroidRuntime::javaThreadShell(void*)+80)\n"
       "  #01 pc 0006dce5  libandroid_runtime.so "
-      "(_ZN7android14AndroidRuntime19javaCreateThreadEtcEPFiPvES1_PKcijPS1_)\n",
+      "(android::AndroidRuntime::javaCreateThreadEtc(int (*)(void*), void*, char const*, int, "
+      "unsigned int, void**))\n",
       frame_info);
   EXPECT_EQ(0xf1f6dc49U, unwinder.frames()[0].pc);
   EXPECT_EQ(0xd8fe6930U, unwinder.frames()[0].sp);
@@ -256,10 +278,10 @@
       "  #01 pc 000000000042a078  libunwindstack_test (SignalMiddleFunction+8)\n"
       "  #02 pc 000000000042a08c  libunwindstack_test (SignalOuterFunction+8)\n"
       "  #03 pc 000000000042d8fc  libunwindstack_test "
-      "(_ZN11unwindstackL19RemoteThroughSignalEij+20)\n"
+      "(unwindstack::RemoteThroughSignal(int, unsigned int)+20)\n"
       "  #04 pc 000000000042d8d8  libunwindstack_test "
-      "(_ZN11unwindstack37UnwindTest_remote_through_signal_Test8TestBodyEv+32)\n"
-      "  #05 pc 0000000000455d70  libunwindstack_test (_ZN7testing4Test3RunEv+392)\n",
+      "(unwindstack::UnwindTest_remote_through_signal_Test::TestBody()+32)\n"
+      "  #05 pc 0000000000455d70  libunwindstack_test (testing::Test::Run()+392)\n",
       frame_info);
   EXPECT_EQ(0x64d09d4fd8U, unwinder.frames()[0].pc);
   EXPECT_EQ(0x7fe0d84040U, unwinder.frames()[0].sp);
@@ -295,54 +317,57 @@
   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"
+      "  #00 pc 00068fb8  libarttestd.so (art::CauseSegfault()+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, "
+      "  #02 pc 000021a8  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"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+885)\n"
       "  #06 pc 0039cf0d  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
       "  #07 pc 00392552  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+354)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+354)\n"
       "  #08 pc 0039399a  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+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"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+885)\n"
       "  #14 pc 0039cf0d  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
       "  #15 pc 00392552  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+354)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+354)\n"
       "  #16 pc 0039399a  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+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"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+885)\n"
       "  #22 pc 0039cf0d  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
       "  #23 pc 00392552  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+354)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+354)\n"
       "  #24 pc 0039399a  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+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 "
@@ -350,81 +375,86 @@
       "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"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+907)\n"
       "  #30 pc 0039cf0d  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
       "  #31 pc 00392552  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+354)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+354)\n"
       "  #32 pc 0039399a  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+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"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+885)\n"
       "  #38 pc 0039cf0d  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
       "  #39 pc 00392552  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+354)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+354)\n"
       "  #40 pc 0039399a  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+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"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+885)\n"
       "  #46 pc 0039cf0d  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
       "  #47 pc 00392552  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+354)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+354)\n"
       "  #48 pc 0039399a  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+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"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+907)\n"
       "  #54 pc 0039cf0d  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
       "  #55 pc 00392552  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+354)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+354)\n"
       "  #56 pc 0039399a  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+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"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+907)\n"
       "  #61 pc 005aac95  libartd.so "
-      "(_ZN3artL18InvokeWithArgArrayERKNS_33ScopedObjectAccessAlreadyRunnableEPNS_9ArtMethodEPNS_"
-      "8ArgArrayEPNS_6JValueEPKc+85)\n"
+      "(art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, "
+      "art::ArgArray*, art::JValue*, char const*)+85)\n"
       "  #62 pc 005aab5a  libartd.so "
-      "(_ZN3art17InvokeWithVarArgsERKNS_33ScopedObjectAccessAlreadyRunnableEP8_jobjectP10_"
-      "jmethodIDPc+362)\n"
+      "(art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, "
+      "_jmethodID*, char*)+362)\n"
       "  #63 pc 0048a3dd  libartd.so "
-      "(_ZN3art3JNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDPc+125)\n"
+      "(art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, char*)+125)\n"
       "  #64 pc 0018448c  libartd.so "
-      "(_ZN3art8CheckJNI11CallMethodVEPKcP7_JNIEnvP8_jobjectP7_jclassP10_jmethodIDPcNS_"
-      "9Primitive4TypeENS_10InvokeTypeE+1964)\n"
+      "(art::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, char*, "
+      "art::Primitive::Type, art::InvokeType)+1964)\n"
       "  #65 pc 0017cf06  libartd.so "
-      "(_ZN3art8CheckJNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDPc+70)\n"
+      "(art::CheckJNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, char*)+70)\n"
       "  #66 pc 00001d8c  dalvikvm32 "
-      "(_ZN7_JNIEnv20CallStaticVoidMethodEP7_jclassP10_jmethodIDz+60)\n"
+      "(_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+60)\n"
       "  #67 pc 00001a80  dalvikvm32 (main+1312)\n"
       "  #68 pc 00018275  libc.so\n",
       frame_info);
@@ -590,38 +620,40 @@
   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, "
+      "  #01 pc 0000212d  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"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+864)\n"
       "  #06 pc 00247833  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
       "  #07 pc 0022e935  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+244)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+244)\n"
       "  #08 pc 0022f71d  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+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"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+864)\n"
       "  #15 pc 00247833  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
       "  #16 pc 0022e935  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+244)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+244)\n"
       "  #17 pc 0022f71d  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+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, "
@@ -629,16 +661,17 @@
       "  #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"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+864)\n"
       "  #24 pc 00247833  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
       "  #25 pc 0022e935  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+244)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+244)\n"
       "  #26 pc 0022f71d  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+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 "
@@ -647,85 +680,90 @@
       "  #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"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+882)\n"
       "  #33 pc 00247833  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
       "  #34 pc 0022e935  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+244)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+244)\n"
       "  #35 pc 0022f71d  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+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"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+864)\n"
       "  #42 pc 00247833  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
       "  #43 pc 0022e935  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+244)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+244)\n"
       "  #44 pc 0022f71d  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+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"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+864)\n"
       "  #51 pc 00247833  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
       "  #52 pc 0022e935  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+244)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+244)\n"
       "  #53 pc 0022f71d  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+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"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+882)\n"
       "  #60 pc 00247833  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
       "  #61 pc 0022e935  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+244)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+244)\n"
       "  #62 pc 0022f71d  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+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"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+882)\n"
       "  #68 pc 003b292d  libartd.so "
-      "(_ZN3artL18InvokeWithArgArrayERKNS_33ScopedObjectAccessAlreadyRunnableEPNS_9ArtMethodEPNS_"
-      "8ArgArrayEPNS_6JValueEPKc+52)\n"
+      "(art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, "
+      "art::ArgArray*, art::JValue*, char const*)+52)\n"
       "  #69 pc 003b26c3  libartd.so "
-      "(_ZN3art17InvokeWithVarArgsERKNS_33ScopedObjectAccessAlreadyRunnableEP8_jobjectP10_"
-      "jmethodIDSt9__va_list+210)\n"
+      "(art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, "
+      "_jmethodID*, std::__va_list)+210)\n"
       "  #70 pc 00308411  libartd.so "
-      "(_ZN3art3JNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDSt9__va_list+76)\n"
+      "(art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+76)\n"
       "  #71 pc 000e6a9f  libartd.so "
-      "(_ZN3art8CheckJNI11CallMethodVEPKcP7_JNIEnvP8_jobjectP7_jclassP10_jmethodIDSt9__va_listNS_"
-      "9Primitive4TypeENS_10InvokeTypeE+1486)\n"
+      "(art::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, "
+      "std::__va_list, art::Primitive::Type, art::InvokeType)+1486)\n"
       "  #72 pc 000e19b9  libartd.so "
-      "(_ZN3art8CheckJNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDSt9__va_list+40)\n"
+      "(art::CheckJNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+40)\n"
       "  #73 pc 0000159f  dalvikvm32 "
-      "(_ZN7_JNIEnv20CallStaticVoidMethodEP7_jclassP10_jmethodIDz+30)\n"
+      "(_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+30)\n"
       "  #74 pc 00001349  dalvikvm32 (main+896)\n"
       "  #75 pc 000850c9  libc.so\n",
       frame_info);
@@ -883,6 +921,43 @@
   EXPECT_EQ(0xff85f0c0U, unwinder.frames()[75].sp);
 }
 
+struct LeakType {
+  LeakType(Maps* maps, Regs* regs, std::shared_ptr<Memory>& process_memory)
+      : maps(maps), regs(regs), process_memory(process_memory) {}
+
+  Maps* maps;
+  Regs* regs;
+  std::shared_ptr<Memory>& process_memory;
+};
+
+static void OfflineUnwind(void* data) {
+  LeakType* leak_data = reinterpret_cast<LeakType*>(data);
+
+  std::unique_ptr<Regs> regs_copy(leak_data->regs->Clone());
+  JitDebug jit_debug(leak_data->process_memory);
+  Unwinder unwinder(128, leak_data->maps, regs_copy.get(), leak_data->process_memory);
+  unwinder.SetJitDebug(&jit_debug, regs_copy->Arch());
+  unwinder.Unwind();
+  ASSERT_EQ(76U, unwinder.NumFrames());
+}
+
+TEST_F(UnwindOfflineTest, unwind_offline_check_for_leaks) {
+  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);
+
+  LeakType data(maps_.get(), regs_.get(), process_memory_);
+  TestCheckForLeaks(OfflineUnwind, &data);
+}
+
 // 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.
@@ -996,41 +1071,44 @@
       "(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"
+      "(art::jit::Jit::MaybeDoOnStackReplacement(art::Thread*, art::ArtMethod*, unsigned int, int, "
+      "art::JValue*)+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"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+374)\n"
       "  #08 pc 001dc593  libart.so "
-      "(_ZN3art11interpreter33ArtInterpreterToInterpreterBridgeEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameEPNS_6JValueE+154)\n"
+      "(art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, "
+      "art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+154)\n"
       "  #09 pc 001f4d01  libart.so "
-      "(_ZN3art11interpreter6DoCallILb0ELb0EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_"
-      "11InstructionEtPNS_6JValueE+732)\n"
+      "(bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, "
+      "art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+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"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+374)\n"
       "  #14 pc 001dc4d5  libart.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+92)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+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"
+      "  #19 pc 000b468d  libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned "
+      "int, art::JValue*, char const*)+136)\n"
       "  #20 pc 00362f49  libart.so "
-      "(_ZN3art12_GLOBAL__N_118InvokeWithArgArrayERKNS_33ScopedObjectAccessAlreadyRunnableEPNS_"
-      "9ArtMethodEPNS0_8ArgArrayEPNS_6JValueEPKc+52)\n"
+      "(art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable "
+      "const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char "
+      "const*)+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"
+      "(art::InvokeVirtualOrInterfaceWithJValues(art::ScopedObjectAccessAlreadyRunnable const&, "
+      "_jobject*, _jmethodID*, jvalue*)+332)\n"
+      "  #22 pc 003851dd  libart.so (art::Thread::CreateCallback(void*)+868)\n"
+      "  #23 pc 00062925  libc.so (__pthread_start(void*)+22)\n"
       "  #24 pc 0001de39  libc.so (__start_thread+24)\n",
       frame_info);
   EXPECT_EQ(0xd025c788U, unwinder.frames()[0].pc);
@@ -1085,6 +1163,46 @@
   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 (art::Thread::CreateCallback(void*)+868)\n"
+      "  #04 pc 00062925  libc.so (__pthread_start(void*)+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));
 
@@ -1094,29 +1212,29 @@
   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"
+      "  #00 pc 0032bfa0  libunwindstack_test (SignalInnerFunction+40)\n"
+      "  #01 pc 0032bfeb  libunwindstack_test (SignalMiddleFunction+2)\n"
+      "  #02 pc 0032bff3  libunwindstack_test (SignalOuterFunction+2)\n"
+      "  #03 pc 0032fed3  libunwindstack_test "
+      "(unwindstack::SignalCallerHandler(int, siginfo*, void*)+26)\n"
+      "  #04 pc 0002652c  libc.so (__restore)\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",
+      "  #06 pc 0032c2d9  libunwindstack_test (InnerFunction+736)\n"
+      "  #07 pc 0032cc4f  libunwindstack_test (MiddleFunction+42)\n"
+      "  #08 pc 0032cc81  libunwindstack_test (OuterFunction+42)\n"
+      "  #09 pc 0032e547  libunwindstack_test "
+      "(unwindstack::RemoteThroughSignal(int, unsigned int)+270)\n"
+      "  #10 pc 0032ed99  libunwindstack_test "
+      "(unwindstack::UnwindTest_remote_through_signal_with_invalid_func_Test::TestBody()+16)\n"
+      "  #11 pc 00354453  libunwindstack_test (testing::Test::Run()+154)\n"
+      "  #12 pc 00354de7  libunwindstack_test (testing::TestInfo::Run()+194)\n"
+      "  #13 pc 00355105  libunwindstack_test (testing::TestCase::Run()+180)\n"
+      "  #14 pc 0035a215  libunwindstack_test "
+      "(testing::internal::UnitTestImpl::RunAllTests()+664)\n"
+      "  #15 pc 00359f4f  libunwindstack_test (testing::UnitTest::Run()+110)\n"
+      "  #16 pc 0034d3db  libunwindstack_test (main+38)\n"
+      "  #17 pc 00092c0d  libc.so (__libc_init+48)\n"
+      "  #18 pc 0004202f  libunwindstack_test (_start_main+38)\n",
       frame_info);
 
   EXPECT_EQ(0x2e55fa0U, unwinder.frames()[0].pc);
@@ -1127,7 +1245,7 @@
   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(0xf413652cU, 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);
@@ -1159,4 +1277,184 @@
   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 (android::IPCThreadState::talkWithDriver(bool)+204)\n"
+      "  #03 pc 00039633  libbinder.so (android::IPCThreadState::getAndExecuteCommand()+10)\n"
+      "  #04 pc 00039b57  libbinder.so (android::IPCThreadState::joinThreadPool(bool)+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);
+}
+
+TEST_F(UnwindOfflineTest, shared_lib_in_apk_arm64) {
+  ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_arm64/", ARCH_ARM64));
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 000000000014ccbc  linker64 (__dl_syscall+28)\n"
+      "  #01 pc 000000000005426c  linker64 "
+      "(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
+      "  #02 pc 00000000000008c0  vdso.so (__kernel_rt_sigreturn)\n"
+      "  #03 pc 00000000000846f4  libc.so (abort+172)\n"
+      "  #04 pc 0000000000084ad4  libc.so (__assert2+36)\n"
+      "  #05 pc 000000000003d5b4  ANGLEPrebuilt.apk!libfeature_support_angle.so (offset 0x4000) "
+      "(ANGLEGetUtilityAPI+56)\n"
+      "  #06 pc 000000000007fe68  libc.so (__libc_init)\n",
+      frame_info);
+
+  EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
+  EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x7e82b5726cULL, unwinder.frames()[1].pc);
+  EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x7e82b018c0ULL, unwinder.frames()[2].pc);
+  EXPECT_EQ(0x7df8ca3da0ULL, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x7e7eecc6f4ULL, unwinder.frames()[3].pc);
+  EXPECT_EQ(0x7dabf3db60ULL, unwinder.frames()[3].sp);
+  EXPECT_EQ(0x7e7eeccad4ULL, unwinder.frames()[4].pc);
+  EXPECT_EQ(0x7dabf3dc40ULL, unwinder.frames()[4].sp);
+  EXPECT_EQ(0x7dabc405b4ULL, unwinder.frames()[5].pc);
+  EXPECT_EQ(0x7dabf3dc50ULL, unwinder.frames()[5].sp);
+  EXPECT_EQ(0x7e7eec7e68ULL, unwinder.frames()[6].pc);
+  EXPECT_EQ(0x7dabf3dc70ULL, unwinder.frames()[6].sp);
+  // Ignore top frame since the test code was modified to end in __libc_init.
+}
+
+TEST_F(UnwindOfflineTest, shared_lib_in_apk_memory_only_arm64) {
+  ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_memory_only_arm64/", ARCH_ARM64));
+  // Add the memory that represents the shared library.
+  MemoryOfflineParts* memory = reinterpret_cast<MemoryOfflineParts*>(process_memory_.get());
+  AddMemory(dir_ + "lib_mem.data", memory);
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 000000000014ccbc  linker64 (__dl_syscall+28)\n"
+      "  #01 pc 000000000005426c  linker64 "
+      "(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
+      "  #02 pc 00000000000008c0  vdso.so (__kernel_rt_sigreturn)\n"
+      "  #03 pc 00000000000846f4  libc.so (abort+172)\n"
+      "  #04 pc 0000000000084ad4  libc.so (__assert2+36)\n"
+      "  #05 pc 000000000003d5b4  ANGLEPrebuilt.apk (offset 0x21d5000)\n"
+      "  #06 pc 000000000007fe68  libc.so (__libc_init)\n",
+      frame_info);
+
+  EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
+  EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x7e82b5726cULL, unwinder.frames()[1].pc);
+  EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x7e82b018c0ULL, unwinder.frames()[2].pc);
+  EXPECT_EQ(0x7df8ca3da0ULL, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x7e7eecc6f4ULL, unwinder.frames()[3].pc);
+  EXPECT_EQ(0x7dabf3db60ULL, unwinder.frames()[3].sp);
+  EXPECT_EQ(0x7e7eeccad4ULL, unwinder.frames()[4].pc);
+  EXPECT_EQ(0x7dabf3dc40ULL, unwinder.frames()[4].sp);
+  EXPECT_EQ(0x7dabc405b4ULL, unwinder.frames()[5].pc);
+  EXPECT_EQ(0x7dabf3dc50ULL, unwinder.frames()[5].sp);
+  EXPECT_EQ(0x7e7eec7e68ULL, unwinder.frames()[6].pc);
+  EXPECT_EQ(0x7dabf3dc70ULL, unwinder.frames()[6].sp);
+  // Ignore top frame since the test code was modified to end in __libc_init.
+}
+
+TEST_F(UnwindOfflineTest, shared_lib_in_apk_single_map_arm64) {
+  ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_single_map_arm64/", ARCH_ARM64));
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(13U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 00000000000814bc  libc.so (syscall+28)\n"
+      "  #01 pc 00000000008cdf5c  test.apk (offset 0x5000)\n"
+      "  #02 pc 00000000008cde9c  test.apk (offset 0x5000)\n"
+      "  #03 pc 00000000008cdd70  test.apk (offset 0x5000)\n"
+      "  #04 pc 00000000008ce408  test.apk (offset 0x5000)\n"
+      "  #05 pc 00000000008ce8d8  test.apk (offset 0x5000)\n"
+      "  #06 pc 00000000008ce814  test.apk (offset 0x5000)\n"
+      "  #07 pc 00000000008bcf60  test.apk (offset 0x5000)\n"
+      "  #08 pc 0000000000133024  test.apk (offset 0x5000)\n"
+      "  #09 pc 0000000000134ad0  test.apk (offset 0x5000)\n"
+      "  #10 pc 0000000000134b64  test.apk (offset 0x5000)\n"
+      "  #11 pc 00000000000e406c  libc.so (__pthread_start(void*)+36)\n"
+      "  #12 pc 0000000000085e18  libc.so (__start_thread+64)\n",
+      frame_info);
+
+  EXPECT_EQ(0x7cbe0b14bcULL, unwinder.frames()[0].pc);
+  EXPECT_EQ(0x7be4f077d0ULL, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x7be6715f5cULL, unwinder.frames()[1].pc);
+  EXPECT_EQ(0x7be4f077d0ULL, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x7be6715e9cULL, unwinder.frames()[2].pc);
+  EXPECT_EQ(0x7be4f07800ULL, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x7be6715d70ULL, unwinder.frames()[3].pc);
+  EXPECT_EQ(0x7be4f07840ULL, unwinder.frames()[3].sp);
+  EXPECT_EQ(0x7be6716408ULL, unwinder.frames()[4].pc);
+  EXPECT_EQ(0x7be4f07860ULL, unwinder.frames()[4].sp);
+  EXPECT_EQ(0x7be67168d8ULL, unwinder.frames()[5].pc);
+  EXPECT_EQ(0x7be4f07880ULL, unwinder.frames()[5].sp);
+  EXPECT_EQ(0x7be6716814ULL, unwinder.frames()[6].pc);
+  EXPECT_EQ(0x7be4f078f0ULL, unwinder.frames()[6].sp);
+  EXPECT_EQ(0x7be6704f60ULL, unwinder.frames()[7].pc);
+  EXPECT_EQ(0x7be4f07910ULL, unwinder.frames()[7].sp);
+  EXPECT_EQ(0x7be5f7b024ULL, unwinder.frames()[8].pc);
+  EXPECT_EQ(0x7be4f07950ULL, unwinder.frames()[8].sp);
+  EXPECT_EQ(0x7be5f7cad0ULL, unwinder.frames()[9].pc);
+  EXPECT_EQ(0x7be4f07aa0ULL, unwinder.frames()[9].sp);
+  EXPECT_EQ(0x7be5f7cb64ULL, unwinder.frames()[10].pc);
+  EXPECT_EQ(0x7be4f07ce0ULL, unwinder.frames()[10].sp);
+  EXPECT_EQ(0x7cbe11406cULL, unwinder.frames()[11].pc);
+  EXPECT_EQ(0x7be4f07d00ULL, unwinder.frames()[11].sp);
+  EXPECT_EQ(0x7cbe0b5e18ULL, unwinder.frames()[12].pc);
+  EXPECT_EQ(0x7be4f07d20ULL, unwinder.frames()[12].sp);
+}
+
+TEST_F(UnwindOfflineTest, invalid_elf_offset_arm) {
+  ASSERT_NO_FATAL_FAILURE(Init("invalid_elf_offset_arm/", ARCH_ARM, false));
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(1U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ("  #00 pc 00aa7508  invalid.apk (offset 0x12e4000)\n", frame_info);
+  EXPECT_EQ(0xc898f508, unwinder.frames()[0].pc);
+  EXPECT_EQ(0xc2044218, unwinder.frames()[0].sp);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index 242cc6a..f76a101 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -32,17 +32,26 @@
 #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 "MemoryRemote.h"
 #include "TestUtils.h"
 
 namespace unwindstack {
 
+enum TestTypeEnum : uint8_t {
+  TEST_TYPE_LOCAL_UNWINDER = 0,
+  TEST_TYPE_LOCAL_UNWINDER_FROM_PID,
+  TEST_TYPE_LOCAL_WAIT_FOR_FINISH,
+  TEST_TYPE_REMOTE,
+  TEST_TYPE_REMOTE_WITH_INVALID_CALL,
+};
+
 static std::atomic_bool g_ready;
 static volatile bool g_ready_for_remote;
 static volatile bool g_signal_ready_for_remote;
@@ -71,7 +80,10 @@
 
 extern "C" void SignalInnerFunction() {
   g_signal_ready_for_remote = true;
-  while (!g_finish.load()) {
+  // Avoid any function calls because not every instruction will be
+  // unwindable.
+  // This method of looping is only used when testing a remote unwind.
+  while (true) {
   }
 }
 
@@ -87,10 +99,10 @@
   SignalOuterFunction();
 }
 
-static std::string ErrorMsg(const std::vector<const char*>& function_names, Unwinder& unwinder) {
+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';
+  for (size_t i = 0; i < unwinder->NumFrames(); i++) {
+    unwind += unwinder->FormatFrame(i) + '\n';
   }
 
   return std::string(
@@ -99,57 +111,78 @@
          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));
+static void VerifyUnwind(Unwinder* unwinder, std::vector<const char*> expected_function_names) {
+  unwinder->Unwind();
 
-  Unwinder unwinder(512, maps, regs, process_memory);
-  unwinder.Unwind();
-
-  std::string expected_function = expected_function_names.back();
-  expected_function_names.pop_back();
-  for (auto& frame : unwinder.frames()) {
-    if (frame.function_name == expected_function) {
+  for (auto& frame : unwinder->frames()) {
+    if (frame.function_name == expected_function_names.back()) {
+      expected_function_names.pop_back();
       if (expected_function_names.empty()) {
         break;
       }
-      expected_function = expected_function_names.back();
-      expected_function_names.pop_back();
     }
   }
 
   ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, unwinder);
 }
 
+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);
+  VerifyUnwind(&unwinder, expected_function_names);
+}
+
 // 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 {
+extern "C" void InnerFunction(TestTypeEnum test_type) {
+  if (test_type == TEST_TYPE_LOCAL_WAIT_FOR_FINISH) {
+    while (!g_finish.load()) {
+    }
+    return;
+  }
+  if (test_type == TEST_TYPE_REMOTE || test_type == TEST_TYPE_REMOTE_WITH_INVALID_CALL) {
     g_ready_for_remote = true;
     g_ready = true;
-    if (trigger_invalid_call) {
+    if (test_type == TEST_TYPE_REMOTE_WITH_INVALID_CALL) {
       void (*crash_func)() = nullptr;
       crash_func();
     }
-    while (!g_finish.load()) {
+    // Avoid any function calls because not every instruction will be
+    // unwindable.
+    // This method of looping is only used when testing a remote unwind.
+    while (true) {
     }
+    return;
   }
+
+  std::unique_ptr<Unwinder> unwinder;
+  std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
+  RegsGetLocal(regs.get());
+  std::unique_ptr<Maps> maps;
+
+  if (test_type == TEST_TYPE_LOCAL_UNWINDER) {
+    maps.reset(new LocalMaps());
+    ASSERT_TRUE(maps->Parse());
+    auto process_memory(Memory::CreateProcessMemory(getpid()));
+    unwinder.reset(new Unwinder(512, maps.get(), regs.get(), process_memory));
+  } else {
+    UnwinderFromPid* unwinder_from_pid = new UnwinderFromPid(512, getpid());
+    ASSERT_TRUE(unwinder_from_pid->Init(regs->Arch()));
+    unwinder_from_pid->SetRegs(regs.get());
+    unwinder.reset(unwinder_from_pid);
+  }
+  VerifyUnwind(unwinder.get(), kFunctionOrder);
 }
 
-extern "C" void MiddleFunction(bool local, bool trigger_invalid_call) {
-  InnerFunction(local, trigger_invalid_call);
+extern "C" void MiddleFunction(TestTypeEnum test_type) {
+  InnerFunction(test_type);
 }
 
-extern "C" void OuterFunction(bool local, bool trigger_invalid_call) {
-  MiddleFunction(local, trigger_invalid_call);
+extern "C" void OuterFunction(TestTypeEnum test_type) {
+  MiddleFunction(test_type);
 }
 
 class UnwindTest : public ::testing::Test {
@@ -158,7 +191,26 @@
 };
 
 TEST_F(UnwindTest, local) {
-  OuterFunction(true, false);
+  OuterFunction(TEST_TYPE_LOCAL_UNWINDER);
+}
+
+TEST_F(UnwindTest, local_use_from_pid) {
+  OuterFunction(TEST_TYPE_LOCAL_UNWINDER_FROM_PID);
+}
+
+static void LocalUnwind(void* data) {
+  TestTypeEnum* test_type = reinterpret_cast<TestTypeEnum*>(data);
+  OuterFunction(*test_type);
+}
+
+TEST_F(UnwindTest, local_check_for_leak) {
+  TestTypeEnum test_type = TEST_TYPE_LOCAL_UNWINDER;
+  TestCheckForLeaks(LocalUnwind, &test_type);
+}
+
+TEST_F(UnwindTest, local_use_from_pid_check_for_leak) {
+  TestTypeEnum test_type = TEST_TYPE_LOCAL_UNWINDER_FROM_PID;
+  TestCheckForLeaks(LocalUnwind, &test_type);
 }
 
 void WaitForRemote(pid_t pid, uint64_t addr, bool leave_attached, bool* completed) {
@@ -193,7 +245,7 @@
 TEST_F(UnwindTest, remote) {
   pid_t pid;
   if ((pid = fork()) == 0) {
-    OuterFunction(false, false);
+    OuterFunction(TEST_TYPE_REMOTE);
     exit(0);
   }
   ASSERT_NE(-1, pid);
@@ -214,11 +266,90 @@
       << "ptrace detach failed with unexpected error: " << strerror(errno);
 }
 
+TEST_F(UnwindTest, unwind_from_pid_remote) {
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    OuterFunction(TEST_TYPE_REMOTE);
+    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.";
+
+  std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
+  ASSERT_TRUE(regs.get() != nullptr);
+
+  UnwinderFromPid unwinder(512, pid);
+  ASSERT_TRUE(unwinder.Init(regs->Arch()));
+  unwinder.SetRegs(regs.get());
+
+  VerifyUnwind(&unwinder, kFunctionOrder);
+
+  // Verify that calling the same object works again.
+
+  ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
+      << "ptrace detach failed with unexpected error: " << strerror(errno);
+}
+
+static void RemoteCheckForLeaks(void (*unwind_func)(void*)) {
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    OuterFunction(TEST_TYPE_REMOTE);
+    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.";
+
+  TestCheckForLeaks(unwind_func, &pid);
+
+  ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
+      << "ptrace detach failed with unexpected error: " << strerror(errno);
+}
+
+static void RemoteUnwind(void* data) {
+  pid_t* pid = reinterpret_cast<pid_t*>(data);
+
+  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);
+}
+
+TEST_F(UnwindTest, remote_check_for_leaks) {
+  RemoteCheckForLeaks(RemoteUnwind);
+}
+
+static void RemoteUnwindFromPid(void* data) {
+  pid_t* pid = reinterpret_cast<pid_t*>(data);
+
+  std::unique_ptr<Regs> regs(Regs::RemoteGet(*pid));
+  ASSERT_TRUE(regs.get() != nullptr);
+
+  UnwinderFromPid unwinder(512, *pid);
+  ASSERT_TRUE(unwinder.Init(regs->Arch()));
+  unwinder.SetRegs(regs.get());
+
+  VerifyUnwind(&unwinder, kFunctionOrder);
+}
+
+TEST_F(UnwindTest, remote_unwind_for_pid_check_for_leaks) {
+  RemoteCheckForLeaks(RemoteUnwindFromPid);
+}
+
 TEST_F(UnwindTest, from_context) {
   std::atomic_int tid(0);
   std::thread thread([&]() {
     tid = syscall(__NR_gettid);
-    OuterFunction(false, false);
+    OuterFunction(TEST_TYPE_LOCAL_WAIT_FOR_FINISH);
   });
 
   struct sigaction act, oldact;
@@ -234,8 +365,7 @@
     usleep(1000);
   }
   ASSERT_NE(0, tid.load());
-  // Portable tgkill method.
-  ASSERT_EQ(0, syscall(__NR_tgkill, getpid(), tid.load(), SIGUSR1)) << "Error: " << strerror(errno);
+  ASSERT_EQ(0, tgkill(getpid(), tid.load(), SIGUSR1)) << "Error: " << strerror(errno);
 
   // Wait for context data.
   void* ucontext;
@@ -269,7 +399,7 @@
     act.sa_flags = SA_RESTART | SA_ONSTACK | sa_flags;
     ASSERT_EQ(0, sigaction(signal, &act, &oldact));
 
-    OuterFunction(false, signal == SIGSEGV);
+    OuterFunction(signal != SIGSEGV ? TEST_TYPE_REMOTE : TEST_TYPE_REMOTE_WITH_INVALID_CALL);
     exit(0);
   }
   ASSERT_NE(-1, pid);
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index b38fb5f..ef1950c 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -42,81 +42,99 @@
 
 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");
+  static void AddMapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+                         const char* name, Elf* elf = nullptr) {
+    std::string str_name(name);
+    maps_->Add(start, end, offset, flags, name, static_cast<uint64_t>(-1));
+    if (elf != nullptr) {
+      const auto& map_info = *--maps_->end();
+      map_info->elf.reset(elf);
+    }
+  }
+
+  static void SetUpTestSuite() {
+    maps_.reset(new Maps);
+
     ElfFake* elf = new ElfFake(new MemoryFake);
-    info->elf.reset(elf);
-    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
-    maps_.FakeAddMapInfo(info);
+    ElfInterfaceFake* interface_fake = new ElfInterfaceFake(nullptr);
+    interface_fake->FakeSetBuildID("FAKE");
+    elf->FakeSetInterface(interface_fake);
+    AddMapInfo(0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so", elf);
 
-    info = new MapInfo(0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
-    maps_.FakeAddMapInfo(info);
+    AddMapInfo(0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
 
-    info = new MapInfo(0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
-                       "/dev/fake_device");
-    maps_.FakeAddMapInfo(info);
+    AddMapInfo(0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
+               "/dev/fake_device");
 
-    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);
+    AddMapInfo(0x20000, 0x22000, 0, PROT_READ | PROT_WRITE, "/system/fake/libunwind.so", elf);
 
-    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);
+    AddMapInfo(0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so", elf);
 
-    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);
+    AddMapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so", elf);
 
-    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);
+    ElfInterfaceFake* interface = new ElfInterfaceFake(nullptr);
+    interface->FakeSetSoname("lib_fake.so");
+    elf->FakeSetInterface(interface);
+    AddMapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk", elf);
+    MapInfo* map_info = maps_->Find(0x43000);
+    ASSERT_TRUE(map_info != nullptr);
+    map_info->elf_start_offset = 0x1d000;
 
-    info = new MapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
-    maps_.FakeAddMapInfo(info);
+    AddMapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
 
-    info = new MapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
+    AddMapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
+    const auto& info = *--maps_->end();
     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);
+    AddMapInfo(0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake_load_bias.so",
+               elf);
 
-    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);
+    AddMapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake_offset.oat",
+               elf);
+    const auto& info2 = *--maps_->end();
+    info2->elf_offset = 0x8000;
+
+    elf = new ElfFake(new MemoryFake);
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    AddMapInfo(0xc0000, 0xc1000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/unreadable.so", elf);
+    const auto& info3 = *--maps_->end();
+    info3->memory_backed_elf = true;
+
+    elf = new ElfFake(new MemoryFake);
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    AddMapInfo(0xc1000, 0xc2000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "[vdso]", elf);
+    const auto& info4 = *--maps_->end();
+    info4->memory_backed_elf = true;
+
+    elf = new ElfFake(new MemoryFake);
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    AddMapInfo(0xc2000, 0xc3000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "", elf);
+    const auto& info5 = *--maps_->end();
+    info5->memory_backed_elf = true;
+
+    elf = new ElfFake(new MemoryFake);
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    AddMapInfo(0xc3000, 0xc4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/memfd:/jit-cache", elf);
+    const auto& info6 = *--maps_->end();
+    info6->memory_backed_elf = true;
+
+    AddMapInfo(0xd0000, 0xd1000, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.apk");
+    const auto& info7 = *--maps_->end();
+    info7->load_bias = 0;
 
     process_memory_.reset(new MemoryFake);
   }
@@ -127,12 +145,12 @@
     regs_.FakeSetReturnAddressValid(false);
   }
 
-  static MapsFake maps_;
+  static std::unique_ptr<Maps> maps_;
   static RegsFake regs_;
   static std::shared_ptr<Memory> process_memory_;
 };
 
-MapsFake UnwinderTest::maps_;
+std::unique_ptr<Maps> UnwinderTest::maps_;
 RegsFake UnwinderTest::regs_(5);
 std::shared_ptr<Memory> UnwinderTest::process_memory_(nullptr);
 
@@ -147,9 +165,10 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(3U, unwinder.NumFrames());
 
@@ -161,7 +180,8 @@
   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(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -175,7 +195,8 @@
   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(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -189,7 +210,8 @@
   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(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -207,10 +229,11 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.SetResolveNames(false);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(3U, unwinder.NumFrames());
 
@@ -221,8 +244,9 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ("", frame->map_name);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -235,8 +259,9 @@
   EXPECT_EQ(0x10010U, frame->sp);
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ("", frame->map_name);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -249,8 +274,9 @@
   EXPECT_EQ(0x10020U, frame->sp);
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ("", frame->map_name);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -264,9 +290,10 @@
   regs_.set_sp(0x10000);
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 
@@ -278,7 +305,8 @@
   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(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0xa5000U, frame->map_start);
   EXPECT_EQ(0xa6000U, frame->map_end);
   EXPECT_EQ(0x5000U, frame->map_load_bias);
@@ -292,9 +320,10 @@
   regs_.set_sp(0x10000);
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 
@@ -306,7 +335,8 @@
   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(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0xa7000U, frame->map_start);
   EXPECT_EQ(0xa8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -320,9 +350,41 @@
   regs_.set_sp(0x10000);
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+  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!lib_fake.so", frame->map_name);
+  EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
+  EXPECT_EQ(0x1d000U, frame->map_exact_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);
+}
+
+TEST_F(UnwinderTest, disable_embedded_soname) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+  regs_.set_pc(0x43000);
+  regs_.set_sp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+  unwinder.SetEmbeddedSoname(false);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 
@@ -334,7 +396,8 @@
   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(0x1d000U, frame->map_elf_start_offset);
+  EXPECT_EQ(0x1d000U, frame->map_exact_offset);
   EXPECT_EQ(0x43000U, frame->map_start);
   EXPECT_EQ(0x44000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -355,9 +418,10 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
   ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 
@@ -369,7 +433,8 @@
   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(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -386,9 +451,10 @@
   regs_.set_pc(0x1000);
   regs_.set_sp(0x10000);
 
-  Unwinder unwinder(20, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(20, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(20U, unwinder.NumFrames());
 
@@ -401,7 +467,8 @@
     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(0U, frame->map_elf_start_offset) << "Failed at frame " << i;
+    EXPECT_EQ(0U, frame->map_exact_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;
@@ -426,10 +493,11 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10070, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   std::vector<std::string> skip_libs{"libunwind.so", "libanother.so"};
   unwinder.Unwind(&skip_libs);
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(3U, unwinder.NumFrames());
 
@@ -441,7 +509,8 @@
   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(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -455,7 +524,8 @@
   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(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x20000U, frame->map_start);
   EXPECT_EQ(0x22000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -469,7 +539,7 @@
   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(0U, frame->map_exact_offset);
   EXPECT_EQ(0x23000U, frame->map_start);
   EXPECT_EQ(0x24000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -486,9 +556,10 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x50020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(2U, unwinder.NumFrames());
 
@@ -500,7 +571,7 @@
   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(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -514,7 +585,7 @@
   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(0U, frame->map_exact_offset);
   EXPECT_EQ(0x20000U, frame->map_start);
   EXPECT_EQ(0x22000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -533,9 +604,10 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 }
@@ -552,9 +624,10 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 }
@@ -566,9 +639,10 @@
   regs_.set_pc(0x41000);
   regs_.set_sp(0x13000);
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 
@@ -580,7 +654,8 @@
   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_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0U, frame->map_start);
   EXPECT_EQ(0U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -601,9 +676,10 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(3U, unwinder.NumFrames());
 
@@ -615,7 +691,8 @@
   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_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0U, frame->map_start);
   EXPECT_EQ(0U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -629,7 +706,8 @@
   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(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -643,7 +721,8 @@
   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(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x23000U, frame->map_start);
   EXPECT_EQ(0x24000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -657,16 +736,68 @@
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
 
   // Fake as if code called a nullptr function.
+  regs_.set_pc(0x20000);
+  regs_.set_sp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0x10010, false));
+  regs_.FakeSetReturnAddress(0x12);
+  regs_.FakeSetReturnAddressValid(true);
+
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+  ASSERT_EQ(2U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x20000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_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()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0U, 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_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_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 and left if there are only
+// two frames and the pc is in the middle nowhere.
+TEST_F(UnwinderTest, speculative_frame_not_removed_pc_bad) {
+  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 unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
-  ASSERT_EQ(1U, unwinder.NumFrames());
+  ASSERT_EQ(2U, unwinder.NumFrames());
 
   auto* frame = &unwinder.frames()[0];
   EXPECT_EQ(0U, frame->num);
@@ -676,11 +807,45 @@
   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_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_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_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_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 that a speculative frame does not cause a crash when it wasn't
+// really added due to a filter.
+TEST_F(UnwinderTest, speculative_frame_check_with_no_frames) {
+  regs_.set_pc(0x23000);
+  regs_.set_sp(0x10000);
+  regs_.FakeSetReturnAddress(0x23100);
+  regs_.FakeSetReturnAddressValid(true);
+
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+
+  std::vector<std::string> skip_names{"libanother.so"};
+  unwinder.Unwind(&skip_names);
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+  ASSERT_EQ(0U, unwinder.NumFrames());
 }
 
 // Verify that an unwind stops when a frame is in given suffix.
@@ -697,14 +862,15 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x53502, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   std::vector<std::string> suffixes{"oat"};
   unwinder.Unwind(nullptr, &suffixes);
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(2U, unwinder.NumFrames());
   // Make sure the elf was not initialized.
-  MapInfo* map_info = maps_.Find(0x53000);
+  MapInfo* map_info = maps_->Find(0x53000);
   ASSERT_TRUE(map_info != nullptr);
   EXPECT_TRUE(map_info->elf == nullptr);
 
@@ -716,7 +882,8 @@
   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(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -729,8 +896,9 @@
   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("/fake/fake.apk!lib_fake.so", frame->map_name);
+  EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
+  EXPECT_EQ(0x1d000U, frame->map_exact_offset);
   EXPECT_EQ(0x43000U, frame->map_start);
   EXPECT_EQ(0x44000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -754,9 +922,10 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_REPEATED_FRAME, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(3U, unwinder.NumFrames());
 
@@ -768,7 +937,8 @@
   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(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -782,7 +952,8 @@
   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(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x33000U, frame->map_start);
   EXPECT_EQ(0x34000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -796,7 +967,8 @@
   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(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x33000U, frame->map_start);
   EXPECT_EQ(0x34000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -809,9 +981,10 @@
   regs_.set_sp(0x10000);
   regs_.FakeSetDexPc(0xa3400);
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(2U, unwinder.NumFrames());
 
@@ -823,7 +996,8 @@
   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(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0xa3000U, frame->map_start);
   EXPECT_EQ(0xa4000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -837,7 +1011,52 @@
   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(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_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_in_map_non_zero_offset) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  regs_.set_pc(0x1000);
+  regs_.set_sp(0x10000);
+  regs_.FakeSetDexPc(0xd0400);
+
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+  ASSERT_EQ(2U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x400U, frame->rel_pc);
+  EXPECT_EQ(0xd0400U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/fake/fake.apk", frame->map_name);
+  EXPECT_EQ(0x1000U, frame->map_elf_start_offset);
+  EXPECT_EQ(0x1000U, frame->map_exact_offset);
+  EXPECT_EQ(0xd0000U, frame->map_start);
+  EXPECT_EQ(0xd1000U, 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_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -850,9 +1069,10 @@
   regs_.set_sp(0x10000);
   regs_.FakeSetDexPc(0x50000);
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(2U, unwinder.NumFrames());
 
@@ -864,7 +1084,8 @@
   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_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0U, frame->map_start);
   EXPECT_EQ(0U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -878,7 +1099,8 @@
   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(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -894,9 +1116,10 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x33402, 0x10010, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(3U, unwinder.NumFrames());
 
@@ -908,7 +1131,8 @@
   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(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0xa3000U, frame->map_start);
   EXPECT_EQ(0xa4000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -922,7 +1146,8 @@
   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(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -936,15 +1161,173 @@
   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(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_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_max_frames) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  regs_.set_pc(0x1000);
+  regs_.set_sp(0x10000);
+  regs_.FakeSetDexPc(0xa3400);
+
+  Unwinder unwinder(1, maps_.get(), &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+  ASSERT_EQ(1U, 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_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_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);
+}
+
+TEST_F(UnwinderTest, elf_from_memory_not_file) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+  regs_.set_pc(0xc0050);
+  regs_.set_sp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_TRUE(unwinder.elf_from_memory_not_file());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x50U, frame->rel_pc);
+  EXPECT_EQ(0xc0050U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/fake/unreadable.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
+  EXPECT_EQ(0xc0000U, frame->map_start);
+  EXPECT_EQ(0xc1000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, elf_from_memory_but_no_valid_file_with_bracket) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+  regs_.set_pc(0xc1050);
+  regs_.set_sp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x50U, frame->rel_pc);
+  EXPECT_EQ(0xc1050U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("[vdso]", frame->map_name);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
+  EXPECT_EQ(0xc1000U, frame->map_start);
+  EXPECT_EQ(0xc2000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, elf_from_memory_but_empty_filename) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+  regs_.set_pc(0xc2050);
+  regs_.set_sp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x50U, frame->rel_pc);
+  EXPECT_EQ(0xc2050U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("", frame->map_name);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
+  EXPECT_EQ(0xc2000U, frame->map_start);
+  EXPECT_EQ(0xc3000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, elf_from_memory_but_from_memfd) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+  regs_.set_pc(0xc3050);
+  regs_.set_sp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x50U, frame->rel_pc);
+  EXPECT_EQ(0xc3050U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/memfd:/jit-cache", frame->map_name);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
+  EXPECT_EQ(0xc3000U, frame->map_start);
+  EXPECT_EQ(0xc4000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
 // Verify format frame code.
-TEST_F(UnwinderTest, format_frame_static) {
+TEST_F(UnwinderTest, format_frame) {
+  RegsFake regs_arm(10);
+  regs_arm.FakeSetArch(ARCH_ARM);
+  Unwinder unwinder32(10, maps_.get(), &regs_arm, process_memory_);
+
+  RegsFake regs_arm64(10);
+  regs_arm64.FakeSetArch(ARCH_ARM64);
+  Unwinder unwinder64(10, maps_.get(), &regs_arm64, process_memory_);
+
   FrameData frame;
   frame.num = 1;
   frame.rel_pc = 0x1000;
@@ -953,39 +1336,67 @@
   frame.function_name = "function";
   frame.function_offset = 100;
   frame.map_name = "/fake/libfake.so";
-  frame.map_offset = 0x2000;
+  frame.map_elf_start_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));
+  EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so (offset 0x2000) (function+100)",
+            unwinder64.FormatFrame(frame));
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (offset 0x2000) (function+100)",
+            unwinder32.FormatFrame(frame));
 
-  frame.map_offset = 0;
+  frame.map_elf_start_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));
+            unwinder64.FormatFrame(frame));
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (function+100)", unwinder32.FormatFrame(frame));
 
   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));
+            unwinder64.FormatFrame(frame));
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (function)", unwinder32.FormatFrame(frame));
+
+  // Verify the function name is demangled.
+  frame.function_name = "_ZN4funcEv";
+  EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so (func())", unwinder64.FormatFrame(frame));
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (func())", unwinder32.FormatFrame(frame));
 
   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));
+  EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so", unwinder64.FormatFrame(frame));
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so", unwinder32.FormatFrame(frame));
 
   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));
+  EXPECT_EQ("  #01 pc 0000000000001000  <anonymous:3000>", unwinder64.FormatFrame(frame));
+  EXPECT_EQ("  #01 pc 00001000  <anonymous:3000>", unwinder32.FormatFrame(frame));
 
   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));
+  EXPECT_EQ("  #01 pc 0000000000001000  <unknown>", unwinder64.FormatFrame(frame));
+  EXPECT_EQ("  #01 pc 00001000  <unknown>", unwinder32.FormatFrame(frame));
+}
+
+TEST_F(UnwinderTest, format_frame_build_id) {
+  RegsFake regs(10);
+  regs.FakeSetArch(ARCH_ARM);
+  Unwinder unwinder(10, maps_.get(), &regs, process_memory_);
+
+  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_elf_start_offset = 0;
+  frame.map_start = 0x3000;
+  frame.map_end = 0x6000;
+  frame.map_flags = PROT_READ;
+
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (function+100)", unwinder.FormatFrame(frame));
+  unwinder.SetDisplayBuildID(true);
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (function+100) (BuildId: 46414b45)",
+            unwinder.FormatFrame(frame));
 }
 
 static std::string ArchToString(ArchEnum arch) {
@@ -1003,7 +1414,7 @@
 }
 
 // Verify format frame code.
-TEST_F(UnwinderTest, format_frame) {
+TEST_F(UnwinderTest, format_frame_by_arch) {
   std::vector<Regs*> reg_list;
   RegsArm* arm = new RegsArm;
   arm->set_pc(0x2300);
@@ -1038,7 +1449,7 @@
   for (auto regs : reg_list) {
     ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 10));
 
-    Unwinder unwinder(64, &maps_, regs, process_memory_);
+    Unwinder unwinder(64, maps_.get(), regs, process_memory_);
     unwinder.Unwind();
 
     ASSERT_EQ(1U, unwinder.NumFrames());
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
index 55aaaf6..5657373 100644
--- a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt
@@ -1,3 +1,4 @@
 d0250000-d2600000 r-xp 0 00:00 0 <anonymous:d0250000>
 e466e000-e4ae8000 r-xp 0 00:00 0 libart.so
+e4ae8000-e4ae9000 rw-p 1000 00:00 0 libart.so
 e7d91000-e7e31000 r-xp 0 00:00 0 libc.so
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/invalid_elf_offset_arm/maps.txt b/libunwindstack/tests/files/offline/invalid_elf_offset_arm/maps.txt
new file mode 100644
index 0000000..022404c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/invalid_elf_offset_arm/maps.txt
@@ -0,0 +1 @@
+c7ee8000-c8c52fff r-xp  12e4000    00:00 0  invalid.apk
diff --git a/libunwindstack/tests/files/offline/invalid_elf_offset_arm/regs.txt b/libunwindstack/tests/files/offline/invalid_elf_offset_arm/regs.txt
new file mode 100644
index 0000000..b7f10ef
--- /dev/null
+++ b/libunwindstack/tests/files/offline/invalid_elf_offset_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: c0434c00
+r1: 2a4c9fbc
+r2: 00000000
+r3: c83ef1f9
+r4: 00000004
+r5: c2044904
+r6: 00000000
+r7: c20443b8
+r8: 000b33ff
+r9: c20444b0
+r10: cac90740
+r11: 00000000
+ip: ed891ca4
+sp: c2044218
+lr: ed807265
+pc: c898f508
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
index f25c781..4043122 100644
--- a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
@@ -1,8 +1,11 @@
 ab0d3000-ab0d8000 r-xp 0 00:00 0   dalvikvm32
 dfe4e000-dfe7b000 r-xp 0 00:00 0   libarttestd.so
+e0445000-e0447000 r--p 0 00:00 0   137-cfi.odex
 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
+e648e000-e690f000 r-xp 0 00:00 0  libart.so
+e690f000-e6910000 rw-p 1000 00:00 0  libart.so
 ed306000-ed801000 r-xp 0 00:00 0   libartd.so
+ed801000-ed802000 rw-p 1000 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_x86/maps.txt b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
index db4f9f7..f255a44 100644
--- a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
@@ -1,6 +1,8 @@
 56573000-56577000 r-xp 0 00:00 0   dalvikvm32
 eb833000-eb8cc000 r-xp 0 00:00 0   libarttestd.so
+ec604000-ec606000 r--p 0 00:00 0   137-cfi.odex
 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
+f732b000-f732c000 rw-p 1000 00:00 0   libartd.so
 f734b000-f74fc000 r-xp 0 00:00 0   libc.so
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/maps.txt b/libunwindstack/tests/files/offline/offset_arm/maps.txt
index 6224464..768dd9f 100644
--- a/libunwindstack/tests/files/offline/offset_arm/maps.txt
+++ b/libunwindstack/tests/files/offline/offset_arm/maps.txt
@@ -1,2 +1,4 @@
+2b2a000-2b6c000 r--p 0 00:00 0   libunwindstack_test
 2b6c000-2e92000 r-xp 42000 00:00 0   libunwindstack_test
+f4110000-f4135000 r--p 0 00:00 0   libc.so
 f4135000-f41a9000 r-xp 25000 00:00 0   libc.so
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk
new file mode 100644
index 0000000..0277359
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so
new file mode 100644
index 0000000..20008fd
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64 b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64
new file mode 100644
index 0000000..b90933b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/maps.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/maps.txt
new file mode 100644
index 0000000..c4fc067
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/maps.txt
@@ -0,0 +1,7 @@
+7dabc03000-7dabc3f000 r--p 4000 00:00 0   ANGLEPrebuilt.apk
+7dabc3f000-7dabcf0000 r-xp 40000 00:00 0   ANGLEPrebuilt.apk
+7e7ee48000-7e7ee88000 r--p 0 00:00 0   libc.so
+7e7ee88000-7e7ef32000 r-xp 40000 00:00 0   libc.so
+7e82b01000-7e82b03000 r-xp 0 00:00 0   vdso.so
+7e82b03000-7e82b3c000 r--p 0 00:00 0   linker64
+7e82b3c000-7e82c77000 r-xp 39000 00:00 0   linker64
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/regs.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/regs.txt
new file mode 100644
index 0000000..1e2ea32
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/regs.txt
@@ -0,0 +1,33 @@
+x0: 7df8ca3c24
+x1: 0
+x2: ffffffff
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 7f7f7f7f7f7f7f7f
+x8: 62
+x9: 20dd5829922a93ac
+x10: 7e82b57420
+x11: 4100
+x12: 7df8ca3b70
+x13: 7df8ca3b98
+x14: 73d015e5
+x15: 39a36122467299
+x16: 76ac
+x17: 0
+x18: 7df8cfc000
+x19: 7dabf3e7a0
+x20: 7df8ca3da0
+x21: 59616d61
+x22: 1
+x23: 7df8ca3c24
+x24: 1894
+x25: 62
+x26: 2
+x27: 0
+x28: 7dabf3e790
+x29: 7df8ca3d90
+sp: 7df8ca3bf0
+lr: 7e82b57270
+pc: 7e82c4fcbc
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack0.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack0.data
new file mode 100644
index 0000000..ec07e15
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack1.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack1.data
new file mode 100644
index 0000000..825bb1a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so
new file mode 100644
index 0000000..205ebd4
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/lib_mem.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/lib_mem.data
new file mode 100644
index 0000000..f39d127
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/lib_mem.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so
new file mode 100644
index 0000000..20008fd
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64 b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64
new file mode 100644
index 0000000..b90933b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/maps.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/maps.txt
new file mode 100644
index 0000000..386d57a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/maps.txt
@@ -0,0 +1,7 @@
+7dabc03000-7dabc3f000 r--p 21d5000 00:00 0   ANGLEPrebuilt.apk
+7dabc3f000-7dabcf0000 r-xp 2211000 00:00 0   ANGLEPrebuilt.apk
+7e7ee48000-7e7ee88000 r--p 0 00:00 0   libc.so
+7e7ee88000-7e7ef32000 r-xp 40000 00:00 0   libc.so
+7e82b01000-7e82b03000 r-xp 0 00:00 0   vdso.so
+7e82b03000-7e82b3c000 r--p 0 00:00 0   linker64
+7e82b3c000-7e82c77000 r-xp 39000 00:00 0   linker64
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/regs.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/regs.txt
new file mode 100644
index 0000000..1e2ea32
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/regs.txt
@@ -0,0 +1,33 @@
+x0: 7df8ca3c24
+x1: 0
+x2: ffffffff
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 7f7f7f7f7f7f7f7f
+x8: 62
+x9: 20dd5829922a93ac
+x10: 7e82b57420
+x11: 4100
+x12: 7df8ca3b70
+x13: 7df8ca3b98
+x14: 73d015e5
+x15: 39a36122467299
+x16: 76ac
+x17: 0
+x18: 7df8cfc000
+x19: 7dabf3e7a0
+x20: 7df8ca3da0
+x21: 59616d61
+x22: 1
+x23: 7df8ca3c24
+x24: 1894
+x25: 62
+x26: 2
+x27: 0
+x28: 7dabf3e790
+x29: 7df8ca3d90
+sp: 7df8ca3bf0
+lr: 7e82b57270
+pc: 7e82c4fcbc
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack0.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack0.data
new file mode 100644
index 0000000..ec07e15
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack1.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack1.data
new file mode 100644
index 0000000..825bb1a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so
new file mode 100644
index 0000000..205ebd4
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so
new file mode 100644
index 0000000..cac1dd9
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt
new file mode 100644
index 0000000..2c5ca62
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt
@@ -0,0 +1,3 @@
+7be5e48000-7be6b2b000 r-xp 5000 00:00 0   test.apk
+7cbe030000-7cbe070000 r--p 0 00:00 0   libc.so
+7cbe070000-7cbe11a000 r-xp 40000 00:00 0   libc.so
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt
new file mode 100644
index 0000000..090aeda
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt
@@ -0,0 +1,33 @@
+x0: 7c326f6568
+x1: 80
+x2: 0
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 7f7f7f7f7f7f7f7f
+x8: 62
+x9: 1
+x10: 1
+x11: 0
+x12: ffffffffc4653600
+x13: 17645696f
+x14: 2742ed97ca77a3
+x15: 3ab49084
+x16: 7be6b6bdb8
+x17: 7cbe0b14a0
+x18: 7c2b02a000
+x19: 0
+x20: 7c326f6568
+x21: 7be69c827c
+x22: 7be69c8272
+x23: 1
+x24: 7be74f7100
+x25: 881
+x26: 7be4f07a00
+x27: c479c000
+x28: 7be4f07998
+x29: 7be4f079b4
+sp: 7be4f077d0
+lr: 7be6715f60
+pc: 7cbe0b14bc
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data
new file mode 100644
index 0000000..27d5bf3
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk
new file mode 100644
index 0000000..70a9c71
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk
Binary files differ
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
index 22ca7bf..1812e50 100644
--- a/libunwindstack/tools/unwind.cpp
+++ b/libunwindstack/tools/unwind.cpp
@@ -35,7 +35,12 @@
 #include <unwindstack/Unwinder.h>
 
 static bool Attach(pid_t pid) {
-  if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+  if (ptrace(PTRACE_SEIZE, pid, 0, 0) == -1) {
+    return false;
+  }
+
+  if (ptrace(PTRACE_INTERRUPT, pid, 0, 0) == -1) {
+    ptrace(PTRACE_DETACH, pid, 0, 0);
     return false;
   }
 
@@ -52,12 +57,6 @@
 }
 
 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");
@@ -90,15 +89,13 @@
   }
   printf("\n");
 
-  auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
-  unwindstack::Unwinder unwinder(128, &remote_maps, regs, process_memory);
+  unwindstack::UnwinderFromPid unwinder(1024, pid);
+  if (!unwinder.Init(regs->Arch())) {
+    printf("Failed to init unwinder object.\n");
+    return;
+  }
 
-  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.SetRegs(regs);
   unwinder.Unwind();
 
   // Print the frames.
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 589731d..4f67d67 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -22,6 +22,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/mman.h>
 #include <sys/ptrace.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -46,11 +47,17 @@
   uint64_t start;
   uint64_t end;
   uint64_t offset;
+  uint64_t flags;
   std::string name;
 };
 
 static bool Attach(pid_t pid) {
-  if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+  if (ptrace(PTRACE_SEIZE, pid, 0, 0) == -1) {
+    return false;
+  }
+
+  if (ptrace(PTRACE_INTERRUPT, pid, 0, 0) == -1) {
+    ptrace(PTRACE_DETACH, pid, 0, 0);
     return false;
   }
 
@@ -69,7 +76,7 @@
 bool SaveRegs(unwindstack::Regs* regs) {
   std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("regs.txt", "w+"), &fclose);
   if (fp == nullptr) {
-    printf("Failed to create file regs.txt.\n");
+    perror("Failed to create file regs.txt");
     return false;
   }
   regs->IterateRegisters([&fp](const char* name, uint64_t value) {
@@ -102,13 +109,14 @@
 
     std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file_name.c_str(), "w+"), &fclose);
     if (fp == nullptr) {
-      printf("Failed to create stack.data.\n");
+      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)) {
-      perror("Failed to write all data.");
+      printf("Failed to write sp_start data: sizeof(sp_start) %zu, written %zu\n", sizeof(sp_start),
+             bytes);
       return false;
     }
 
@@ -141,7 +149,7 @@
 
   std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
   if (output == nullptr) {
-    printf("Cannot create %s\n", cur_name.c_str());
+    perror((std::string("Cannot create ") + cur_name).c_str());
     return false;
   }
 
@@ -157,16 +165,22 @@
   return true;
 }
 
-bool CopyElfFromFile(map_info_t* info) {
+bool CopyElfFromFile(map_info_t* info, bool* file_copied) {
+  std::string cur_name = basename(info->name.c_str());
+  if (*file_copied) {
+    info->name = cur_name;
+    return true;
+  }
+
   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) {
-    printf("Cannot create file %s\n", cur_name.c_str());
+    perror((std::string("Cannot create file " + cur_name)).c_str());
     return false;
   }
   std::vector<uint8_t> buffer(10000);
@@ -186,6 +200,39 @@
   return true;
 }
 
+map_info_t* FillInAndGetMapInfo(std::unordered_map<uint64_t, map_info_t>& maps_by_start,
+                                unwindstack::MapInfo* map_info) {
+  auto info = &maps_by_start[map_info->start];
+  info->start = map_info->start;
+  info->end = map_info->end;
+  info->offset = map_info->offset;
+  info->name = map_info->name;
+  info->flags = map_info->flags;
+
+  return info;
+}
+
+void SaveMapInformation(std::shared_ptr<unwindstack::Memory>& process_memory, map_info_t* info,
+                        bool* file_copied) {
+  if (CopyElfFromFile(info, file_copied)) {
+    return;
+  }
+  *file_copied = false;
+
+  // 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)) {
+    return;
+  }
+
+  printf("Cannot save memory or file for map ");
+  if (!info->name.empty()) {
+    printf("%s\n", info->name.c_str());
+  } else {
+    printf("anonymous:%" PRIx64 "\n", info->start);
+  }
+}
+
 int SaveData(pid_t pid) {
   unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(pid);
   if (regs == nullptr) {
@@ -193,12 +240,6 @@
     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;
@@ -206,46 +247,48 @@
 
   // Do an unwind so we know how much of the stack to save, and what
   // elf files are involved.
+  unwindstack::UnwinderFromPid unwinder(1024, pid);
+  if (!unwinder.Init(regs->Arch())) {
+    printf("Unable to init unwinder object.\n");
+    return 1;
+  }
+  unwinder.SetRegs(regs);
   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;
+  unwindstack::Maps* maps = unwinder.GetMaps();
   uint64_t sp_map_start = 0;
-  unwindstack::MapInfo* map_info = maps.Find(sp);
+  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);
+  for (const 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);
-          }
-        }
+      map_info = maps->Find(frame.map_start);
+
+      auto info = FillInAndGetMapInfo(maps_by_start, map_info);
+      bool file_copied = false;
+      SaveMapInformation(unwinder.GetProcessMemory(), info, &file_copied);
+
+      // If you are using a a linker that creates two maps (one read-only, one
+      // read-executable), it's necessary to capture the previous map
+      // information if needed.
+      unwindstack::MapInfo* prev_map = map_info->prev_map;
+      if (prev_map != nullptr && map_info->offset != 0 && prev_map->offset == 0 &&
+          prev_map->flags == PROT_READ && map_info->name == prev_map->name &&
+          maps_by_start.count(prev_map->start) == 0) {
+        info = FillInAndGetMapInfo(maps_by_start, prev_map);
+        SaveMapInformation(unwinder.GetProcessMemory(), info, &file_copied);
       }
     }
   }
@@ -265,13 +308,23 @@
 
   std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("maps.txt", "w+"), &fclose);
   if (fp == nullptr) {
-    printf("Failed to create maps.txt.\n");
+    perror("Failed to create maps.txt");
     return false;
   }
 
   for (auto& element : sorted_maps) {
+    char perms[5] = {"---p"};
     map_info_t& map = element.second;
-    fprintf(fp.get(), "%" PRIx64 "-%" PRIx64 " r-xp %" PRIx64 " 00:00 0", map.start, map.end,
+    if (map.flags & PROT_READ) {
+      perms[0] = 'r';
+    }
+    if (map.flags & PROT_WRITE) {
+      perms[1] = 'w';
+    }
+    if (map.flags & PROT_EXEC) {
+      perms[2] = 'x';
+    }
+    fprintf(fp.get(), "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " 00:00 0", map.start, map.end, perms,
             map.offset);
     if (!map.name.empty()) {
       fprintf(fp.get(), "   %s", map.name.c_str());
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 5a8edfd..7a6d8ba 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -31,29 +31,29 @@
 #include <unwindstack/Elf.h>
 #include <unwindstack/ElfInterface.h>
 #include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
 
 #include "ArmExidx.h"
 #include "ElfInterfaceArm.h"
 
 namespace unwindstack {
 
-void DumpArm(ElfInterfaceArm* interface) {
+void DumpArm(Elf* elf, ElfInterfaceArm* interface) {
   if (interface == nullptr) {
     printf("No ARM Unwind Information.\n\n");
     return;
   }
 
   printf("ARM Unwind Information:\n");
+  uint64_t load_bias = elf->GetLoadBias();
   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) {
+           entry.second.offset + entry.second.table_size + load_bias);
+    for (auto pc : *interface) {
       std::string name;
-      printf("  PC 0x%" PRIx64, addr + load_bias);
+      printf("  PC 0x%" PRIx64, pc + load_bias);
       uint64_t func_offset;
-      uint64_t pc = addr + load_bias;
-      if (interface->GetFunctionName(pc, load_bias, &name, &func_offset) && !name.empty()) {
+      if (elf->GetFunctionName(pc + load_bias, &name, &func_offset) && !name.empty()) {
         printf(" <%s>", name.c_str());
       }
       printf("\n");
@@ -63,7 +63,7 @@
         continue;
       }
       ArmExidx arm(nullptr, interface->memory(), nullptr);
-      arm.set_log(true);
+      arm.set_log(ARM_LOG_FULL);
       arm.set_log_skip_execution(true);
       arm.set_log_indent(2);
       if (!arm.ExtractEntryData(entry)) {
@@ -82,21 +82,21 @@
   printf("\n");
 }
 
-void DumpDwarfSection(ElfInterface* interface, DwarfSection* section, uint64_t load_bias) {
+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, fde->pc_start + load_bias);
+    printf("\n  PC 0x%" PRIx64 "-0x%" PRIx64, fde->pc_start, fde->pc_end);
     std::string name;
     uint64_t func_offset;
-    if (interface->GetFunctionName(fde->pc_start, load_bias, &name, &func_offset) && !name.empty()) {
+    if (elf->GetFunctionName(fde->pc_start, &name, &func_offset) && !name.empty()) {
       printf(" <%s>", name.c_str());
     }
     printf("\n");
-    if (!section->Log(2, UINT64_MAX, load_bias, fde)) {
+    if (!section->Log(2, UINT64_MAX, fde)) {
       printf("Failed to process cfa information for entry at 0x%" PRIx64 "\n", fde->pc_start);
     }
   }
@@ -106,33 +106,35 @@
   // 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()) {
+  Elf elf(Memory::CreateFileMemory(file, offset).release());
+  if (!elf.Init() || !elf.valid()) {
     printf("%s is not a valid elf file.\n", file);
     return 1;
   }
 
-  std::string soname;
-  if (elf.GetSoname(&soname)) {
+  std::string soname(elf.GetSoname());
+  if (!soname.empty()) {
     printf("Soname: %s\n", soname.c_str());
   }
 
+  std::string build_id = elf.GetBuildID();
+  if (!build_id.empty()) {
+    printf("Build ID: ");
+    for (size_t i = 0; i < build_id.size(); ++i) {
+      printf("%02hhx", build_id[i]);
+    }
+    printf("\n");
+  }
+
   ElfInterface* interface = elf.interface();
   if (elf.machine_type() == EM_ARM) {
-    DumpArm(reinterpret_cast<ElfInterfaceArm*>(interface));
+    DumpArm(&elf, reinterpret_cast<ElfInterfaceArm*>(interface));
     printf("\n");
   }
 
   if (interface->eh_frame() != nullptr) {
     printf("eh_frame information:\n");
-    DumpDwarfSection(interface, interface->eh_frame(), elf.GetLoadBias());
+    DumpDwarfSection(&elf, interface->eh_frame(), elf.GetLoadBias());
     printf("\n");
   } else {
     printf("\nno eh_frame information\n");
@@ -140,7 +142,7 @@
 
   if (interface->debug_frame() != nullptr) {
     printf("\ndebug_frame information:\n");
-    DumpDwarfSection(interface, interface->debug_frame(), elf.GetLoadBias());
+    DumpDwarfSection(&elf, interface->debug_frame(), elf.GetLoadBias());
     printf("\n");
   } else {
     printf("\nno debug_frame information\n");
@@ -151,12 +153,12 @@
   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);
+      DumpDwarfSection(&elf, 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);
+      DumpDwarfSection(&elf, gnu_debugdata_interface->debug_frame(), 0);
       printf("\n");
     }
   } else {
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index 47a4f91..d0562d9 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -33,8 +33,11 @@
 #include <unwindstack/Elf.h>
 #include <unwindstack/ElfInterface.h>
 #include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
 
+#include "ArmExidx.h"
 #include "DwarfOp.h"
+#include "ElfInterfaceArm.h"
 
 namespace unwindstack {
 
@@ -136,16 +139,35 @@
   }
 }
 
-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;
+void PrintArmRegInformation(ElfInterfaceArm* interface, uint64_t pc) {
+  printf("\nArm exidx:\n");
+  uint64_t entry_offset;
+  if (!interface->FindEntry(pc, &entry_offset)) {
+    return;
   }
 
-  Elf elf(memory);
-  if (!elf.Init(true) || !elf.valid()) {
+  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) {
+  Elf elf(Memory::CreateFileMemory(file, pc).release());
+  if (!elf.Init() || !elf.valid()) {
     printf("%s is not a valid elf file.\n", file);
     return 1;
   }
@@ -157,17 +179,27 @@
     return 1;
   }
 
-  std::string soname;
-  if (elf.GetSoname(&soname)) {
+  std::string soname(elf.GetSoname());
+  if (!soname.empty()) {
     printf("Soname: %s\n\n", soname.c_str());
   }
 
-  printf("PC 0x%" PRIx64 ":\n", pc);
+  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 - load_bias, elf.class_type());
+    PrintRegInformation(section, elf.memory(), pc, elf.class_type());
   } else {
     printf("\nno eh_frame information\n");
   }
@@ -175,7 +207,7 @@
   section = interface->debug_frame();
   if (section != nullptr) {
     printf("\ndebug_frame:\n");
-    PrintRegInformation(section, memory, pc - load_bias, elf.class_type());
+    PrintRegInformation(section, elf.memory(), pc, elf.class_type());
     printf("\n");
   } else {
     printf("\nno debug_frame information\n");
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index 086dffe..8df2284 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -59,20 +59,14 @@
   // 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()) {
+  unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(argv[1], 0).release());
+  if (!elf.Init() || !elf.valid()) {
     printf("%s is not a valid elf file.\n", argv[1]);
     return 1;
   }
 
-  std::string soname;
-  if (elf.GetSoname(&soname)) {
+  std::string soname(elf.GetSoname());
+  if (!soname.empty()) {
     printf("Soname: %s\n\n", soname.c_str());
   }
 
@@ -95,7 +89,6 @@
   }
 
   std::string name;
-  uint64_t load_bias = elf.GetLoadBias();
   if (argc == 3) {
     std::string cur_name;
     uint64_t func_offset;
@@ -113,8 +106,8 @@
 
   // 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 + load_bias;
-    uint64_t end = entry.second.table_size + load_bias;
+    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;
diff --git a/libutils/Android.bp b/libutils/Android.bp
index a4fc4b4..8be4dd0 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -15,17 +15,21 @@
 cc_library_headers {
     name: "libutils_headers",
     vendor_available: true,
+    recovery_available: true,
     host_supported: true,
+    native_bridge_supported: true,
 
     header_libs: [
         "liblog_headers",
         "libsystem_headers",
         "libcutils_headers",
+        "libprocessgroup_headers",
     ],
     export_header_lib_headers: [
         "liblog_headers",
         "libsystem_headers",
         "libcutils_headers",
+        "libprocessgroup_headers",
     ],
     export_include_dirs: ["include"],
 
@@ -46,6 +50,7 @@
 cc_defaults {
     name: "libutils_defaults",
     vendor_available: true,
+    recovery_available: true,
     vndk: {
         enabled: true,
         support_system_process: true,
@@ -56,8 +61,8 @@
         "-Wall",
         "-Werror",
     ],
-    include_dirs: ["external/safe-iop/include"],
     header_libs: [
+        "libbase_headers",
         "libutils_headers",
     ],
     export_header_lib_headers: [
@@ -65,6 +70,7 @@
     ],
 
     shared_libs: [
+        "libcutils",
         "liblog",
     ],
 
@@ -79,7 +85,7 @@
             cflags: ["-fvisibility=protected"],
 
             shared_libs: [
-                "libcutils",
+                "libprocessgroup",
                 "libdl",
                 "libvndksupport",
             ],
@@ -89,12 +95,8 @@
             },
         },
 
-        host: {
-            cflags: ["-DLIBUTILS_NATIVE=1"],
-
-            shared: {
-                enabled: false,
-            },
+        recovery: {
+            exclude_shared_libs: ["libvndksupport"],
         },
 
         linux_bionic: {
@@ -120,6 +122,7 @@
 cc_library {
     name: "libutils",
     defaults: ["libutils_defaults"],
+    native_bridge_supported: true,
 
     srcs: [
         "FileMap.cpp",
@@ -129,7 +132,6 @@
         "PropertyMap.cpp",
         "RefBase.cpp",
         "SharedBuffer.cpp",
-        "Static.cpp",
         "StopWatch.cpp",
         "String8.cpp",
         "String16.cpp",
@@ -150,6 +152,7 @@
             ],
         },
         linux: {
+            header_libs: ["libbase_headers"],
             srcs: [
                 "Looper.cpp",
             ],
@@ -171,32 +174,118 @@
         },
     },
 
+    shared_libs: [
+         "libutils",
+         "libbacktrace",
+    ],
+
     target: {
-        android: {
-            shared_libs: [
-                "libutils",
-                "libbacktrace",
-            ],
-        },
         linux: {
             srcs: [
                 "ProcessCallStack.cpp",
             ],
         },
+        darwin: {
+            enabled: false,
+        },
+        windows: {
+            enabled: false,
+        },
     },
 }
 
-// Include subdirectory makefiles
-// ============================================================
+cc_test {
+    name: "libutils_test",
+    host_supported: true,
+
+    srcs: [
+        "BitSet_test.cpp",
+        "FileMap_test.cpp",
+        "LruCache_test.cpp",
+        "Mutex_test.cpp",
+        "SharedBuffer_test.cpp",
+        "String8_test.cpp",
+        "StrongPointer_test.cpp",
+        "Unicode_test.cpp",
+        "Vector_test.cpp",
+    ],
+
+    target: {
+        android: {
+            srcs: [
+                "SystemClock_test.cpp",
+            ],
+            shared_libs: [
+                "libz",
+                "liblog",
+                "libcutils",
+                "libutils",
+                "libbase",
+            ],
+        },
+        linux: {
+            srcs: [
+                "Looper_test.cpp",
+                "RefBase_test.cpp",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libutils",
+                "liblog",
+                "libbase",
+            ],
+        },
+    },
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wthread-safety",
+    ],
+
+    test_suites: ["device-tests"],
+}
+
+// TODO: the test infrastructure isn't yet capable of running this,
+// so it's broken out into its own test so that the main libutils_tests
+// can be in presubmit even if this can't.
 
 cc_test {
-    name: "SharedBufferTest",
-    host_supported: true,
-    static_libs: ["libutils"],
-    shared_libs: ["liblog"],
-    srcs: ["SharedBufferTest.cpp"],
+    name: "libutils_singleton_test",
+    srcs: ["Singleton_test.cpp"],
     cflags: [
         "-Wall",
         "-Werror",
     ],
+    shared_libs: ["libbase"],
+
+    required: [
+        ":libutils_test_singleton1",
+        ":libutils_test_singleton2",
+    ],
+}
+
+cc_test_library {
+    name: "libutils_test_singleton1",
+    host_supported: true,
+    relative_install_path: "libutils_test",
+    srcs: ["Singleton_test1.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
+
+cc_test_library {
+    name: "libutils_test_singleton2",
+    host_supported: true,
+    relative_install_path: "libutils_test",
+    srcs: ["Singleton_test2.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: ["libutils_test_singleton1"],
 }
diff --git a/libutils/tests/BitSet_test.cpp b/libutils/BitSet_test.cpp
similarity index 100%
rename from libutils/tests/BitSet_test.cpp
rename to libutils/BitSet_test.cpp
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index bd6015e..fe6f33d 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -16,16 +16,15 @@
 
 #define LOG_TAG "CallStack"
 
-#include <utils/CallStack.h>
-
-#include <memory>
-
 #include <utils/Printer.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
 
 #include <backtrace/Backtrace.h>
 
+#define CALLSTACK_WEAK  // Don't generate weak definitions.
+#include <utils/CallStack.h>
+
 namespace android {
 
 CallStack::CallStack() {
@@ -76,4 +75,30 @@
     }
 }
 
+// The following four functions may be used via weak symbol references from libutils.
+// Clients assume that if any of these symbols are available, then deleteStack() is.
+
+#ifdef WEAKS_AVAILABLE
+
+CallStack::CallStackUPtr CallStack::getCurrentInternal(int ignoreDepth) {
+    CallStack::CallStackUPtr stack(new CallStack());
+    stack->update(ignoreDepth + 1);
+    return stack;
+}
+
+void CallStack::logStackInternal(const char* logtag, const CallStack* stack,
+                                 android_LogPriority priority) {
+    stack->log(logtag, priority);
+}
+
+String8 CallStack::stackToStringInternal(const char* prefix, const CallStack* stack) {
+    return stack->toString(prefix);
+}
+
+void CallStack::deleteStack(CallStack* stack) {
+    delete stack;
+}
+
+#endif // WEAKS_AVAILABLE
+
 }; // namespace android
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 3c4d81c..1202c15 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__)
       ,
@@ -62,16 +62,22 @@
 }
 
 // Move Constructor.
-FileMap::FileMap(FileMap&& other)
-    : mFileName(other.mFileName), mBasePtr(other.mBasePtr), mBaseLength(other.mBaseLength),
-      mDataOffset(other.mDataOffset), mDataPtr(other.mDataPtr), mDataLength(other.mDataLength)
+FileMap::FileMap(FileMap&& other) noexcept
+    : mFileName(other.mFileName),
+      mBasePtr(other.mBasePtr),
+      mBaseLength(other.mBaseLength),
+      mDataOffset(other.mDataOffset),
+      mDataPtr(other.mDataPtr),
+      mDataLength(other.mDataLength)
 #if defined(__MINGW32__)
-      , mFileHandle(other.mFileHandle), mFileMapping(other.mFileMapping)
+      ,
+      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;
@@ -79,16 +85,16 @@
 }
 
 // Move assign operator.
-FileMap& FileMap::operator=(FileMap&& other) {
+FileMap& FileMap::operator=(FileMap&& other) noexcept {
     mFileName = other.mFileName;
     mBasePtr = other.mBasePtr;
     mBaseLength = other.mBaseLength;
     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 +107,7 @@
 // Destructor.
 FileMap::~FileMap(void)
 {
-    if (mFileName != NULL) {
+    if (mFileName != nullptr) {
         free(mFileName);
     }
 #if defined(__MINGW32__)
@@ -168,12 +174,6 @@
         return false;
     }
 #else // !defined(__MINGW32__)
-    int     prot, flags, adjust;
-    off64_t adjOffset;
-    size_t  adjLength;
-
-    void* ptr;
-
     assert(fd >= 0);
     assert(offset >= 0);
     assert(length > 0);
@@ -187,32 +187,33 @@
         }
     }
 
-    adjust = offset % mPageSize;
-    adjOffset = offset - adjust;
-    adjLength = length + adjust;
+    int adjust = offset % mPageSize;
+    off64_t adjOffset = offset - adjust;
+    size_t adjLength = length + adjust;
 
-    flags = MAP_SHARED;
-    prot = PROT_READ;
-    if (!readOnly)
-        prot |= PROT_WRITE;
+    int flags = MAP_SHARED;
+    int prot = PROT_READ;
+    if (!readOnly) prot |= PROT_WRITE;
 
-    ptr = mmap(NULL, adjLength, prot, flags, fd, adjOffset);
+    void* 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));
-        return false;
+        if (errno == EINVAL && length == 0) {
+            ptr = nullptr;
+            adjust = 0;
+        } else {
+            ALOGE("mmap(%lld,%zu) failed: %s\n", (long long)adjOffset, adjLength, strerror(errno));
+            return false;
+        }
     }
     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;
     mDataLength = length;
 
-    assert(mBasePtr != NULL);
-
     ALOGV("MAP: base %p/%zu data %p/%zu\n",
         mBasePtr, mBaseLength, mDataPtr, mDataLength);
 
diff --git a/libutils/FileMap_test.cpp b/libutils/FileMap_test.cpp
new file mode 100644
index 0000000..576d89b
--- /dev/null
+++ b/libutils/FileMap_test.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/FileMap.h"
+
+#include <gtest/gtest.h>
+
+#include "android-base/file.h"
+
+TEST(FileMap, zero_length_mapping) {
+    // http://b/119818070 "app crashes when reading asset of zero length".
+    // mmap fails with EINVAL for a zero length region.
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+
+    android::FileMap m;
+    ASSERT_TRUE(m.create("test", tf.fd, 4096, 0, true));
+    ASSERT_STREQ("test", m.getFileName());
+    ASSERT_EQ(0u, m.getDataLength());
+    ASSERT_EQ(4096, m.getDataOffset());
+}
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 6c57b2e..14e3e35 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -14,7 +14,9 @@
 #define DEBUG_CALLBACKS 0
 
 #include <utils/Looper.h>
+
 #include <sys/eventfd.h>
+#include <cinttypes>
 
 namespace android {
 
@@ -29,7 +31,7 @@
 
 void WeakMessageHandler::handleMessage(const Message& message) {
     sp<MessageHandler> handler = mHandler.promote();
-    if (handler != NULL) {
+    if (handler != nullptr) {
         handler->handleMessage(message);
     }
 }
@@ -51,43 +53,38 @@
 
 // --- Looper ---
 
-// Hint for number of file descriptors to be associated with the epoll instance.
-static const int EPOLL_SIZE_HINT = 8;
-
 // Maximum number of file descriptors for which to retrieve poll events each iteration.
 static const int EPOLL_MAX_EVENTS = 16;
 
 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);
-    mWakeEventFd = -1;
-    if (mEpollFd >= 0) {
-        close(mEpollFd);
-    }
 }
 
 void Looper::initTLSKey() {
-    int result = pthread_key_create(& gTLSKey, threadDestructor);
-    LOG_ALWAYS_FATAL_IF(result != 0, "Could not allocate TLS key.");
+    int error = pthread_key_create(&gTLSKey, threadDestructor);
+    LOG_ALWAYS_FATAL_IF(error != 0, "Could not allocate TLS key: %s", strerror(error));
 }
 
 void Looper::threadDestructor(void *st) {
     Looper* const self = static_cast<Looper*>(st);
-    if (self != NULL) {
+    if (self != nullptr) {
         self->decStrong((void*)threadDestructor);
     }
 }
@@ -95,13 +92,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);
     }
 }
@@ -116,7 +113,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);
     }
@@ -137,18 +134,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_create1(EPOLL_CLOEXEC));
     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));
 
@@ -157,7 +154,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));
@@ -190,9 +187,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;
             }
         }
@@ -201,9 +198,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;
         }
 
@@ -239,7 +236,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;
@@ -281,7 +278,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 {
@@ -401,11 +398,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) {
-            LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s",
-                    mWakeEventFd, strerror(errno));
+            LOG_ALWAYS_FATAL("Could not write wake signal to fd %d (returned %zd): %s",
+                             mWakeEventFd.get(), nWrite, strerror(errno));
         }
     }
 }
@@ -416,7 +413,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) {
@@ -427,7 +424,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) {
@@ -467,14 +464,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
@@ -495,7 +492,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));
@@ -542,7 +539,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/Looper_test.cpp b/libutils/Looper_test.cpp
new file mode 100644
index 0000000..6fdc0edc
--- /dev/null
+++ b/libutils/Looper_test.cpp
@@ -0,0 +1,746 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+
+#include <utils/Looper.h>
+#include <utils/Timers.h>
+#include <utils/StopWatch.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <utils/threads.h>
+
+// # of milliseconds to fudge stopwatch measurements
+#define TIMING_TOLERANCE_MS 25
+
+namespace android {
+
+enum {
+    MSG_TEST1 = 1,
+    MSG_TEST2 = 2,
+    MSG_TEST3 = 3,
+    MSG_TEST4 = 4,
+};
+
+class Pipe {
+public:
+    int sendFd;
+    int receiveFd;
+
+    Pipe() {
+        int fds[2];
+        ::pipe(fds);
+
+        receiveFd = fds[0];
+        sendFd = fds[1];
+    }
+
+    ~Pipe() {
+        if (sendFd != -1) {
+            ::close(sendFd);
+        }
+
+        if (receiveFd != -1) {
+            ::close(receiveFd);
+        }
+    }
+
+    status_t writeSignal() {
+        ssize_t nWritten = ::write(sendFd, "*", 1);
+        return nWritten == 1 ? 0 : -errno;
+    }
+
+    status_t readSignal() {
+        char buf[1];
+        ssize_t nRead = ::read(receiveFd, buf, 1);
+        return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno;
+    }
+};
+
+class DelayedTask : public Thread {
+    int mDelayMillis;
+
+public:
+    explicit DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { }
+
+protected:
+    virtual ~DelayedTask() { }
+
+    virtual void doTask() = 0;
+
+    virtual bool threadLoop() {
+        usleep(mDelayMillis * 1000);
+        doTask();
+        return false;
+    }
+};
+
+class DelayedWake : public DelayedTask {
+    sp<Looper> mLooper;
+
+public:
+    DelayedWake(int delayMillis, const sp<Looper> looper) :
+        DelayedTask(delayMillis), mLooper(looper) {
+    }
+
+protected:
+    virtual void doTask() {
+        mLooper->wake();
+    }
+};
+
+class DelayedWriteSignal : public DelayedTask {
+    Pipe* mPipe;
+
+public:
+    DelayedWriteSignal(int delayMillis, Pipe* pipe) :
+        DelayedTask(delayMillis), mPipe(pipe) {
+    }
+
+protected:
+    virtual void doTask() {
+        mPipe->writeSignal();
+    }
+};
+
+class CallbackHandler {
+public:
+    void setCallback(const sp<Looper>& looper, int fd, int events) {
+        looper->addFd(fd, 0, events, staticHandler, this);
+    }
+
+protected:
+    virtual ~CallbackHandler() { }
+
+    virtual int handler(int fd, int events) = 0;
+
+private:
+    static int staticHandler(int fd, int events, void* data) {
+        return static_cast<CallbackHandler*>(data)->handler(fd, events);
+    }
+};
+
+class StubCallbackHandler : public CallbackHandler {
+public:
+    int nextResult;
+    int callbackCount;
+
+    int fd;
+    int events;
+
+    explicit StubCallbackHandler(int nextResult) : nextResult(nextResult),
+            callbackCount(0), fd(-1), events(-1) {
+    }
+
+protected:
+    virtual int handler(int fd, int events) {
+        callbackCount += 1;
+        this->fd = fd;
+        this->events = events;
+        return nextResult;
+    }
+};
+
+class StubMessageHandler : public MessageHandler {
+public:
+    Vector<Message> messages;
+
+    virtual void handleMessage(const Message& message) {
+        messages.push(message);
+    }
+};
+
+class LooperTest : public testing::Test {
+protected:
+    sp<Looper> mLooper;
+
+    virtual void SetUp() {
+        mLooper = new Looper(true);
+    }
+
+    virtual void TearDown() {
+        mLooper.clear();
+    }
+};
+
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeout) {
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(100);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. equal timeout";
+    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+            << "pollOnce result should be LOOPER_POLL_TIMEOUT";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturns) {
+    mLooper->wake();
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(1000);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. zero because wake() was called before waiting";
+    EXPECT_EQ(Looper::POLL_WAKE, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because loop was awoken";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturns) {
+    sp<DelayedWake> delayedWake = new DelayedWake(100, mLooper);
+    delayedWake->run("LooperTest");
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(1000);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. equal wake delay";
+    EXPECT_EQ(Looper::POLL_WAKE, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because loop was awoken";
+}
+
+TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturns) {
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(0);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should be approx. zero";
+    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+            << "pollOnce result should be Looper::POLL_TIMEOUT";
+}
+
+TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturns) {
+    Pipe pipe;
+    StubCallbackHandler handler(true);
+
+    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(0);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should be approx. zero";
+    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+            << "pollOnce result should be Looper::POLL_TIMEOUT";
+    EXPECT_EQ(0, handler.callbackCount)
+            << "callback should not have been invoked because FD was not signalled";
+}
+
+TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCallbackAndReturns) {
+    Pipe pipe;
+    StubCallbackHandler handler(true);
+
+    ASSERT_EQ(OK, pipe.writeSignal());
+    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(0);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should be approx. zero";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled";
+    EXPECT_EQ(1, handler.callbackCount)
+            << "callback should be invoked exactly once";
+    EXPECT_EQ(pipe.receiveFd, handler.fd)
+            << "callback should have received pipe fd as parameter";
+    EXPECT_EQ(Looper::EVENT_INPUT, handler.events)
+            << "callback should have received Looper::EVENT_INPUT as events";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturns) {
+    Pipe pipe;
+    StubCallbackHandler handler(true);
+
+    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(100);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. equal timeout";
+    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+            << "pollOnce result should be Looper::POLL_TIMEOUT";
+    EXPECT_EQ(0, handler.callbackCount)
+            << "callback should not have been invoked because FD was not signalled";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_ImmediatelyInvokesCallbackAndReturns) {
+    Pipe pipe;
+    StubCallbackHandler handler(true);
+
+    pipe.writeSignal();
+    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(100);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    ASSERT_EQ(OK, pipe.readSignal())
+            << "signal should actually have been written";
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should be approx. zero";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled";
+    EXPECT_EQ(1, handler.callbackCount)
+            << "callback should be invoked exactly once";
+    EXPECT_EQ(pipe.receiveFd, handler.fd)
+            << "callback should have received pipe fd as parameter";
+    EXPECT_EQ(Looper::EVENT_INPUT, handler.events)
+            << "callback should have received Looper::EVENT_INPUT as events";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_PromptlyInvokesCallbackAndReturns) {
+    Pipe pipe;
+    StubCallbackHandler handler(true);
+    sp<DelayedWriteSignal> delayedWriteSignal = new DelayedWriteSignal(100, & pipe);
+
+    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
+    delayedWriteSignal->run("LooperTest");
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(1000);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    ASSERT_EQ(OK, pipe.readSignal())
+            << "signal should actually have been written";
+    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. equal signal delay";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled";
+    EXPECT_EQ(1, handler.callbackCount)
+            << "callback should be invoked exactly once";
+    EXPECT_EQ(pipe.receiveFd, handler.fd)
+            << "callback should have received pipe fd as parameter";
+    EXPECT_EQ(Looper::EVENT_INPUT, handler.events)
+            << "callback should have received Looper::EVENT_INPUT as events";
+}
+
+TEST_F(LooperTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) {
+    Pipe pipe;
+    StubCallbackHandler handler(true);
+
+    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
+    pipe.writeSignal(); // would cause FD to be considered signalled
+    mLooper->removeFd(pipe.receiveFd);
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(100);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    ASSERT_EQ(OK, pipe.readSignal())
+            << "signal should actually have been written";
+    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. equal timeout because FD was no longer registered";
+    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+            << "pollOnce result should be Looper::POLL_TIMEOUT";
+    EXPECT_EQ(0, handler.callbackCount)
+            << "callback should not be invoked";
+}
+
+TEST_F(LooperTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedAgainLater) {
+    Pipe pipe;
+    StubCallbackHandler handler(false);
+
+    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
+
+    // First loop: Callback is registered and FD is signalled.
+    pipe.writeSignal();
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(0);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    ASSERT_EQ(OK, pipe.readSignal())
+            << "signal should actually have been written";
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. equal zero because FD was already signalled";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled";
+    EXPECT_EQ(1, handler.callbackCount)
+            << "callback should be invoked";
+
+    // Second loop: Callback is no longer registered and FD is signalled.
+    pipe.writeSignal();
+
+    stopWatch.reset();
+    result = mLooper->pollOnce(0);
+    elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    ASSERT_EQ(OK, pipe.readSignal())
+            << "signal should actually have been written";
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. equal zero because timeout was zero";
+    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+            << "pollOnce result should be Looper::POLL_TIMEOUT";
+    EXPECT_EQ(1, handler.callbackCount)
+            << "callback should not be invoked this time";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonCallbackFdIsSignalled_ReturnsIdent) {
+    const int expectedIdent = 5;
+    void* expectedData = this;
+
+    Pipe pipe;
+
+    pipe.writeSignal();
+    mLooper->addFd(pipe.receiveFd, expectedIdent, Looper::EVENT_INPUT, nullptr, expectedData);
+
+    StopWatch stopWatch("pollOnce");
+    int fd;
+    int events;
+    void* data;
+    int result = mLooper->pollOnce(100, &fd, &events, &data);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    ASSERT_EQ(OK, pipe.readSignal())
+            << "signal should actually have been written";
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should be approx. zero";
+    EXPECT_EQ(expectedIdent, result)
+            << "pollOnce result should be the ident of the FD that was signalled";
+    EXPECT_EQ(pipe.receiveFd, fd)
+            << "pollOnce should have returned the received pipe fd";
+    EXPECT_EQ(Looper::EVENT_INPUT, events)
+            << "pollOnce should have returned Looper::EVENT_INPUT as events";
+    EXPECT_EQ(expectedData, data)
+            << "pollOnce should have returned the data";
+}
+
+TEST_F(LooperTest, AddFd_WhenCallbackAdded_ReturnsOne) {
+    Pipe pipe;
+    int result = mLooper->addFd(pipe.receiveFd, 0, Looper::EVENT_INPUT, nullptr, nullptr);
+
+    EXPECT_EQ(1, result)
+            << "addFd should return 1 because FD was added";
+}
+
+TEST_F(LooperTest, AddFd_WhenIdentIsNegativeAndCallbackIsNull_ReturnsError) {
+    Pipe pipe;
+    int result = mLooper->addFd(pipe.receiveFd, -1, Looper::EVENT_INPUT, nullptr, nullptr);
+
+    EXPECT_EQ(-1, result)
+            << "addFd should return -1 because arguments were invalid";
+}
+
+TEST_F(LooperTest, AddFd_WhenNoCallbackAndAllowNonCallbacksIsFalse_ReturnsError) {
+    Pipe pipe;
+    sp<Looper> looper = new Looper(false /*allowNonCallbacks*/);
+    int result = looper->addFd(pipe.receiveFd, 0, 0, nullptr, nullptr);
+
+    EXPECT_EQ(-1, result)
+            << "addFd should return -1 because arguments were invalid";
+}
+
+TEST_F(LooperTest, RemoveFd_WhenCallbackNotAdded_ReturnsZero) {
+    int result = mLooper->removeFd(1);
+
+    EXPECT_EQ(0, result)
+            << "removeFd should return 0 because FD not registered";
+}
+
+TEST_F(LooperTest, RemoveFd_WhenCallbackAddedThenRemovedTwice_ReturnsOnceFirstTimeAndReturnsZeroSecondTime) {
+    Pipe pipe;
+    StubCallbackHandler handler(false);
+    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
+
+    // First time.
+    int result = mLooper->removeFd(pipe.receiveFd);
+
+    EXPECT_EQ(1, result)
+            << "removeFd should return 1 first time because FD was registered";
+
+    // Second time.
+    result = mLooper->removeFd(pipe.receiveFd);
+
+    EXPECT_EQ(0, result)
+            << "removeFd should return 0 second time because FD was no longer registered";
+}
+
+TEST_F(LooperTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInvoked) {
+    Pipe pipe;
+    StubCallbackHandler handler1(true);
+    StubCallbackHandler handler2(true);
+
+    handler1.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
+    handler2.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT); // replace it
+    pipe.writeSignal(); // would cause FD to be considered signalled
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(100);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    ASSERT_EQ(OK, pipe.readSignal())
+            << "signal should actually have been written";
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. zero because FD was already signalled";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled";
+    EXPECT_EQ(0, handler1.callbackCount)
+            << "original handler callback should not be invoked because it was replaced";
+    EXPECT_EQ(1, handler2.callbackCount)
+            << "replacement handler callback should be invoked";
+}
+
+TEST_F(LooperTest, SendMessage_WhenOneMessageIsEnqueue_ShouldInvokeHandlerDuringNextPoll) {
+    sp<StubMessageHandler> handler = new StubMessageHandler();
+    mLooper->sendMessage(handler, Message(MSG_TEST1));
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(100);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. zero because message was already sent";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
+    EXPECT_EQ(size_t(1), handler->messages.size())
+            << "handled message";
+    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+            << "handled message";
+}
+
+TEST_F(LooperTest, SendMessage_WhenMultipleMessagesAreEnqueued_ShouldInvokeHandlersInOrderDuringNextPoll) {
+    sp<StubMessageHandler> handler1 = new StubMessageHandler();
+    sp<StubMessageHandler> handler2 = new StubMessageHandler();
+    mLooper->sendMessage(handler1, Message(MSG_TEST1));
+    mLooper->sendMessage(handler2, Message(MSG_TEST2));
+    mLooper->sendMessage(handler1, Message(MSG_TEST3));
+    mLooper->sendMessage(handler1, Message(MSG_TEST4));
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(1000);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. zero because message was already sent";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
+    EXPECT_EQ(size_t(3), handler1->messages.size())
+            << "handled message";
+    EXPECT_EQ(MSG_TEST1, handler1->messages[0].what)
+            << "handled message";
+    EXPECT_EQ(MSG_TEST3, handler1->messages[1].what)
+            << "handled message";
+    EXPECT_EQ(MSG_TEST4, handler1->messages[2].what)
+            << "handled message";
+    EXPECT_EQ(size_t(1), handler2->messages.size())
+            << "handled message";
+    EXPECT_EQ(MSG_TEST2, handler2->messages[0].what)
+            << "handled message";
+}
+
+TEST_F(LooperTest, SendMessageDelayed_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) {
+    sp<StubMessageHandler> handler = new StubMessageHandler();
+    mLooper->sendMessageDelayed(ms2ns(100), handler, Message(MSG_TEST1));
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(1000);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "first poll should end quickly because next message timeout was computed";
+    EXPECT_EQ(Looper::POLL_WAKE, result)
+            << "pollOnce result should be Looper::POLL_WAKE due to wakeup";
+    EXPECT_EQ(size_t(0), handler->messages.size())
+            << "no message handled yet";
+
+    result = mLooper->pollOnce(1000);
+    elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_EQ(size_t(1), handler->messages.size())
+            << "handled message";
+    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+            << "handled message";
+    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "second poll should end around the time of the delayed message dispatch";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
+
+    result = mLooper->pollOnce(100);
+    elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "third poll should timeout";
+    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+            << "pollOnce result should be Looper::POLL_TIMEOUT because there were no messages left";
+}
+
+TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) {
+    sp<StubMessageHandler> handler = new StubMessageHandler();
+    mLooper->sendMessageDelayed(ms2ns(-1000), handler, Message(MSG_TEST1));
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(100);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. zero because message was already sent";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
+    EXPECT_EQ(size_t(1), handler->messages.size())
+            << "handled message";
+    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+            << "handled message";
+}
+
+TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) {
+    sp<StubMessageHandler> handler = new StubMessageHandler();
+    mLooper->sendMessageDelayed(0, handler, Message(MSG_TEST1));
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(100);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. zero because message was already sent";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
+    EXPECT_EQ(size_t(1), handler->messages.size())
+            << "handled message";
+    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+            << "handled message";
+}
+
+TEST_F(LooperTest, SendMessageAtTime_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) {
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    sp<StubMessageHandler> handler = new StubMessageHandler();
+    mLooper->sendMessageAtTime(now + ms2ns(100), handler, Message(MSG_TEST1));
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(1000);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "first poll should end quickly because next message timeout was computed";
+    EXPECT_EQ(Looper::POLL_WAKE, result)
+            << "pollOnce result should be Looper::POLL_WAKE due to wakeup";
+    EXPECT_EQ(size_t(0), handler->messages.size())
+            << "no message handled yet";
+
+    result = mLooper->pollOnce(1000);
+    elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_EQ(size_t(1), handler->messages.size())
+            << "handled message";
+    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+            << "handled message";
+    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "second poll should end around the time of the delayed message dispatch";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
+
+    result = mLooper->pollOnce(100);
+    elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "third poll should timeout";
+    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+            << "pollOnce result should be Looper::POLL_TIMEOUT because there were no messages left";
+}
+
+TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) {
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    sp<StubMessageHandler> handler = new StubMessageHandler();
+    mLooper->sendMessageAtTime(now - ms2ns(1000), handler, Message(MSG_TEST1));
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(100);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. zero because message was already sent";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
+    EXPECT_EQ(size_t(1), handler->messages.size())
+            << "handled message";
+    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+            << "handled message";
+}
+
+TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) {
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    sp<StubMessageHandler> handler = new StubMessageHandler();
+    mLooper->sendMessageAtTime(now, handler, Message(MSG_TEST1));
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(100);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. zero because message was already sent";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
+    EXPECT_EQ(size_t(1), handler->messages.size())
+            << "handled message";
+    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+            << "handled message";
+}
+
+TEST_F(LooperTest, RemoveMessage_WhenRemovingAllMessagesForHandler_ShouldRemoveThoseMessage) {
+    sp<StubMessageHandler> handler = new StubMessageHandler();
+    mLooper->sendMessage(handler, Message(MSG_TEST1));
+    mLooper->sendMessage(handler, Message(MSG_TEST2));
+    mLooper->sendMessage(handler, Message(MSG_TEST3));
+    mLooper->removeMessages(handler);
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(0);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. zero because message was sent so looper was awoken";
+    EXPECT_EQ(Looper::POLL_WAKE, result)
+            << "pollOnce result should be Looper::POLL_WAKE because looper was awoken";
+    EXPECT_EQ(size_t(0), handler->messages.size())
+            << "no messages to handle";
+
+    result = mLooper->pollOnce(0);
+
+    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+            << "pollOnce result should be Looper::POLL_TIMEOUT because there was nothing to do";
+    EXPECT_EQ(size_t(0), handler->messages.size())
+            << "no messages to handle";
+}
+
+TEST_F(LooperTest, RemoveMessage_WhenRemovingSomeMessagesForHandler_ShouldRemoveThoseMessage) {
+    sp<StubMessageHandler> handler = new StubMessageHandler();
+    mLooper->sendMessage(handler, Message(MSG_TEST1));
+    mLooper->sendMessage(handler, Message(MSG_TEST2));
+    mLooper->sendMessage(handler, Message(MSG_TEST3));
+    mLooper->sendMessage(handler, Message(MSG_TEST4));
+    mLooper->removeMessages(handler, MSG_TEST3);
+    mLooper->removeMessages(handler, MSG_TEST1);
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(0);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. zero because message was sent so looper was awoken";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because two messages were sent";
+    EXPECT_EQ(size_t(2), handler->messages.size())
+            << "no messages to handle";
+    EXPECT_EQ(MSG_TEST2, handler->messages[0].what)
+            << "handled message";
+    EXPECT_EQ(MSG_TEST4, handler->messages[1].what)
+            << "handled message";
+
+    result = mLooper->pollOnce(0);
+
+    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+            << "pollOnce result should be Looper::POLL_TIMEOUT because there was nothing to do";
+    EXPECT_EQ(size_t(2), handler->messages.size())
+            << "no more messages to handle";
+}
+
+} // namespace android
diff --git a/libutils/LruCache_test.cpp b/libutils/LruCache_test.cpp
new file mode 100644
index 0000000..c4d917b
--- /dev/null
+++ b/libutils/LruCache_test.cpp
@@ -0,0 +1,459 @@
+/*
+ * 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 <stdlib.h>
+
+#include <android/log.h>
+#include <gtest/gtest.h>
+#include <utils/JenkinsHash.h>
+#include <utils/LruCache.h>
+
+namespace {
+
+typedef int SimpleKey;
+typedef const char* StringValue;
+
+struct ComplexKey {
+    int k;
+
+    explicit ComplexKey(int k) : k(k) {
+        instanceCount += 1;
+    }
+
+    ComplexKey(const ComplexKey& other) : k(other.k) {
+        instanceCount += 1;
+    }
+
+    ~ComplexKey() {
+        instanceCount -= 1;
+    }
+
+    bool operator ==(const ComplexKey& other) const {
+        return k == other.k;
+    }
+
+    bool operator !=(const ComplexKey& other) const {
+        return k != other.k;
+    }
+
+    static ssize_t instanceCount;
+};
+
+ssize_t ComplexKey::instanceCount = 0;
+
+struct ComplexValue {
+    int v;
+
+    explicit ComplexValue(int v) : v(v) {
+        instanceCount += 1;
+    }
+
+    ComplexValue(const ComplexValue& other) : v(other.v) {
+        instanceCount += 1;
+    }
+
+    ~ComplexValue() {
+        instanceCount -= 1;
+    }
+
+    static ssize_t instanceCount;
+};
+
+ssize_t ComplexValue::instanceCount = 0;
+
+struct KeyWithPointer {
+    int *ptr;
+    bool operator ==(const KeyWithPointer& other) const {
+        return *ptr == *other.ptr;
+    }
+};
+
+struct KeyFailsOnCopy : public ComplexKey {
+    public:
+    KeyFailsOnCopy(const KeyFailsOnCopy& key) : ComplexKey(key) {
+        ADD_FAILURE();
+    }
+    KeyFailsOnCopy(int key) : ComplexKey(key) { }
+};
+
+} // namespace
+
+
+namespace android {
+
+typedef LruCache<ComplexKey, ComplexValue> ComplexCache;
+
+template<> inline android::hash_t hash_type(const ComplexKey& value) {
+    return hash_type(value.k);
+}
+
+template<> inline android::hash_t hash_type(const KeyWithPointer& value) {
+    return hash_type(*value.ptr);
+}
+
+template<> inline android::hash_t hash_type(const KeyFailsOnCopy& value) {
+    return hash_type<ComplexKey>(value);
+}
+
+class EntryRemovedCallback : public OnEntryRemoved<SimpleKey, StringValue> {
+public:
+    EntryRemovedCallback() : callbackCount(0), lastKey(-1), lastValue(nullptr) { }
+    ~EntryRemovedCallback() {}
+    void operator()(SimpleKey& k, StringValue& v) {
+        callbackCount += 1;
+        lastKey = k;
+        lastValue = v;
+    }
+    ssize_t callbackCount;
+    SimpleKey lastKey;
+    StringValue lastValue;
+};
+
+class InvalidateKeyCallback : public OnEntryRemoved<KeyWithPointer, StringValue> {
+public:
+    void operator()(KeyWithPointer& k, StringValue&) {
+        delete k.ptr;
+        k.ptr = nullptr;
+    }
+};
+
+class LruCacheTest : public testing::Test {
+protected:
+    virtual void SetUp() {
+        ComplexKey::instanceCount = 0;
+        ComplexValue::instanceCount = 0;
+    }
+
+    virtual void TearDown() {
+        ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+    }
+
+    void assertInstanceCount(ssize_t keys, ssize_t values) {
+        if (keys != ComplexKey::instanceCount || values != ComplexValue::instanceCount) {
+            FAIL() << "Expected " << keys << " keys and " << values << " values "
+                    "but there were actually " << ComplexKey::instanceCount << " keys and "
+                    << ComplexValue::instanceCount << " values";
+        }
+    }
+};
+
+TEST_F(LruCacheTest, Empty) {
+    LruCache<SimpleKey, StringValue> cache(100);
+
+    EXPECT_EQ(nullptr, cache.get(0));
+    EXPECT_EQ(0u, cache.size());
+}
+
+TEST_F(LruCacheTest, Simple) {
+    LruCache<SimpleKey, StringValue> cache(100);
+
+    cache.put(1, "one");
+    cache.put(2, "two");
+    cache.put(3, "three");
+    EXPECT_STREQ("one", cache.get(1));
+    EXPECT_STREQ("two", cache.get(2));
+    EXPECT_STREQ("three", cache.get(3));
+    EXPECT_EQ(3u, cache.size());
+}
+
+TEST_F(LruCacheTest, MaxCapacity) {
+    LruCache<SimpleKey, StringValue> cache(2);
+
+    cache.put(1, "one");
+    cache.put(2, "two");
+    cache.put(3, "three");
+    EXPECT_EQ(nullptr, cache.get(1));
+    EXPECT_STREQ("two", cache.get(2));
+    EXPECT_STREQ("three", cache.get(3));
+    EXPECT_EQ(2u, cache.size());
+}
+
+TEST_F(LruCacheTest, RemoveLru) {
+    LruCache<SimpleKey, StringValue> cache(100);
+
+    cache.put(1, "one");
+    cache.put(2, "two");
+    cache.put(3, "three");
+    cache.removeOldest();
+    EXPECT_EQ(nullptr, cache.get(1));
+    EXPECT_STREQ("two", cache.get(2));
+    EXPECT_STREQ("three", cache.get(3));
+    EXPECT_EQ(2u, cache.size());
+}
+
+TEST_F(LruCacheTest, GetUpdatesLru) {
+    LruCache<SimpleKey, StringValue> cache(100);
+
+    cache.put(1, "one");
+    cache.put(2, "two");
+    cache.put(3, "three");
+    EXPECT_STREQ("one", cache.get(1));
+    cache.removeOldest();
+    EXPECT_STREQ("one", cache.get(1));
+    EXPECT_EQ(nullptr, cache.get(2));
+    EXPECT_STREQ("three", cache.get(3));
+    EXPECT_EQ(2u, cache.size());
+}
+
+uint32_t hash_int(int x) {
+    return JenkinsHashWhiten(JenkinsHashMix(0, x));
+}
+
+TEST_F(LruCacheTest, StressTest) {
+    const size_t kCacheSize = 512;
+    LruCache<SimpleKey, StringValue> cache(512);
+    const size_t kNumKeys = 16 * 1024;
+    const size_t kNumIters = 100000;
+    char* strings[kNumKeys];
+
+    for (size_t i = 0; i < kNumKeys; i++) {
+        strings[i] = (char *)malloc(16);
+        sprintf(strings[i], "%zu", i);
+    }
+
+    srandom(12345);
+    int hitCount = 0;
+    for (size_t i = 0; i < kNumIters; i++) {
+        int index = random() % kNumKeys;
+        uint32_t key = hash_int(index);
+        const char *val = cache.get(key);
+        if (val != nullptr) {
+            EXPECT_EQ(strings[index], val);
+            hitCount++;
+        } else {
+            cache.put(key, strings[index]);
+        }
+    }
+    size_t expectedHitCount = kNumIters * kCacheSize / kNumKeys;
+    EXPECT_LT(int(expectedHitCount * 0.9), hitCount);
+    EXPECT_GT(int(expectedHitCount * 1.1), hitCount);
+    EXPECT_EQ(kCacheSize, cache.size());
+
+    for (size_t i = 0; i < kNumKeys; i++) {
+        free((void *)strings[i]);
+    }
+}
+
+TEST_F(LruCacheTest, NoLeak) {
+    ComplexCache cache(100);
+
+    cache.put(ComplexKey(0), ComplexValue(0));
+    cache.put(ComplexKey(1), ComplexValue(1));
+    EXPECT_EQ(2U, cache.size());
+    assertInstanceCount(2, 3);  // the member mNullValue counts as an instance
+}
+
+TEST_F(LruCacheTest, Clear) {
+    ComplexCache cache(100);
+
+    cache.put(ComplexKey(0), ComplexValue(0));
+    cache.put(ComplexKey(1), ComplexValue(1));
+    EXPECT_EQ(2U, cache.size());
+    assertInstanceCount(2, 3);
+    cache.clear();
+    assertInstanceCount(0, 1);
+}
+
+TEST_F(LruCacheTest, ClearNoDoubleFree) {
+    {
+        ComplexCache cache(100);
+
+        cache.put(ComplexKey(0), ComplexValue(0));
+        cache.put(ComplexKey(1), ComplexValue(1));
+        EXPECT_EQ(2U, cache.size());
+        assertInstanceCount(2, 3);
+        cache.removeOldest();
+        cache.clear();
+        assertInstanceCount(0, 1);
+    }
+    assertInstanceCount(0, 0);
+}
+
+TEST_F(LruCacheTest, ClearReuseOk) {
+    ComplexCache cache(100);
+
+    cache.put(ComplexKey(0), ComplexValue(0));
+    cache.put(ComplexKey(1), ComplexValue(1));
+    EXPECT_EQ(2U, cache.size());
+    assertInstanceCount(2, 3);
+    cache.clear();
+    assertInstanceCount(0, 1);
+    cache.put(ComplexKey(0), ComplexValue(0));
+    cache.put(ComplexKey(1), ComplexValue(1));
+    EXPECT_EQ(2U, cache.size());
+    assertInstanceCount(2, 3);
+}
+
+TEST_F(LruCacheTest, Callback) {
+    LruCache<SimpleKey, StringValue> cache(100);
+    EntryRemovedCallback callback;
+    cache.setOnEntryRemovedListener(&callback);
+
+    cache.put(1, "one");
+    cache.put(2, "two");
+    cache.put(3, "three");
+    EXPECT_EQ(3U, cache.size());
+    cache.removeOldest();
+    EXPECT_EQ(1, callback.callbackCount);
+    EXPECT_EQ(1, callback.lastKey);
+    EXPECT_STREQ("one", callback.lastValue);
+}
+
+TEST_F(LruCacheTest, CallbackOnClear) {
+    LruCache<SimpleKey, StringValue> cache(100);
+    EntryRemovedCallback callback;
+    cache.setOnEntryRemovedListener(&callback);
+
+    cache.put(1, "one");
+    cache.put(2, "two");
+    cache.put(3, "three");
+    EXPECT_EQ(3U, cache.size());
+    cache.clear();
+    EXPECT_EQ(3, callback.callbackCount);
+}
+
+TEST_F(LruCacheTest, CallbackRemovesKeyWorksOK) {
+    LruCache<KeyWithPointer, StringValue> cache(1);
+    InvalidateKeyCallback callback;
+    cache.setOnEntryRemovedListener(&callback);
+    KeyWithPointer key1;
+    key1.ptr = new int(1);
+    KeyWithPointer key2;
+    key2.ptr = new int(2);
+
+    cache.put(key1, "one");
+    // As the size of the cache is 1, the put will call the callback.
+    // Make sure everything goes smoothly even if the callback invalidates
+    // the key (b/24785286)
+    cache.put(key2, "two");
+    EXPECT_EQ(1U, cache.size());
+    EXPECT_STREQ("two", cache.get(key2));
+    cache.clear();
+}
+
+TEST_F(LruCacheTest, IteratorCheck) {
+    LruCache<int, int> cache(100);
+
+    cache.put(1, 4);
+    cache.put(2, 5);
+    cache.put(3, 6);
+    EXPECT_EQ(3U, cache.size());
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        int v = it.value();
+        // Check we haven't seen the value before.
+        EXPECT_TRUE(returnedValues.find(v) == returnedValues.end());
+        returnedValues.insert(v);
+    }
+    EXPECT_EQ(std::unordered_set<int>({4, 5, 6}), returnedValues);
+}
+
+TEST_F(LruCacheTest, EmptyCacheIterator) {
+    // Check that nothing crashes...
+    LruCache<int, int> cache(100);
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        returnedValues.insert(it.value());
+    }
+    EXPECT_EQ(std::unordered_set<int>(), returnedValues);
+}
+
+TEST_F(LruCacheTest, OneElementCacheIterator) {
+    // Check that nothing crashes...
+    LruCache<int, int> cache(100);
+    cache.put(1, 2);
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        returnedValues.insert(it.value());
+    }
+    EXPECT_EQ(std::unordered_set<int>({ 2 }), returnedValues);
+}
+
+TEST_F(LruCacheTest, OneElementCacheRemove) {
+    LruCache<int, int> cache(100);
+    cache.put(1, 2);
+
+    cache.remove(1);
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        returnedValues.insert(it.value());
+    }
+    EXPECT_EQ(std::unordered_set<int>({ }), returnedValues);
+}
+
+TEST_F(LruCacheTest, Remove) {
+    LruCache<int, int> cache(100);
+    cache.put(1, 4);
+    cache.put(2, 5);
+    cache.put(3, 6);
+
+    cache.remove(2);
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        returnedValues.insert(it.value());
+    }
+    EXPECT_EQ(std::unordered_set<int>({ 4, 6 }), returnedValues);
+}
+
+TEST_F(LruCacheTest, RemoveYoungest) {
+    LruCache<int, int> cache(100);
+    cache.put(1, 4);
+    cache.put(2, 5);
+    cache.put(3, 6);
+
+    cache.remove(3);
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        returnedValues.insert(it.value());
+    }
+    EXPECT_EQ(std::unordered_set<int>({ 4, 5 }), returnedValues);
+}
+
+TEST_F(LruCacheTest, RemoveNonMember) {
+    LruCache<int, int> cache(100);
+    cache.put(1, 4);
+    cache.put(2, 5);
+    cache.put(3, 6);
+
+    cache.remove(7);
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        returnedValues.insert(it.value());
+    }
+    EXPECT_EQ(std::unordered_set<int>({ 4, 5, 6 }), returnedValues);
+}
+
+TEST_F(LruCacheTest, DontCopyKeyInGet) {
+    LruCache<KeyFailsOnCopy, KeyFailsOnCopy> cache(1);
+    // Check that get doesn't copy the key
+    cache.get(KeyFailsOnCopy(0));
+}
+
+}
diff --git a/libutils/Mutex_test.cpp b/libutils/Mutex_test.cpp
new file mode 100644
index 0000000..79f4302
--- /dev/null
+++ b/libutils/Mutex_test.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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();
+}
+
+TEST(Mutex, tryLock) {
+    if (mLock.tryLock() != 0) {
+        return;
+    }
+    mLock.unlock();
+}
+
+#if defined(__ANDROID__)
+TEST(Mutex, timedLock) {
+    if (mLock.timedLock(1) != 0) {
+        return;
+    }
+    mLock.unlock();
+}
+#endif
diff --git a/libutils/NativeHandle.cpp b/libutils/NativeHandle.cpp
index 97d06b8..d437a9f 100644
--- a/libutils/NativeHandle.cpp
+++ b/libutils/NativeHandle.cpp
@@ -20,7 +20,7 @@
 namespace android {
 
 sp<NativeHandle> NativeHandle::create(native_handle_t* handle, bool ownsHandle) {
-    return handle ? new NativeHandle(handle, ownsHandle) : NULL;
+    return handle ? new NativeHandle(handle, ownsHandle) : nullptr;
 }
 
 NativeHandle::NativeHandle(native_handle_t* handle, bool ownsHandle)
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp
index cbf042e..c9ae210 100644
--- a/libutils/Printer.cpp
+++ b/libutils/Printer.cpp
@@ -73,7 +73,7 @@
 }
 
 void LogPrinter::printLine(const char* string) {
-    if (string == NULL) {
+    if (string == nullptr) {
         ALOGW("%s: NULL string passed in", __FUNCTION__);
         return;
     }
@@ -107,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) {
@@ -127,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 b8fb6dc..f054de9 100644
--- a/libutils/ProcessCallStack.cpp
+++ b/libutils/ProcessCallStack.cpp
@@ -42,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);
@@ -76,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;
 
@@ -88,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);
     }
@@ -128,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;
@@ -140,7 +140,7 @@
 
     // Get current time.
     {
-        time_t t = time(NULL);
+        time_t t = time(nullptr);
         struct tm tm;
         localtime_r(&t, &tm);
 
@@ -152,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 4bcdd0f..f00272a 100644
--- a/libutils/PropertyMap.cpp
+++ b/libutils/PropertyMap.cpp
@@ -112,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);
@@ -208,7 +208,7 @@
 
         mTokenizer->nextLine();
     }
-    return NO_ERROR;
+    return OK;
 }
 
 } // namespace android
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 8bccb0f..ae10789 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -17,30 +17,43 @@
 #define LOG_TAG "RefBase"
 // #define LOG_NDEBUG 0
 
+#include <memory>
+
+#include <android-base/macros.h>
+
 #include <utils/RefBase.h>
 
 #include <utils/CallStack.h>
 
+#include <utils/Mutex.h>
+
 #ifndef __unused
 #define __unused __attribute__((__unused__))
 #endif
 
-// compile with refcounting debugging enabled
-#define DEBUG_REFS                      0
+// Compile with refcounting debugging enabled.
+#define DEBUG_REFS 0
+
+// The following three are ignored unless DEBUG_REFS is set.
 
 // whether ref-tracking is enabled by default, if not, trackMe(true, false)
 // needs to be called explicitly
-#define DEBUG_REFS_ENABLED_BY_DEFAULT   0
+#define DEBUG_REFS_ENABLED_BY_DEFAULT 0
 
 // whether callstack are collected (significantly slows things down)
-#define DEBUG_REFS_CALLSTACK_ENABLED    1
+#define DEBUG_REFS_CALLSTACK_ENABLED 1
 
 // folder where stack traces are saved when DEBUG_REFS is enabled
 // this folder needs to exist and be writable
-#define DEBUG_REFS_CALLSTACK_PATH       "/data/debug"
+#define DEBUG_REFS_CALLSTACK_PATH "/data/debug"
 
 // log all reference counting operations
-#define PRINT_REFS                      0
+#define PRINT_REFS 0
+
+// Continue after logging a stack trace if ~RefBase discovers that reference
+// count has never been incremented. Normally we conspicuously crash in that
+// case.
+#define DEBUG_REFBASE_DESTRUCTION 1
 
 // ---------------------------------------------------------------------------
 
@@ -184,7 +197,7 @@
                 char inc = refs->ref >= 0 ? '+' : '-';
                 ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
 #if DEBUG_REFS_CALLSTACK_ENABLED
-                refs->stack.log(LOG_TAG);
+                CallStack::logStack(LOG_TAG, refs->stack.get());
 #endif
                 refs = refs->next;
             }
@@ -198,14 +211,14 @@
                 char inc = refs->ref >= 0 ? '+' : '-';
                 ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
 #if DEBUG_REFS_CALLSTACK_ENABLED
-                refs->stack.log(LOG_TAG);
+                CallStack::logStack(LOG_TAG, refs->stack.get());
 #endif
                 refs = refs->next;
             }
         }
         if (dumpStack) {
             ALOGE("above errors at:");
-            CallStack stack(LOG_TAG);
+            CallStack::logStack(LOG_TAG);
         }
     }
 
@@ -279,7 +292,7 @@
                      this);
             int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644);
             if (rc >= 0) {
-                write(rc, text.string(), text.length());
+                (void)write(rc, text.string(), text.length());
                 close(rc);
                 ALOGD("STACK TRACE for %p saved in %s", this, name);
             }
@@ -294,7 +307,7 @@
         ref_entry* next;
         const void* id;
 #if DEBUG_REFS_CALLSTACK_ENABLED
-        CallStack stack;
+        CallStack::CallStackUPtr stack;
 #endif
         int32_t ref;
     };
@@ -311,7 +324,7 @@
             ref->ref = mRef;
             ref->id = id;
 #if DEBUG_REFS_CALLSTACK_ENABLED
-            ref->stack.update(2);
+            ref->stack = CallStack::getCurrent(2);
 #endif
             ref->next = *refs;
             *refs = ref;
@@ -346,7 +359,7 @@
                 ref = ref->next;
             }
 
-            CallStack stack(LOG_TAG);
+            CallStack::logStack(LOG_TAG);
         }
     }
 
@@ -373,7 +386,7 @@
                      inc, refs->id, refs->ref);
             out->append(buf);
 #if DEBUG_REFS_CALLSTACK_ENABLED
-            out->append(refs->stack.toString("\t\t"));
+            out->append(CallStack::stackToString("\t\t", refs->stack.get()));
 #else
             out->append("\t\t(call stacks disabled)");
 #endif
@@ -468,7 +481,7 @@
     case INITIAL_STRONG_VALUE:
         refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
                 std::memory_order_relaxed);
-        // fall through...
+        FALLTHROUGH_INTENDED;
     case 0:
         refs->mBase->onFirstRef();
     }
@@ -700,19 +713,19 @@
         if (mRefs->mWeak.load(std::memory_order_relaxed) == 0) {
             delete mRefs;
         }
-    } else if (mRefs->mStrong.load(std::memory_order_relaxed)
-            == INITIAL_STRONG_VALUE) {
+    } else if (mRefs->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE) {
         // We never acquired a strong reference on this object.
-        LOG_ALWAYS_FATAL_IF(mRefs->mWeak.load() != 0,
-                "RefBase: Explicit destruction with non-zero weak "
-                "reference count");
-        // TODO: Always report if we get here. Currently MediaMetadataRetriever
-        // C++ objects are inconsistently managed and sometimes get here.
-        // There may be other cases, but we believe they should all be fixed.
-        delete mRefs;
+#if DEBUG_REFBASE_DESTRUCTION
+        // Treating this as fatal is prone to causing boot loops. For debugging, it's
+        // better to treat as non-fatal.
+        ALOGD("RefBase: Explicit destruction, weak count = %d (in %p)", mRefs->mWeak.load(), this);
+        CallStack::logStack(LOG_TAG);
+#else
+        LOG_ALWAYS_FATAL("RefBase: Explicit destruction, weak count = %d", mRefs->mWeak.load());
+#endif
     }
     // 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)
diff --git a/libutils/RefBase_test.cpp b/libutils/RefBase_test.cpp
new file mode 100644
index 0000000..c9b4894
--- /dev/null
+++ b/libutils/RefBase_test.cpp
@@ -0,0 +1,408 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <utils/StrongPointer.h>
+#include <utils/RefBase.h>
+
+#include <thread>
+#include <atomic>
+#include <sched.h>
+#include <errno.h>
+
+// Enhanced version of StrongPointer_test, but using RefBase underneath.
+
+using namespace android;
+
+static constexpr int NITERS = 1000000;
+
+static constexpr int INITIAL_STRONG_VALUE = 1 << 28;  // Mirroring RefBase definition.
+
+class Foo : public RefBase {
+public:
+    Foo(bool* deleted_check) : mDeleted(deleted_check) {
+        *mDeleted = false;
+    }
+
+    ~Foo() {
+        *mDeleted = true;
+    }
+private:
+    bool* mDeleted;
+};
+
+// A version of Foo that ensures that all objects are allocated at the same
+// address. No more than one can be allocated at a time. Thread-hostile.
+class FooFixedAlloc : public RefBase {
+public:
+    static void* operator new(size_t size) {
+        if (mAllocCount != 0) {
+            abort();
+        }
+        mAllocCount = 1;
+        if (theMemory == nullptr) {
+            theMemory = malloc(size);
+        }
+        return theMemory;
+    }
+
+    static void operator delete(void *p) {
+        if (mAllocCount != 1 || p != theMemory) {
+            abort();
+        }
+        mAllocCount = 0;
+    }
+
+    FooFixedAlloc(bool* deleted_check) : mDeleted(deleted_check) {
+        *mDeleted = false;
+    }
+
+    ~FooFixedAlloc() {
+        *mDeleted = true;
+    }
+private:
+    bool* mDeleted;
+    static int mAllocCount;
+    static void* theMemory;
+};
+
+int FooFixedAlloc::mAllocCount(0);
+void* FooFixedAlloc::theMemory(nullptr);
+
+TEST(RefBase, StrongMoves) {
+    bool isDeleted;
+    Foo* foo = new Foo(&isDeleted);
+    ASSERT_EQ(INITIAL_STRONG_VALUE, foo->getStrongCount());
+    ASSERT_FALSE(isDeleted) << "Already deleted...?";
+    sp<Foo> sp1(foo);
+    wp<Foo> wp1(sp1);
+    ASSERT_EQ(1, foo->getStrongCount());
+    // Weak count includes both strong and weak references.
+    ASSERT_EQ(2, foo->getWeakRefs()->getWeakCount());
+    {
+        sp<Foo> sp2 = std::move(sp1);
+        ASSERT_EQ(1, foo->getStrongCount())
+                << "std::move failed, incremented refcnt";
+        ASSERT_EQ(nullptr, sp1.get()) << "std::move failed, sp1 is still valid";
+        // The strong count isn't increasing, let's double check the old object
+        // is properly reset and doesn't early delete
+        sp1 = std::move(sp2);
+    }
+    ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!";
+    {
+        // Now let's double check it deletes on time
+        sp<Foo> sp2 = std::move(sp1);
+    }
+    ASSERT_TRUE(isDeleted) << "foo was leaked!";
+    ASSERT_TRUE(wp1.promote().get() == nullptr);
+}
+
+TEST(RefBase, WeakCopies) {
+    bool isDeleted;
+    Foo* foo = new Foo(&isDeleted);
+    EXPECT_EQ(0, foo->getWeakRefs()->getWeakCount());
+    ASSERT_FALSE(isDeleted) << "Foo (weak) already deleted...?";
+    wp<Foo> wp1(foo);
+    EXPECT_EQ(1, foo->getWeakRefs()->getWeakCount());
+    {
+        wp<Foo> wp2 = wp1;
+        ASSERT_EQ(2, foo->getWeakRefs()->getWeakCount());
+    }
+    EXPECT_EQ(1, foo->getWeakRefs()->getWeakCount());
+    ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!";
+    wp1 = nullptr;
+    ASSERT_FALSE(isDeleted) << "Deletion on wp destruction should no longer occur";
+}
+
+TEST(RefBase, Comparisons) {
+    bool isDeleted, isDeleted2, isDeleted3;
+    Foo* foo = new Foo(&isDeleted);
+    Foo* foo2 = new Foo(&isDeleted2);
+    sp<Foo> sp1(foo);
+    sp<Foo> sp2(foo2);
+    wp<Foo> wp1(sp1);
+    wp<Foo> wp2(sp1);
+    wp<Foo> wp3(sp2);
+    ASSERT_TRUE(wp1 == wp2);
+    ASSERT_TRUE(wp1 == sp1);
+    ASSERT_TRUE(wp3 == sp2);
+    ASSERT_TRUE(wp1 != sp2);
+    ASSERT_TRUE(wp1 <= wp2);
+    ASSERT_TRUE(wp1 >= wp2);
+    ASSERT_FALSE(wp1 != wp2);
+    ASSERT_FALSE(wp1 > wp2);
+    ASSERT_FALSE(wp1 < wp2);
+    ASSERT_FALSE(sp1 == sp2);
+    ASSERT_TRUE(sp1 != sp2);
+    bool sp1_smaller = sp1 < sp2;
+    wp<Foo>wp_smaller = sp1_smaller ? wp1 : wp3;
+    wp<Foo>wp_larger = sp1_smaller ? wp3 : wp1;
+    ASSERT_TRUE(wp_smaller < wp_larger);
+    ASSERT_TRUE(wp_smaller != wp_larger);
+    ASSERT_TRUE(wp_smaller <= wp_larger);
+    ASSERT_FALSE(wp_smaller == wp_larger);
+    ASSERT_FALSE(wp_smaller > wp_larger);
+    ASSERT_FALSE(wp_smaller >= wp_larger);
+    sp2 = nullptr;
+    ASSERT_TRUE(isDeleted2);
+    ASSERT_FALSE(isDeleted);
+    ASSERT_FALSE(wp3 == sp2);
+    // Comparison results on weak pointers should not be affected.
+    ASSERT_TRUE(wp_smaller < wp_larger);
+    ASSERT_TRUE(wp_smaller != wp_larger);
+    ASSERT_TRUE(wp_smaller <= wp_larger);
+    ASSERT_FALSE(wp_smaller == wp_larger);
+    ASSERT_FALSE(wp_smaller > wp_larger);
+    ASSERT_FALSE(wp_smaller >= wp_larger);
+    wp2 = nullptr;
+    ASSERT_FALSE(wp1 == wp2);
+    ASSERT_TRUE(wp1 != wp2);
+    wp1.clear();
+    ASSERT_TRUE(wp1 == wp2);
+    ASSERT_FALSE(wp1 != wp2);
+    wp3.clear();
+    ASSERT_TRUE(wp1 == wp3);
+    ASSERT_FALSE(wp1 != wp3);
+    ASSERT_FALSE(isDeleted);
+    sp1.clear();
+    ASSERT_TRUE(isDeleted);
+    ASSERT_TRUE(sp1 == sp2);
+    // Try to check that null pointers are properly initialized.
+    {
+        // Try once with non-null, to maximize chances of getting junk on the
+        // stack.
+        sp<Foo> sp3(new Foo(&isDeleted3));
+        wp<Foo> wp4(sp3);
+        wp<Foo> wp5;
+        ASSERT_FALSE(wp4 == wp5);
+        ASSERT_TRUE(wp4 != wp5);
+        ASSERT_FALSE(sp3 == wp5);
+        ASSERT_FALSE(wp5 == sp3);
+        ASSERT_TRUE(sp3 != wp5);
+        ASSERT_TRUE(wp5 != sp3);
+        ASSERT_TRUE(sp3 == wp4);
+    }
+    {
+        sp<Foo> sp3;
+        wp<Foo> wp4(sp3);
+        wp<Foo> wp5;
+        ASSERT_TRUE(wp4 == wp5);
+        ASSERT_FALSE(wp4 != wp5);
+        ASSERT_TRUE(sp3 == wp5);
+        ASSERT_TRUE(wp5 == sp3);
+        ASSERT_FALSE(sp3 != wp5);
+        ASSERT_FALSE(wp5 != sp3);
+        ASSERT_TRUE(sp3 == wp4);
+    }
+}
+
+// Check whether comparison against dead wp works, even if the object referenced
+// by the new wp happens to be at the same address.
+TEST(RefBase, ReplacedComparison) {
+    bool isDeleted, isDeleted2;
+    FooFixedAlloc* foo = new FooFixedAlloc(&isDeleted);
+    sp<FooFixedAlloc> sp1(foo);
+    wp<FooFixedAlloc> wp1(sp1);
+    ASSERT_TRUE(wp1 == sp1);
+    sp1.clear();  // Deallocates the object.
+    ASSERT_TRUE(isDeleted);
+    FooFixedAlloc* foo2 = new FooFixedAlloc(&isDeleted2);
+    ASSERT_FALSE(isDeleted2);
+    ASSERT_EQ(foo, foo2);  // Not technically a legal comparison, but ...
+    sp<FooFixedAlloc> sp2(foo2);
+    wp<FooFixedAlloc> wp2(sp2);
+    ASSERT_TRUE(sp2 == wp2);
+    ASSERT_FALSE(sp2 != wp2);
+    ASSERT_TRUE(sp2 != wp1);
+    ASSERT_FALSE(sp2 == wp1);
+    ASSERT_FALSE(sp2 == sp1);  // sp1 is null.
+    ASSERT_FALSE(wp1 == wp2);  // wp1 refers to old object.
+    ASSERT_TRUE(wp1 != wp2);
+    ASSERT_TRUE(wp1 > wp2 || wp1 < wp2);
+    ASSERT_TRUE(wp1 >= wp2 || wp1 <= wp2);
+    ASSERT_FALSE(wp1 >= wp2 && wp1 <= wp2);
+    ASSERT_FALSE(wp1 == nullptr);
+    wp1 = sp2;
+    ASSERT_TRUE(wp1 == wp2);
+    ASSERT_FALSE(wp1 != wp2);
+}
+
+// Set up a situation in which we race with visit2AndRremove() to delete
+// 2 strong references.  Bar destructor checks that there are no early
+// deletions and prior updates are visible to destructor.
+class Bar : public RefBase {
+public:
+    Bar(std::atomic<int>* delete_count) : mVisited1(false), mVisited2(false),
+            mDeleteCount(delete_count) {
+    }
+
+    ~Bar() {
+        EXPECT_TRUE(mVisited1);
+        EXPECT_TRUE(mVisited2);
+        (*mDeleteCount)++;
+    }
+    bool mVisited1;
+    bool mVisited2;
+private:
+    std::atomic<int>* mDeleteCount;
+};
+
+static sp<Bar> buffer;
+static std::atomic<bool> bufferFull(false);
+
+// Wait until bufferFull has value val.
+static inline void waitFor(bool val) {
+    while (bufferFull != val) {}
+}
+
+cpu_set_t otherCpus;
+
+// Divide the cpus we're allowed to run on into myCpus and otherCpus.
+// Set origCpus to the processors we were originally allowed to run on.
+// Return false if origCpus doesn't include at least processors 0 and 1.
+static bool setExclusiveCpus(cpu_set_t* origCpus /* out */,
+        cpu_set_t* myCpus /* out */, cpu_set_t* otherCpus) {
+    if (sched_getaffinity(0, sizeof(cpu_set_t), origCpus) != 0) {
+        return false;
+    }
+    if (!CPU_ISSET(0,  origCpus) || !CPU_ISSET(1, origCpus)) {
+        return false;
+    }
+    CPU_ZERO(myCpus);
+    CPU_ZERO(otherCpus);
+    CPU_OR(myCpus, myCpus, origCpus);
+    CPU_OR(otherCpus, otherCpus, origCpus);
+    for (unsigned i = 0; i < CPU_SETSIZE; ++i) {
+        // I get the even cores, the other thread gets the odd ones.
+        if (i & 1) {
+            CPU_CLR(i, myCpus);
+        } else {
+            CPU_CLR(i, otherCpus);
+        }
+    }
+    return true;
+}
+
+static void visit2AndRemove() {
+    if (sched_setaffinity(0, sizeof(cpu_set_t), &otherCpus) != 0) {
+        FAIL() << "setaffinity returned:" << errno;
+    }
+    for (int i = 0; i < NITERS; ++i) {
+        waitFor(true);
+        buffer->mVisited2 = true;
+        buffer = nullptr;
+        bufferFull = false;
+    }
+}
+
+TEST(RefBase, RacingDestructors) {
+    cpu_set_t origCpus;
+    cpu_set_t myCpus;
+    // Restrict us and the helper thread to disjoint cpu sets.
+    // This prevents us from getting scheduled against each other,
+    // which would be atrociously slow.
+    if (setExclusiveCpus(&origCpus, &myCpus, &otherCpus)) {
+        std::thread t(visit2AndRemove);
+        std::atomic<int> deleteCount(0);
+        if (sched_setaffinity(0, sizeof(cpu_set_t), &myCpus) != 0) {
+            FAIL() << "setaffinity returned:" << errno;
+        }
+        for (int i = 0; i < NITERS; ++i) {
+            waitFor(false);
+            Bar* bar = new Bar(&deleteCount);
+            sp<Bar> sp3(bar);
+            buffer = sp3;
+            bufferFull = true;
+            ASSERT_TRUE(bar->getStrongCount() >= 1);
+            // Weak count includes strong count.
+            ASSERT_TRUE(bar->getWeakRefs()->getWeakCount() >= 1);
+            sp3->mVisited1 = true;
+            sp3 = nullptr;
+        }
+        t.join();
+        if (sched_setaffinity(0, sizeof(cpu_set_t), &origCpus) != 0) {
+            FAIL();
+        }
+        ASSERT_EQ(NITERS, deleteCount) << "Deletions missed!";
+    }  // Otherwise this is slow and probably pointless on a uniprocessor.
+}
+
+static wp<Bar> wpBuffer;
+static std::atomic<bool> wpBufferFull(false);
+
+// Wait until wpBufferFull has value val.
+static inline void wpWaitFor(bool val) {
+    while (wpBufferFull != val) {}
+}
+
+static void visit3AndRemove() {
+    if (sched_setaffinity(0, sizeof(cpu_set_t), &otherCpus) != 0) {
+        FAIL() << "setaffinity returned:" << errno;
+    }
+    for (int i = 0; i < NITERS; ++i) {
+        wpWaitFor(true);
+        {
+            sp<Bar> sp1 = wpBuffer.promote();
+            // We implicitly check that sp1 != NULL
+            sp1->mVisited2 = true;
+        }
+        wpBuffer = nullptr;
+        wpBufferFull = false;
+    }
+}
+
+TEST(RefBase, RacingPromotions) {
+    cpu_set_t origCpus;
+    cpu_set_t myCpus;
+    // Restrict us and the helper thread to disjoint cpu sets.
+    // This prevents us from getting scheduled against each other,
+    // which would be atrociously slow.
+    if (setExclusiveCpus(&origCpus, &myCpus, &otherCpus)) {
+        std::thread t(visit3AndRemove);
+        std::atomic<int> deleteCount(0);
+        if (sched_setaffinity(0, sizeof(cpu_set_t), &myCpus) != 0) {
+            FAIL() << "setaffinity returned:" << errno;
+        }
+        for (int i = 0; i < NITERS; ++i) {
+            Bar* bar = new Bar(&deleteCount);
+            wp<Bar> wp1(bar);
+            bar->mVisited1 = true;
+            if (i % (NITERS / 10) == 0) {
+                // Do this rarely, since it generates a log message.
+                wp1 = nullptr;  // No longer destroys the object.
+                wp1 = bar;
+            }
+            wpBuffer = wp1;
+            ASSERT_EQ(bar->getWeakRefs()->getWeakCount(), 2);
+            wpBufferFull = true;
+            // Promotion races with that in visit3AndRemove.
+            // This may or may not succeed, but it shouldn't interfere with
+            // the concurrent one.
+            sp<Bar> sp1 = wp1.promote();
+            wpWaitFor(false);  // Waits for other thread to drop strong pointer.
+            sp1 = nullptr;
+            // No strong pointers here.
+            sp1 = wp1.promote();
+            ASSERT_EQ(sp1.get(), nullptr) << "Dead wp promotion succeeded!";
+        }
+        t.join();
+        if (sched_setaffinity(0, sizeof(cpu_set_t), &origCpus) != 0) {
+            FAIL();
+        }
+        ASSERT_EQ(NITERS, deleteCount) << "Deletions missed!";
+    }  // Otherwise this is slow and probably pointless on a uniprocessor.
+}
diff --git a/libutils/SharedBuffer.cpp b/libutils/SharedBuffer.cpp
index bad98b2..7910c6e 100644
--- a/libutils/SharedBuffer.cpp
+++ b/libutils/SharedBuffer.cpp
@@ -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
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/SharedBufferTest.cpp b/libutils/SharedBuffer_test.cpp
similarity index 100%
rename from libutils/SharedBufferTest.cpp
rename to libutils/SharedBuffer_test.cpp
diff --git a/libutils/Singleton_test.cpp b/libutils/Singleton_test.cpp
new file mode 100644
index 0000000..61084b0
--- /dev/null
+++ b/libutils/Singleton_test.cpp
@@ -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.
+ */
+
+#define LOG_TAG "Singleton_test"
+
+#include <dlfcn.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <utils/Singleton.h>
+
+#include <gtest/gtest.h>
+
+#include "Singleton_test.h"
+
+namespace android {
+
+TEST(SingletonTest, bug35674422) {
+    std::string path = android::base::GetExecutableDirectory();
+    // libutils_test_singleton1.so contains the ANDROID_SINGLETON_STATIC_INSTANCE
+    // definition of SingletonTestData, load it first.
+    std::string lib = android::base::StringPrintf("%s/libutils_test_singleton1.so", path.c_str());
+    void* handle1 = dlopen(lib.c_str(), RTLD_NOW);
+    ASSERT_TRUE(handle1 != nullptr) << dlerror();
+
+    // libutils_test_singleton2.so references SingletonTestData but should not
+    // have a definition
+    lib = android::base::StringPrintf("%s/libutils_test_singleton2.so", path.c_str());
+    void* handle2 = dlopen(lib.c_str(), RTLD_NOW);
+    ASSERT_TRUE(handle2 != nullptr) << dlerror();
+
+    using has_fn_t = decltype(&singletonHasInstance);
+    using get_fn_t = decltype(&singletonGetInstanceContents);
+    using set_fn_t = decltype(&singletonSetInstanceContents);
+
+    has_fn_t has1 = reinterpret_cast<has_fn_t>(dlsym(handle1, "singletonHasInstance"));
+    ASSERT_TRUE(has1 != nullptr) << dlerror();
+    has_fn_t has2 = reinterpret_cast<has_fn_t>(dlsym(handle2, "singletonHasInstance"));
+    ASSERT_TRUE(has2 != nullptr) << dlerror();
+    get_fn_t get1 = reinterpret_cast<get_fn_t>(dlsym(handle1, "singletonGetInstanceContents"));
+    ASSERT_TRUE(get1 != nullptr) << dlerror();
+    get_fn_t get2 = reinterpret_cast<get_fn_t>(dlsym(handle2, "singletonGetInstanceContents"));
+    ASSERT_TRUE(get2 != nullptr) << dlerror();
+    set_fn_t set1 = reinterpret_cast<set_fn_t>(dlsym(handle2, "singletonSetInstanceContents"));
+    ASSERT_TRUE(set1 != nullptr) << dlerror();
+
+    EXPECT_FALSE(has1());
+    EXPECT_FALSE(has2());
+    set1(12345678U);
+    EXPECT_TRUE(has1());
+    EXPECT_TRUE(has2());
+    EXPECT_EQ(12345678U, get1());
+    EXPECT_EQ(12345678U, get2());
+}
+
+}
diff --git a/libutils/tests/Singleton_test.h b/libutils/Singleton_test.h
similarity index 100%
rename from libutils/tests/Singleton_test.h
rename to libutils/Singleton_test.h
diff --git a/libutils/tests/Singleton_test1.cpp b/libutils/Singleton_test1.cpp
similarity index 100%
rename from libutils/tests/Singleton_test1.cpp
rename to libutils/Singleton_test1.cpp
diff --git a/libutils/tests/Singleton_test2.cpp b/libutils/Singleton_test2.cpp
similarity index 100%
rename from libutils/tests/Singleton_test2.cpp
rename to libutils/Singleton_test2.cpp
diff --git a/libutils/Static.cpp b/libutils/Static.cpp
deleted file mode 100644
index 3ed07a1..0000000
--- a/libutils/Static.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// All static variables go here, to control initialization and
-// destruction order in the library.
-
-namespace android {
-
-// For String8.cpp
-extern void initialize_string8();
-extern void terminate_string8();
-
-// For String16.cpp
-extern void initialize_string16();
-extern void terminate_string16();
-
-class LibUtilsFirstStatics
-{
-public:
-    LibUtilsFirstStatics()
-    {
-        initialize_string8();
-        initialize_string16();
-    }
-    
-    ~LibUtilsFirstStatics()
-    {
-        terminate_string16();
-        terminate_string8();
-    }
-};
-
-static LibUtilsFirstStatics gFirstStatics;
-int gDarwinCantLoadAllObjects = 1;
-
-}   // namespace android
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index e8f1c51..818b171 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -24,29 +24,16 @@
 
 namespace android {
 
-static SharedBuffer* gEmptyStringBuf = NULL;
-static char16_t* gEmptyString = NULL;
+static inline char16_t* getEmptyString() {
+    static SharedBuffer* gEmptyStringBuf = [] {
+        SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t));
+        char16_t* str = static_cast<char16_t*>(buf->data());
+        *str = 0;
+        return buf;
+    }();
 
-static inline char16_t* getEmptyString()
-{
     gEmptyStringBuf->acquire();
-   return gEmptyString;
-}
-
-void initialize_string16()
-{
-    SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t));
-    char16_t* str = (char16_t*)buf->data();
-    *str = 0;
-    gEmptyStringBuf = buf;
-    gEmptyString = str;
-}
-
-void terminate_string16()
-{
-    SharedBuffer::bufferFromData(gEmptyString)->release();
-    gEmptyStringBuf = NULL;
-    gEmptyString = NULL;
+    return static_cast<char16_t*>(gEmptyStringBuf->data());
 }
 
 // ---------------------------------------------------------------------------
@@ -104,7 +91,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
@@ -170,12 +157,12 @@
     if (begin >= N) {
         SharedBuffer::bufferFromData(mString)->release();
         mString = getEmptyString();
-        return NO_ERROR;
+        return OK;
     }
     if ((begin+len) > N) len = N-begin;
     if (begin == 0 && len == N) {
         setTo(other);
-        return NO_ERROR;
+        return OK;
     }
 
     if (&other == this) {
@@ -204,7 +191,7 @@
         memmove(str, other, len*sizeof(char16_t));
         str[len] = 0;
         mString = str;
-        return NO_ERROR;
+        return OK;
     }
     return NO_MEMORY;
 }
@@ -215,9 +202,9 @@
     const size_t otherLen = other.size();
     if (myLen == 0) {
         setTo(other);
-        return NO_ERROR;
+        return OK;
     } else if (otherLen == 0) {
-        return NO_ERROR;
+        return OK;
     }
 
     if (myLen >= SIZE_MAX / sizeof(char16_t) - otherLen) {
@@ -231,7 +218,7 @@
         char16_t* str = (char16_t*)buf->data();
         memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t));
         mString = str;
-        return NO_ERROR;
+        return OK;
     }
     return NO_MEMORY;
 }
@@ -241,9 +228,9 @@
     const size_t myLen = size();
     if (myLen == 0) {
         setTo(chrs, otherLen);
-        return NO_ERROR;
+        return OK;
     } else if (otherLen == 0) {
-        return NO_ERROR;
+        return OK;
     }
 
     if (myLen >= SIZE_MAX / sizeof(char16_t) - otherLen) {
@@ -258,7 +245,7 @@
         memcpy(str+myLen, chrs, otherLen*sizeof(char16_t));
         str[myLen+otherLen] = 0;
         mString = str;
-        return NO_ERROR;
+        return OK;
     }
     return NO_MEMORY;
 }
@@ -273,9 +260,9 @@
     const size_t myLen = size();
     if (myLen == 0) {
         return setTo(chrs, len);
-        return NO_ERROR;
+        return OK;
     } else if (len == 0) {
-        return NO_ERROR;
+        return OK;
     }
 
     if (pos > myLen) pos = myLen;
@@ -299,7 +286,7 @@
         #if 0
         printf("Result (%d chrs): %s\n", size(), String8(*this).string());
         #endif
-        return NO_ERROR;
+        return OK;
     }
     return NO_MEMORY;
 }
@@ -355,7 +342,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') {
@@ -370,14 +357,14 @@
             edit[i] = tolower((char)v);
         }
     }
-    return NO_ERROR;
+    return OK;
 }
 
 status_t String16::replaceAll(char16_t replaceThis, char16_t withThis)
 {
     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) {
@@ -391,7 +378,7 @@
             edit[i] = withThis;
         }
     }
-    return NO_ERROR;
+    return OK;
 }
 
 status_t String16::remove(size_t len, size_t begin)
@@ -400,11 +387,11 @@
     if (begin >= N) {
         SharedBuffer::bufferFromData(mString)->release();
         mString = getEmptyString();
-        return NO_ERROR;
+        return OK;
     }
     if ((begin+len) > N) len = N-begin;
     if (begin == 0 && len == N) {
-        return NO_ERROR;
+        return OK;
     }
 
     if (begin > 0) {
@@ -423,7 +410,7 @@
         char16_t* str = (char16_t*)buf->data();
         str[len] = 0;
         mString = str;
-        return NO_ERROR;
+        return OK;
     }
     return NO_MEMORY;
 }
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index ad0e72e..d13548e 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -40,40 +40,16 @@
 // to OS_PATH_SEPARATOR.
 #define RES_PATH_SEPARATOR '/'
 
-static SharedBuffer* gEmptyStringBuf = NULL;
-static char* gEmptyString = NULL;
+static inline char* getEmptyString() {
+    static SharedBuffer* gEmptyStringBuf = [] {
+        SharedBuffer* buf = SharedBuffer::alloc(1);
+        char* str = static_cast<char*>(buf->data());
+        *str = 0;
+        return buf;
+    }();
 
-extern int gDarwinCantLoadAllObjects;
-int gDarwinIsReallyAnnoying;
-
-void initialize_string8();
-
-static inline char* getEmptyString()
-{
     gEmptyStringBuf->acquire();
-    return gEmptyString;
-}
-
-void initialize_string8()
-{
-    // HACK: This dummy dependency forces linking libutils Static.cpp,
-    // which is needed to initialize String8/String16 classes.
-    // These variables are named for Darwin, but are needed elsewhere too,
-    // including static linking on any platform.
-    gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects;
-
-    SharedBuffer* buf = SharedBuffer::alloc(1);
-    char* str = (char*)buf->data();
-    *str = 0;
-    gEmptyStringBuf = buf;
-    gEmptyString = str;
-}
-
-void terminate_string8()
-{
-    SharedBuffer::bufferFromData(gEmptyString)->release();
-    gEmptyStringBuf = NULL;
-    gEmptyString = NULL;
+    return static_cast<char*>(gEmptyStringBuf->data());
 }
 
 // ---------------------------------------------------------------------------
@@ -82,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");
@@ -92,7 +68,7 @@
             str[len] = 0;
             return str;
         }
-        return NULL;
+        return nullptr;
     }
 
     return getEmptyString();
@@ -150,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
@@ -171,7 +147,7 @@
 String8::String8(const char* o)
     : mString(allocFromUTF8(o, strlen(o)))
 {
-    if (mString == NULL) {
+    if (mString == nullptr) {
         mString = getEmptyString();
     }
 }
@@ -179,7 +155,7 @@
 String8::String8(const char* o, size_t len)
     : mString(allocFromUTF8(o, len))
 {
-    if (mString == NULL) {
+    if (mString == nullptr) {
         mString = getEmptyString();
     }
 }
@@ -254,7 +230,7 @@
     const char *newString = allocFromUTF8(other, strlen(other));
     SharedBuffer::bufferFromData(mString)->release();
     mString = newString;
-    if (mString) return NO_ERROR;
+    if (mString) return OK;
 
     mString = getEmptyString();
     return NO_MEMORY;
@@ -265,7 +241,7 @@
     const char *newString = allocFromUTF8(other, len);
     SharedBuffer::bufferFromData(mString)->release();
     mString = newString;
-    if (mString) return NO_ERROR;
+    if (mString) return OK;
 
     mString = getEmptyString();
     return NO_MEMORY;
@@ -276,7 +252,7 @@
     const char *newString = allocFromUTF16(other, len);
     SharedBuffer::bufferFromData(mString)->release();
     mString = newString;
-    if (mString) return NO_ERROR;
+    if (mString) return OK;
 
     mString = getEmptyString();
     return NO_MEMORY;
@@ -287,7 +263,7 @@
     const char *newString = allocFromUTF32(other, len);
     SharedBuffer::bufferFromData(mString)->release();
     mString = newString;
-    if (mString) return NO_ERROR;
+    if (mString) return OK;
 
     mString = getEmptyString();
     return NO_MEMORY;
@@ -298,9 +274,9 @@
     const size_t otherLen = other.bytes();
     if (bytes() == 0) {
         setTo(other);
-        return NO_ERROR;
+        return OK;
     } else if (otherLen == 0) {
-        return NO_ERROR;
+        return OK;
     }
 
     return real_append(other.string(), otherLen);
@@ -316,7 +292,7 @@
     if (bytes() == 0) {
         return setTo(other, otherLen);
     } else if (otherLen == 0) {
-        return NO_ERROR;
+        return OK;
     }
 
     return real_append(other, otherLen);
@@ -335,7 +311,7 @@
 
 status_t String8::appendFormatV(const char* fmt, va_list args)
 {
-    int n, result = NO_ERROR;
+    int n, result = OK;
     va_list tmp_args;
 
     /* args is undefined after vsnprintf.
@@ -343,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) {
@@ -370,7 +346,7 @@
         str += myLen;
         memcpy(str, other, otherLen);
         str[otherLen] = '\0';
-        return NO_ERROR;
+        return OK;
     }
     return NO_MEMORY;
 }
@@ -384,7 +360,7 @@
         mString = str;
         return str;
     }
-    return NULL;
+    return nullptr;
 }
 
 void String8::unlockBuffer()
@@ -406,7 +382,7 @@
         mString = str;
     }
 
-    return NO_ERROR;
+    return OK;
 }
 
 ssize_t String8::find(const char* other, size_t start) const
@@ -492,21 +468,6 @@
     unlockBuffer(len);
 }
 
-size_t String8::getUtf32Length() const
-{
-    return utf8_to_utf32_length(mString, length());
-}
-
-int32_t String8::getUtf32At(size_t index, size_t *next_index) const
-{
-    return utf32_from_utf8_at(mString, length(), index, next_index);
-}
-
-void String8::getUtf32(char32_t* dst) const
-{
-    utf8_to_utf32(mString, length(), dst);
-}
-
 // ---------------------------------------------------------------------------
 // Path functions
 
@@ -536,7 +497,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);
@@ -548,7 +509,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);
@@ -567,7 +528,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;
@@ -591,15 +552,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);
@@ -610,7 +571,7 @@
     char* ext;
 
     ext = find_extension();
-    if (ext != NULL)
+    if (ext != nullptr)
         return String8(ext);
     else
         return String8("");
@@ -622,7 +583,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/tests/String8_test.cpp b/libutils/String8_test.cpp
similarity index 100%
rename from libutils/tests/String8_test.cpp
rename to libutils/String8_test.cpp
diff --git a/libutils/tests/StrongPointer_test.cpp b/libutils/StrongPointer_test.cpp
similarity index 100%
rename from libutils/tests/StrongPointer_test.cpp
rename to libutils/StrongPointer_test.cpp
diff --git a/libutils/tests/SystemClock_test.cpp b/libutils/SystemClock_test.cpp
similarity index 100%
rename from libutils/tests/SystemClock_test.cpp
rename to libutils/SystemClock_test.cpp
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 7d7f0e2..31ca138 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -36,7 +36,7 @@
 
 #include <utils/Log.h>
 
-#include <cutils/sched_policy.h>
+#include <processgroup/sched_policy.h>
 
 #if defined(__ANDROID__)
 # define __android_unused
@@ -163,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;
@@ -379,7 +379,7 @@
 {
     DWORD dwWaitResult;
     dwWaitResult = WaitForSingleObject((HANDLE) mState, INFINITE);
-    return dwWaitResult != WAIT_OBJECT_0 ? -1 : NO_ERROR;
+    return dwWaitResult != WAIT_OBJECT_0 ? -1 : OK;
 }
 
 void Mutex::unlock()
@@ -506,7 +506,7 @@
         ReleaseMutex(condState->internalMutex);
         WaitForSingleObject(hMutex, INFINITE);
 
-        return res == WAIT_OBJECT_0 ? NO_ERROR : -1;
+        return res == WAIT_OBJECT_0 ? OK : -1;
     }
 } WinCondition;
 
@@ -639,13 +639,15 @@
  */
 
 Thread::Thread(bool canCallJava)
-    :   mCanCallJava(canCallJava),
-        mThread(thread_id_t(-1)),
-        mLock("Thread::mLock"),
-        mStatus(NO_ERROR),
-        mExitPending(false), mRunning(false)
+    : mCanCallJava(canCallJava),
+      mThread(thread_id_t(-1)),
+      mLock("Thread::mLock"),
+      mStatus(OK),
+      mExitPending(false),
+      mRunning(false)
 #if defined(__ANDROID__)
-        , mTid(-1)
+      ,
+      mTid(-1)
 #endif
 {
 }
@@ -656,7 +658,7 @@
 
 status_t Thread::readyToRun()
 {
-    return NO_ERROR;
+    return OK;
 }
 
 status_t Thread::run(const char* name, int32_t priority, size_t stack)
@@ -672,7 +674,7 @@
 
     // reset status and exitPending to their default value, so we can
     // try again after an error happened (either below, or in readyToRun())
-    mStatus = NO_ERROR;
+    mStatus = OK;
     mExitPending = false;
     mThread = thread_id_t(-1);
 
@@ -700,10 +702,10 @@
     }
 
     // Do not refer to mStatus here: The thread is already running (may, in fact
-    // already have exited with a valid mStatus result). The NO_ERROR indication
+    // already have exited with a valid mStatus result). The OK indication
     // here merely indicates successfully starting the thread and does not
     // imply successful termination/execution.
-    return NO_ERROR;
+    return OK;
 
     // Exiting scope of mLock is a memory barrier and allows new thread to run
 }
@@ -728,7 +730,7 @@
         if (first) {
             first = false;
             self->mStatus = self->readyToRun();
-            result = (self->mStatus == NO_ERROR);
+            result = (self->mStatus == OK);
 
             if (result && !self->exitPending()) {
                 // Binder threads (and maybe others) rely on threadLoop
@@ -768,7 +770,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 b2df9a5..c3641ef 100644
--- a/libutils/Timers.cpp
+++ b/libutils/Timers.cpp
@@ -45,7 +45,7 @@
     // 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
diff --git a/libutils/Tokenizer.cpp b/libutils/Tokenizer.cpp
index b68a2cf..98dd2fd 100644
--- a/libutils/Tokenizer.cpp
+++ b/libutils/Tokenizer.cpp
@@ -28,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,
@@ -46,9 +46,9 @@
 }
 
 status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {
-    *outTokenizer = NULL;
+    *outTokenizer = nullptr;
 
-    int result = NO_ERROR;
+    int result = OK;
     int fd = ::open(filename.string(), O_RDONLY);
     if (fd < 0) {
         result = -errno;
@@ -64,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)
@@ -81,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);
                 }
@@ -98,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/Unicode.cpp b/libutils/Unicode.cpp
index 1086831..24a745a 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -16,8 +16,9 @@
 
 #define LOG_TAG "unicode"
 
-#include <utils/Unicode.h>
+#include <android-base/macros.h>
 #include <limits.h>
+#include <utils/Unicode.h>
 
 #include <log/log.h>
 
@@ -105,8 +106,11 @@
     switch (bytes)
     {   /* note: everything falls through. */
         case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+            FALLTHROUGH_INTENDED;
         case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+            FALLTHROUGH_INTENDED;
         case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+            FALLTHROUGH_INTENDED;
         case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]);
     }
 }
@@ -159,7 +163,7 @@
         return -1;
     }
     size_t dummy_index;
-    if (next_index == NULL) {
+    if (next_index == nullptr) {
         next_index = &dummy_index;
     }
     size_t num_read;
@@ -173,7 +177,7 @@
 
 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;
     }
 
@@ -195,7 +199,7 @@
 
 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;
     }
 
@@ -271,25 +275,6 @@
   return ss-s;
 }
 
-
-char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n)
-{
-  char16_t *q = dst;
-  const char16_t *p = src;
-  char ch;
-
-  while (n) {
-    n--;
-    *q++ = ch = *p++;
-    if ( !ch )
-      break;
-  }
-
-  *q = 0;
-
-  return dst;
-}
-
 size_t strnlen16(const char16_t *s, size_t maxlen)
 {
   const char16_t *ss = s;
@@ -340,30 +325,9 @@
            : 0);
 }
 
-int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2)
-{
-    const char16_t* e1 = s1H+n1;
-    const char16_t* e2 = s2N+n2;
-
-    while (s1H < e1 && s2N < e2) {
-        const char16_t c2 = ntohs(*s2N);
-        const int d = (int)*s1H++ - (int)c2;
-        s2N++;
-        if (d) {
-            return d;
-        }
-    }
-
-    return n1 < n2
-        ? (0 - (int)ntohs(*s2N))
-        : (n1 > n2
-           ? ((int)*s1H - 0)
-           : 0);
-}
-
 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;
     }
 
@@ -440,7 +404,7 @@
 
 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;
     }
 
@@ -490,7 +454,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;
@@ -515,7 +479,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/tests/Unicode_test.cpp b/libutils/Unicode_test.cpp
similarity index 100%
rename from libutils/tests/Unicode_test.cpp
rename to libutils/Unicode_test.cpp
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
index ef3277f..c97a19b 100644
--- a/libutils/VectorImpl.cpp
+++ b/libutils/VectorImpl.cpp
@@ -24,8 +24,6 @@
 
 #include <log/log.h>
 
-#include <safe_iop.h>
-
 #include "SharedBuffer.h"
 
 /*****************************************************************************/
@@ -44,7 +42,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)
 {
 }
 
@@ -63,7 +61,7 @@
         "[%p] subclasses of VectorImpl must call finish_vector()"
         " in their destructor. Leaking %d bytes.",
         this, (int)(mCount*mItemSize));
-    // We can't call _do_destroy() here because the vtable is already gone. 
+    // We can't call _do_destroy() here because the vtable is already gone.
 }
 
 VectorImpl& VectorImpl::operator = (const VectorImpl& rhs)
@@ -77,7 +75,7 @@
             mCount = rhs.mCount;
             SharedBuffer::bufferFromData(mStorage)->acquire();
         } else {
-            mStorage = 0;
+            mStorage = nullptr;
             mCount = 0;
         }
     }
@@ -89,14 +87,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 +139,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 +175,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);
@@ -199,13 +197,13 @@
                 _do_copy(temp, item, 1);
 
                 ssize_t j = i-1;
-                void* next = reinterpret_cast<char*>(array) + mItemSize*(i);                    
+                void* next = reinterpret_cast<char*>(array) + mItemSize*(i);
                 do {
                     _do_destroy(next, 1);
                     _do_copy(next, curr, 1);
                     next = curr;
                     --j;
-                    curr = NULL;
+                    curr = nullptr;
                     if (j >= 0) {
                         curr = reinterpret_cast<char*>(array) + mItemSize*(j);
                     }
@@ -216,13 +214,13 @@
             }
             i++;
         }
-        
+
         if (temp) {
             _do_destroy(temp, 1);
             free(temp);
         }
     }
-    return NO_ERROR;
+    return OK;
 }
 
 void VectorImpl::pop()
@@ -233,7 +231,7 @@
 
 void VectorImpl::push()
 {
-    push(0);
+    push(nullptr);
 }
 
 void VectorImpl::push(const void* item)
@@ -243,7 +241,7 @@
 
 ssize_t VectorImpl::add()
 {
-    return add(0);
+    return add(nullptr);
 }
 
 ssize_t VectorImpl::add(const void* item)
@@ -253,7 +251,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 +265,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 +292,7 @@
 void VectorImpl::finish_vector()
 {
     release_storage();
-    mStorage = 0;
+    mStorage = nullptr;
     mCount = 0;
 }
 
@@ -315,7 +313,7 @@
             return reinterpret_cast<char*>(buffer) + index*mItemSize;
         }
     }
-    return 0;
+    return nullptr;
 }
 
 const void* VectorImpl::itemLocation(size_t index) const
@@ -330,7 +328,7 @@
             return reinterpret_cast<const char*>(buffer) + index*mItemSize;
         }
     }
-    return 0;
+    return nullptr;
 }
 
 ssize_t VectorImpl::setCapacity(size_t new_capacity)
@@ -342,7 +340,7 @@
     }
 
     size_t new_allocation_size = 0;
-    LOG_ALWAYS_FATAL_IF(!safe_mul(&new_allocation_size, new_capacity, mItemSize));
+    LOG_ALWAYS_FATAL_IF(__builtin_mul_overflow(new_capacity, mItemSize, &new_allocation_size));
     SharedBuffer* sb = SharedBuffer::alloc(new_allocation_size);
     if (sb) {
         void* array = sb->data();
@@ -356,7 +354,7 @@
 }
 
 ssize_t VectorImpl::resize(size_t size) {
-    ssize_t result = NO_ERROR;
+    ssize_t result = OK;
     if (size > mCount) {
         result = insertAt(mCount, size - mCount);
     } else if (size < mCount) {
@@ -372,7 +370,7 @@
         if (sb->release(SharedBuffer::eKeepStorage) == 1) {
             _do_destroy(mStorage, mCount);
             SharedBuffer::dealloc(sb);
-        } 
+        }
     }
 }
 
@@ -386,7 +384,7 @@
             this, (int)where, (int)amount, (int)mCount); // caller already checked
 
     size_t new_size;
-    LOG_ALWAYS_FATAL_IF(!safe_add(&new_size, mCount, amount), "new_size overflow");
+    LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(mCount, amount, &new_size), "new_size overflow");
 
     if (capacity() < new_size) {
         // NOTE: This implementation used to resize vectors as per ((3*x + 1) / 2)
@@ -397,17 +395,18 @@
         //
         // This approximates the old calculation, using (x + (x/2) + 1) instead.
         size_t new_capacity = 0;
-        LOG_ALWAYS_FATAL_IF(!safe_add(&new_capacity, new_size, (new_size / 2)),
+        LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(new_size, (new_size / 2), &new_capacity),
                             "new_capacity overflow");
-        LOG_ALWAYS_FATAL_IF(!safe_add(&new_capacity, new_capacity, static_cast<size_t>(1u)),
-                            "new_capacity overflow");
+        LOG_ALWAYS_FATAL_IF(
+                __builtin_add_overflow(new_capacity, static_cast<size_t>(1u), &new_capacity),
+                "new_capacity overflow");
         new_capacity = max(kMinVectorCapacity, new_capacity);
 
         size_t new_alloc_size = 0;
-        LOG_ALWAYS_FATAL_IF(!safe_mul(&new_alloc_size, new_capacity, mItemSize),
+        LOG_ALWAYS_FATAL_IF(__builtin_mul_overflow(new_capacity, mItemSize, &new_alloc_size),
                             "new_alloc_size overflow");
 
-//        ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity);
+        // ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity);
         if ((mStorage) &&
             (mCount==where) &&
             (mFlags & HAS_TRIVIAL_COPY) &&
@@ -418,7 +417,7 @@
             if (sb) {
                 mStorage = sb->data();
             } else {
-                return NULL;
+                return nullptr;
             }
         } else {
             SharedBuffer* sb = SharedBuffer::alloc(new_alloc_size);
@@ -435,7 +434,7 @@
                 release_storage();
                 mStorage = const_cast<void*>(array);
             } else {
-                return NULL;
+                return nullptr;
             }
         }
     } else {
@@ -464,7 +463,7 @@
             this, (int)where, (int)amount, (int)mCount); // caller already checked
 
     size_t new_size;
-    LOG_ALWAYS_FATAL_IF(!safe_sub(&new_size, mCount, amount));
+    LOG_ALWAYS_FATAL_IF(__builtin_sub_overflow(mCount, amount, &new_size));
 
     if (new_size < (capacity() / 2)) {
         // NOTE: (new_size * 2) is safe because capacity didn't overflow and
@@ -645,13 +644,13 @@
             }
         }
     }
-    return NO_ERROR;
+    return OK;
 }
 
 ssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector)
 {
     // we've merging a sorted vector... nice!
-    ssize_t err = NO_ERROR;
+    ssize_t err = OK;
     if (!vector.isEmpty()) {
         // first take care of the case where the vectors are sorted together
         if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) {
@@ -678,4 +677,3 @@
 /*****************************************************************************/
 
 }; // namespace android
-
diff --git a/libutils/Vector_test.cpp b/libutils/Vector_test.cpp
new file mode 100644
index 0000000..5336c40
--- /dev/null
+++ b/libutils/Vector_test.cpp
@@ -0,0 +1,139 @@
+/*
+ * 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 "Vector_test"
+
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+#include <unistd.h>
+
+#include <android/log.h>
+#include <gtest/gtest.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class VectorTest : public testing::Test {
+protected:
+    virtual void SetUp() {
+    }
+
+    virtual void TearDown() {
+    }
+
+public:
+};
+
+
+TEST_F(VectorTest, CopyOnWrite_CopyAndAddElements) {
+
+    Vector<int> vector;
+    Vector<int> other;
+    vector.setCapacity(8);
+
+    vector.add(1);
+    vector.add(2);
+    vector.add(3);
+
+    EXPECT_EQ(3U, vector.size());
+
+    // copy the vector
+    other = vector;
+
+    EXPECT_EQ(3U, other.size());
+
+    // add an element to the first vector
+    vector.add(4);
+
+    // make sure the sizes are correct
+    EXPECT_EQ(4U, vector.size());
+    EXPECT_EQ(3U, other.size());
+
+    // add an element to the copy
+    other.add(5);
+
+    // make sure the sizes are correct
+    EXPECT_EQ(4U, vector.size());
+    EXPECT_EQ(4U, other.size());
+
+    // make sure the content of both vectors are correct
+    EXPECT_EQ(vector[3], 4);
+    EXPECT_EQ(other[3], 5);
+}
+
+TEST_F(VectorTest, SetCapacity_Overflow) {
+  Vector<int> vector;
+  EXPECT_DEATH(vector.setCapacity(SIZE_MAX / sizeof(int) + 1), "Assertion failed");
+}
+
+TEST_F(VectorTest, SetCapacity_ShrinkBelowSize) {
+  Vector<int> vector;
+  vector.add(1);
+  vector.add(2);
+  vector.add(3);
+  vector.add(4);
+
+  vector.setCapacity(8);
+  ASSERT_EQ(8U, vector.capacity());
+  vector.setCapacity(2);
+  ASSERT_EQ(8U, vector.capacity());
+}
+
+TEST_F(VectorTest, _grow_OverflowSize) {
+  Vector<int> vector;
+  vector.add(1);
+
+  // Checks that the size calculation (not the capacity calculation) doesn't
+  // overflow : the size here will be (1 + SIZE_MAX).
+  EXPECT_DEATH(vector.insertArrayAt(nullptr, 0, SIZE_MAX), "new_size overflow");
+}
+
+TEST_F(VectorTest, _grow_OverflowCapacityDoubling) {
+  Vector<int> vector;
+
+  // This should fail because the calculated capacity will overflow even though
+  // the size of the vector doesn't.
+  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(nullptr, 0, (SIZE_MAX / 2)), "new_alloc_size overflow");
+}
+
+TEST_F(VectorTest, editArray_Shared) {
+  Vector<int> vector1;
+  vector1.add(1);
+  vector1.add(2);
+  vector1.add(3);
+  vector1.add(4);
+
+  Vector<int> vector2 = vector1;
+  ASSERT_EQ(vector1.array(), vector2.array());
+  // We must make a copy here, since we're not the exclusive owners
+  // of this array.
+  ASSERT_NE(vector1.editArray(), vector2.editArray());
+
+  // Vector doesn't implement operator ==.
+  ASSERT_EQ(vector1.size(), vector2.size());
+  for (size_t i = 0; i < vector1.size(); ++i) {
+    EXPECT_EQ(vector1[i], vector2[i]);
+  }
+}
+
+} // namespace android
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/CallStack.h b/libutils/include/utils/CallStack.h
index 27e89f4..7a4a345 100644
--- a/libutils/include/utils/CallStack.h
+++ b/libutils/include/utils/CallStack.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_CALLSTACK_H
 #define ANDROID_CALLSTACK_H
 
+#include <memory>
+
 #include <android/log.h>
 #include <backtrace/backtrace_constants.h>
 #include <utils/String8.h>
@@ -25,6 +27,19 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#if !defined(__APPLE__) && !defined(_WIN32)
+# define WEAKS_AVAILABLE 1
+#endif
+#ifndef CALLSTACK_WEAK
+# ifdef WEAKS_AVAILABLE
+#   define CALLSTACK_WEAK __attribute__((weak))
+# else // !WEAKS_AVAILABLE
+#   define CALLSTACK_WEAK
+# endif // !WEAKS_AVAILABLE
+#endif // CALLSTACK_WEAK predefined
+
+#define ALWAYS_INLINE __attribute__((always_inline))
+
 namespace android {
 
 class Printer;
@@ -36,7 +51,7 @@
     CallStack();
     // Create a callstack with the current thread's stack trace.
     // Immediately dump it to logcat using the given logtag.
-    CallStack(const char* logtag, int32_t ignoreDepth=1);
+    CallStack(const char* logtag, int32_t ignoreDepth = 1);
     ~CallStack();
 
     // Reset the stack frames (same as creating an empty call stack).
@@ -44,18 +59,18 @@
 
     // Immediately collect the stack traces for the specified thread.
     // The default is to dump the stack of the current call.
-    void update(int32_t ignoreDepth=1, pid_t tid=BACKTRACE_CURRENT_THREAD);
+    void update(int32_t ignoreDepth = 1, pid_t tid = BACKTRACE_CURRENT_THREAD);
 
     // 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;
@@ -63,10 +78,92 @@
     // Get the count of stack frames that are in this call stack.
     size_t size() const { return mFrameLines.size(); }
 
-private:
+    // DO NOT USE ANYTHING BELOW HERE. The following public members are expected
+    // to disappear again shortly, once a better replacement facility exists.
+    // The replacement facility will be incompatible!
+
+    // Debugging accesses to some basic functionality. These use weak symbols to
+    // avoid introducing a dependency on libutilscallstack. Such a dependency from
+    // libutils results in a cyclic build dependency. These routines can be called
+    // from within libutils. But if the actual library is unavailable, they do
+    // nothing.
+    //
+    // DO NOT USE THESE. They will disappear.
+    struct StackDeleter {
+#ifdef WEAKS_AVAILABLE
+        void operator()(CallStack* stack) {
+            deleteStack(stack);
+        }
+#else
+        void operator()(CallStack*) {}
+#endif
+    };
+
+    typedef std::unique_ptr<CallStack, StackDeleter> CallStackUPtr;
+
+    // Return current call stack if possible, nullptr otherwise.
+#ifdef WEAKS_AVAILABLE
+    static CallStackUPtr ALWAYS_INLINE getCurrent(int32_t ignoreDepth = 1) {
+        if (reinterpret_cast<uintptr_t>(getCurrentInternal) == 0) {
+            ALOGW("CallStack::getCurrentInternal not linked, returning null");
+            return CallStackUPtr(nullptr);
+        } else {
+            return getCurrentInternal(ignoreDepth);
+        }
+    }
+#else // !WEAKS_AVAILABLE
+    static CallStackUPtr ALWAYS_INLINE getCurrent(int32_t = 1) {
+        return CallStackUPtr(nullptr);
+    }
+#endif // !WEAKS_AVAILABLE
+
+#ifdef WEAKS_AVAILABLE
+    static void ALWAYS_INLINE logStack(const char* logtag, CallStack* stack = getCurrent().get(),
+                                       android_LogPriority priority = ANDROID_LOG_DEBUG) {
+        if (reinterpret_cast<uintptr_t>(logStackInternal) != 0 && stack != nullptr) {
+            logStackInternal(logtag, stack, priority);
+        } else {
+            ALOG(LOG_WARN, logtag, "CallStack::logStackInternal not linked");
+        }
+    }
+
+#else
+    static void ALWAYS_INLINE logStack(const char* logtag, CallStack* = getCurrent().get(),
+                                       android_LogPriority = ANDROID_LOG_DEBUG) {
+        ALOG(LOG_WARN, logtag, "CallStack::logStackInternal not linked");
+    }
+#endif // !WEAKS_AVAILABLE
+
+#ifdef WEAKS_AVAILABLE
+    static String8 ALWAYS_INLINE stackToString(const char* prefix = nullptr,
+                                               const CallStack* stack = getCurrent().get()) {
+        if (reinterpret_cast<uintptr_t>(stackToStringInternal) != 0 && stack != nullptr) {
+            return stackToStringInternal(prefix, stack);
+        } else {
+            return String8::format("%s<CallStack package not linked>", (prefix ? prefix : ""));
+        }
+    }
+#else // !WEAKS_AVAILABLE
+    static String8 ALWAYS_INLINE stackToString(const char* prefix = nullptr,
+                                               const CallStack* = getCurrent().get()) {
+        return String8::format("%s<CallStack package not linked>", (prefix ? prefix : ""));
+    }
+#endif // !WEAKS_AVAILABLE
+
+  private:
+#ifdef WEAKS_AVAILABLE
+    static CallStackUPtr CALLSTACK_WEAK getCurrentInternal(int32_t ignoreDepth);
+    static void CALLSTACK_WEAK logStackInternal(const char* logtag, const CallStack* stack,
+                                                android_LogPriority priority);
+    static String8 CALLSTACK_WEAK stackToStringInternal(const char* prefix, const CallStack* stack);
+    // The deleter is only invoked on non-null pointers. Hence it will never be
+    // invoked if CallStack is not linked.
+    static void CALLSTACK_WEAK deleteStack(CallStack* stack);
+#endif // WEAKS_AVAILABLE
+
     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 9bf82eb..540ed82 100644
--- a/libutils/include/utils/Condition.h
+++ b/libutils/include/utils/Condition.h
@@ -124,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
@@ -159,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 4cbb462..96bd70e 100644
--- a/libutils/include/utils/Debug.h
+++ b/libutils/include/utils/Debug.h
@@ -35,6 +35,6 @@
     CompileTimeAssert<( _exp )>();
 
 // ---------------------------------------------------------------------------
-}; // 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..1e03677 100644
--- a/libutils/include/utils/Errors.h
+++ b/libutils/include/utils/Errors.h
@@ -14,22 +14,19 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_ERRORS_H
-#define ANDROID_ERRORS_H
+#pragma once
 
-#include <sys/types.h>
 #include <errno.h>
+#include <stdint.h>
+#include <sys/types.h>
 
 namespace android {
 
-// use this type to return error codes
-#ifdef _WIN32
-typedef int         status_t;
-#else
-typedef int32_t     status_t;
-#endif
-
-/* the MS C runtime lacks a few error codes */
+/**
+ * The type used to return success/failure from frameworks APIs.
+ * See the anonymous enum below for valid values.
+ */
+typedef int32_t status_t;
 
 /*
  * Error codes. 
@@ -43,8 +40,8 @@
 #endif
 
 enum {
-    OK                = 0,    // Everything's swell.
-    NO_ERROR          = 0,    // No errors.
+    OK                = 0,    // Preferred constant for checking success.
+    NO_ERROR          = OK,   // Deprecated synonym for `OK`. Prefer `OK` because it doesn't conflict with Windows.
 
     UNKNOWN_ERROR       = (-2147483647-1), // INT32_MIN value
 
@@ -81,8 +78,4 @@
 # define NO_ERROR 0L
 #endif
 
-}; // namespace android
-    
-// ---------------------------------------------------------------------------
-    
-#endif // ANDROID_ERRORS_H
+}  // namespace android
diff --git a/libutils/include/utils/FileMap.h b/libutils/include/utils/FileMap.h
index 7d372e1..f9f8f3c 100644
--- a/libutils/include/utils/FileMap.h
+++ b/libutils/include/utils/FileMap.h
@@ -52,8 +52,8 @@
 public:
     FileMap(void);
 
-    FileMap(FileMap&& f);
-    FileMap& operator=(FileMap&& f);
+    FileMap(FileMap&& f) noexcept;
+    FileMap& operator=(FileMap&& f) noexcept;
 
     /*
      * Create a new mapping on an open file.
@@ -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 675e211..9d00602 100644
--- a/libutils/include/utils/Flattenable.h
+++ b/libutils/include/utils/Flattenable.h
@@ -190,16 +190,14 @@
     inline status_t flatten(void* buffer, size_t size) const {
         if (size < sizeof(T)) return NO_MEMORY;
         memcpy(buffer, static_cast<T const*>(this), sizeof(T));
-        return NO_ERROR;
+        return OK;
     }
     inline status_t unflatten(void const* buffer, size_t) {
         memcpy(static_cast<T*>(this), buffer, sizeof(T));
-        return NO_ERROR;
+        return OK;
     }
 };
 
-
-}; // namespace android
-
+}  // namespace android
 
 #endif /* ANDROID_UTILS_FLATTENABLE_H */
diff --git a/libutils/include/utils/Functor.h b/libutils/include/utils/Functor.h
index 3182a9c..c458699 100644
--- a/libutils/include/utils/Functor.h
+++ b/libutils/include/utils/Functor.h
@@ -29,9 +29,9 @@
 public:
     Functor() {}
     virtual ~Functor() {}
-    virtual status_t operator ()(int /*what*/, void* /*data*/) { return NO_ERROR; }
+    virtual status_t operator()(int /*what*/, void* /*data*/) { return OK; }
 };
 
-}; // namespace android
+}  // namespace android
 
 #endif // ANDROID_FUNCTOR_H
diff --git a/libutils/include/utils/KeyedVector.h b/libutils/include/utils/KeyedVector.h
index 03bfe27..7bda99b 100644
--- a/libutils/include/utils/KeyedVector.h
+++ b/libutils/include/utils/KeyedVector.h
@@ -211,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
index 65257ed..e488e60 100644
--- a/libutils/include/utils/LightRefBase.h
+++ b/libutils/include/utils/LightRefBase.h
@@ -69,4 +69,4 @@
     virtual ~VirtualLightRefBase() = default;
 };
 
-}; // namespace android
+}  // namespace android
diff --git a/libutils/include/utils/List.h b/libutils/include/utils/List.h
index daca016..25b56fd 100644
--- a/libutils/include/utils/List.h
+++ b/libutils/include/utils/List.h
@@ -329,6 +329,6 @@
     return *this;
 }
 
-}; // namespace android
+}  // namespace android
 
 #endif // _LIBS_UTILS_LIST_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 af6076c..1325bf3 100644
--- a/libutils/include/utils/Mutex.h
+++ b/libutils/include/utils/Mutex.h
@@ -100,7 +100,7 @@
 
     Mutex();
     explicit Mutex(const char* name);
-    explicit Mutex(int type, const char* name = NULL);
+    explicit Mutex(int type, const char* name = nullptr);
     ~Mutex();
 
     // lock or unlock the mutex
@@ -108,7 +108,7 @@
     void unlock() RELEASE();
 
     // lock if possible; returns 0 on success, error otherwise
-    status_t tryLock() TRY_ACQUIRE(true);
+    status_t tryLock() TRY_ACQUIRE(0);
 
 #if defined(__ANDROID__)
     // Lock the mutex, but don't wait longer than timeoutNs (relative time).
@@ -122,7 +122,7 @@
     // 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) TRY_ACQUIRE(true);
+    status_t timedLock(nsecs_t timeoutNs) TRY_ACQUIRE(0);
 #endif
 
     // Manages the mutex automatically. It'll be locked when Autolock is
@@ -160,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) {
@@ -173,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() {
@@ -212,7 +212,7 @@
 typedef Mutex::Autolock AutoMutex;
 
 // ---------------------------------------------------------------------------
-}; // namespace android
+}  // namespace android
 // ---------------------------------------------------------------------------
 
 #endif // _LIBS_UTILS_MUTEX_H
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 223b666..a105474 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -171,6 +171,8 @@
 #define ANDROID_REF_BASE_H
 
 #include <atomic>
+#include <functional>
+#include <type_traits>  // for common_type.
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -192,19 +194,26 @@
 // ---------------------------------------------------------------------------
 
 #define COMPARE_WEAK(_op_)                                      \
-inline bool operator _op_ (const sp<T>& o) const {              \
-    return m_ptr _op_ o.m_ptr;                                  \
-}                                                               \
-inline bool operator _op_ (const T* o) const {                  \
-    return m_ptr _op_ o;                                        \
-}                                                               \
-template<typename U>                                            \
-inline bool operator _op_ (const sp<U>& o) const {              \
-    return m_ptr _op_ o.m_ptr;                                  \
-}                                                               \
 template<typename U>                                            \
 inline bool operator _op_ (const U* o) const {                  \
     return m_ptr _op_ o;                                        \
+}                                                               \
+/* Needed to handle type inference for nullptr: */              \
+inline bool operator _op_ (const T* o) const {                  \
+    return m_ptr _op_ o;                                        \
+}
+
+template<template<typename C> class comparator, typename T, typename U>
+static inline bool _wp_compare_(T* a, U* b) {
+    return comparator<typename std::common_type<T*, U*>::type>()(a, b);
+}
+
+// Use std::less and friends to avoid undefined behavior when ordering pointers
+// to different objects.
+#define COMPARE_WEAK_FUNCTIONAL(_op_, _compare_)                 \
+template<typename U>                                             \
+inline bool operator _op_ (const U* o) const {                   \
+    return _wp_compare_<_compare_>(m_ptr, o);                    \
 }
 
 // ---------------------------------------------------------------------------
@@ -354,7 +363,7 @@
 public:
     typedef typename RefBase::weakref_type weakref_type;
 
-    inline wp() : m_ptr(0) { }
+    inline wp() : m_ptr(nullptr), m_refs(nullptr) { }
 
     wp(T* other);  // NOLINT(implicit)
     wp(const wp<T>& other);
@@ -395,39 +404,51 @@
 
     COMPARE_WEAK(==)
     COMPARE_WEAK(!=)
-    COMPARE_WEAK(>)
-    COMPARE_WEAK(<)
-    COMPARE_WEAK(<=)
-    COMPARE_WEAK(>=)
+    COMPARE_WEAK_FUNCTIONAL(>, std::greater)
+    COMPARE_WEAK_FUNCTIONAL(<, std::less)
+    COMPARE_WEAK_FUNCTIONAL(<=, std::less_equal)
+    COMPARE_WEAK_FUNCTIONAL(>=, std::greater_equal)
 
-    inline bool operator == (const wp<T>& o) const {
-        return (m_ptr == o.m_ptr) && (m_refs == o.m_refs);
-    }
     template<typename U>
     inline bool operator == (const wp<U>& o) const {
-        return m_ptr == o.m_ptr;
+        return m_refs == o.m_refs;  // Implies m_ptr == o.mptr; see invariants below.
     }
 
-    inline bool operator > (const wp<T>& o) const {
-        return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
+    template<typename U>
+    inline bool operator == (const sp<U>& o) const {
+        // Just comparing m_ptr fields is often dangerous, since wp<> may refer to an older
+        // object at the same address.
+        if (o == nullptr) {
+          return m_ptr == nullptr;
+        } else {
+          return m_refs == o->getWeakRefs();  // Implies m_ptr == o.mptr.
+        }
     }
+
+    template<typename U>
+    inline bool operator != (const sp<U>& o) const {
+        return !(*this == o);
+    }
+
     template<typename U>
     inline bool operator > (const wp<U>& o) const {
-        return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
+        if (m_ptr == o.m_ptr) {
+            return _wp_compare_<std::greater>(m_refs, o.m_refs);
+        } else {
+            return _wp_compare_<std::greater>(m_ptr, o.m_ptr);
+        }
     }
 
-    inline bool operator < (const wp<T>& o) const {
-        return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
-    }
     template<typename U>
     inline bool operator < (const wp<U>& o) const {
-        return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
+        if (m_ptr == o.m_ptr) {
+            return _wp_compare_<std::less>(m_refs, o.m_refs);
+        } else {
+            return _wp_compare_<std::less>(m_ptr, o.m_ptr);
+        }
     }
-                         inline bool operator != (const wp<T>& o) const { return m_refs != o.m_refs; }
     template<typename U> inline bool operator != (const wp<U>& o) const { return !operator == (o); }
-                         inline bool operator <= (const wp<T>& o) const { return !operator > (o); }
     template<typename U> inline bool operator <= (const wp<U>& o) const { return !operator > (o); }
-                         inline bool operator >= (const wp<T>& o) const { return !operator < (o); }
     template<typename U> inline bool operator >= (const wp<U>& o) const { return !operator < (o); }
 
 private:
@@ -446,11 +467,27 @@
 // ---------------------------------------------------------------------------
 // No user serviceable parts below here.
 
+// Implementation invariants:
+// Either
+// 1) m_ptr and m_refs are both null, or
+// 2) m_refs == m_ptr->mRefs, or
+// 3) *m_ptr is no longer live, and m_refs points to the weakref_type object that corresponded
+//    to m_ptr while it was live. *m_refs remains live while a wp<> refers to it.
+//
+// The m_refs field in a RefBase object is allocated on construction, unique to that RefBase
+// object, and never changes. Thus if two wp's have identical m_refs fields, they are either both
+// null or point to the same object. If two wp's have identical m_ptr fields, they either both
+// point to the same live object and thus have the same m_ref fields, or at least one of the
+// objects is no longer live.
+//
+// Note that the above comparison operations go out of their way to provide an ordering consistent
+// with ordinary pointer comparison; otherwise they could ignore m_ptr, and just compare m_refs.
+
 template<typename T>
 wp<T>::wp(T* other)
     : m_ptr(other)
 {
-    if (other) m_refs = other->createWeak(this);
+    m_refs = other ? m_refs = other->createWeak(this) : nullptr;
 }
 
 template<typename T>
@@ -464,16 +501,14 @@
 wp<T>::wp(const sp<T>& other)
     : m_ptr(other.m_ptr)
 {
-    if (m_ptr) {
-        m_refs = m_ptr->createWeak(this);
-    }
+    m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;
 }
 
 template<typename T> template<typename U>
 wp<T>::wp(U* other)
     : m_ptr(other)
 {
-    if (other) m_refs = other->createWeak(this);
+    m_refs = other ? other->createWeak(this) : nullptr;
 }
 
 template<typename T> template<typename U>
@@ -483,6 +518,8 @@
     if (m_ptr) {
         m_refs = other.m_refs;
         m_refs->incWeak(this);
+    } else {
+        m_refs = nullptr;
     }
 }
 
@@ -490,9 +527,7 @@
 wp<T>::wp(const sp<U>& other)
     : m_ptr(other.m_ptr)
 {
-    if (m_ptr) {
-        m_refs = m_ptr->createWeak(this);
-    }
+    m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;
 }
 
 template<typename T>
@@ -505,7 +540,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;
@@ -528,7 +563,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;
@@ -563,7 +598,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;
@@ -595,6 +630,7 @@
 {
     if (m_ptr) {
         m_refs->decWeak(this);
+        m_refs = 0;
         m_ptr = 0;
     }
 }
@@ -683,7 +719,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 bc47a5c..44d8ad7 100644
--- a/libutils/include/utils/Singleton.h
+++ b/libutils/include/utils/Singleton.h
@@ -51,7 +51,7 @@
     static TYPE& getInstance() {
         Mutex::Autolock _l(sLock);
         TYPE* instance = sInstance;
-        if (instance == 0) {
+        if (instance == nullptr) {
             instance = new TYPE();
             sInstance = instance;
         }
@@ -60,7 +60,7 @@
 
     static bool hasInstance() {
         Mutex::Autolock _l(sLock);
-        return sInstance != 0;
+        return sInstance != nullptr;
     }
     
 protected:
@@ -90,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);  /* NOLINT */ \
+    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 47c1376..394db12 100644
--- a/libutils/include/utils/SortedVector.h
+++ b/libutils/include/utils/SortedVector.h
@@ -288,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 76d78d0..9b14ac8 100644
--- a/libutils/include/utils/StopWatch.h
+++ b/libutils/include/utils/StopWatch.h
@@ -52,9 +52,7 @@
     int             mNumLaps;
 };
 
-
-}; // namespace android
-
+}  // namespace android
 
 // ---------------------------------------------------------------------------
 
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index 5f0ce06..afbc2ed 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -243,7 +243,7 @@
     return mString;
 }
 
-}; // namespace android
+}  // namespace android
 
 // ---------------------------------------------------------------------------
 
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index 94ac32f..0ddcbb2 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -95,13 +95,6 @@
                     __attribute__((format (printf, 2, 3)));
             status_t            appendFormatV(const char* fmt, va_list args);
 
-            // Note that this function takes O(N) time to calculate the value.
-            // No cache value is stored.
-            size_t              getUtf32Length() const;
-            int32_t             getUtf32At(size_t index,
-                                           size_t *next_index) const;
-            void                getUtf32(char32_t* dst) const;
-
     inline  String8&            operator=(const String8& other);
     inline  String8&            operator=(const char* other);
 
@@ -187,7 +180,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 ae6d9c8..9cd7c75 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -17,6 +17,9 @@
 #ifndef ANDROID_STRONG_POINTER_H
 #define ANDROID_STRONG_POINTER_H
 
+#include <functional>
+#include <type_traits>  // for common_type.
+
 // ---------------------------------------------------------------------------
 namespace android {
 
@@ -24,13 +27,12 @@
 
 // ---------------------------------------------------------------------------
 
-#define COMPARE(_op_)                                           \
-inline bool operator _op_ (const sp<T>& o) const {              \
-    return m_ptr _op_ o.m_ptr;                                  \
-}                                                               \
-inline bool operator _op_ (const T* o) const {                  \
-    return m_ptr _op_ o;                                        \
-}                                                               \
+// TODO: Maybe remove sp<> ? wp<> comparison? These are dangerous: If the wp<>
+// was created before the sp<>, and they point to different objects, they may
+// compare equal even if they are entirely unrelated. E.g. CameraService
+// currently performa such comparisons.
+
+#define COMPARE_STRONG(_op_)                                           \
 template<typename U>                                            \
 inline bool operator _op_ (const sp<U>& o) const {              \
     return m_ptr _op_ o.m_ptr;                                  \
@@ -39,24 +41,37 @@
 inline bool operator _op_ (const U* o) const {                  \
     return m_ptr _op_ o;                                        \
 }                                                               \
-inline bool operator _op_ (const wp<T>& o) const {              \
-    return m_ptr _op_ o.m_ptr;                                  \
-}                                                               \
-template<typename U>                                            \
-inline bool operator _op_ (const wp<U>& o) const {              \
-    return m_ptr _op_ o.m_ptr;                                  \
+/* Needed to handle type inference for nullptr: */              \
+inline bool operator _op_ (const T* o) const {                  \
+    return m_ptr _op_ o;                                        \
 }
 
+template<template<typename C> class comparator, typename T, typename U>
+static inline bool _sp_compare_(T* a, U* b) {
+    return comparator<typename std::common_type<T*, U*>::type>()(a, b);
+}
+
+// Use std::less and friends to avoid undefined behavior when ordering pointers
+// to different objects.
+#define COMPARE_STRONG_FUNCTIONAL(_op_, _compare_)               \
+template<typename U>                                             \
+inline bool operator _op_ (const sp<U>& o) const {               \
+    return _sp_compare_<_compare_>(m_ptr, o.m_ptr);              \
+}                                                                \
+template<typename U>                                             \
+inline bool operator _op_ (const U* o) const {                   \
+    return _sp_compare_<_compare_>(m_ptr, o);                    \
+}
 // ---------------------------------------------------------------------------
 
 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);
-    sp(sp<T>&& other);
+    sp(sp<T>&& other) noexcept;
     template<typename U> sp(U* other);  // NOLINT(implicit)
     template<typename U> sp(const sp<U>& other);  // NOLINT(implicit)
     template<typename U> sp(sp<U>&& other);  // NOLINT(implicit)
@@ -67,7 +82,7 @@
 
     sp& operator = (T* other);
     sp& operator = (const sp<T>& other);
-    sp& operator = (sp<T>&& other);
+    sp& operator=(sp<T>&& other) noexcept;
 
     template<typename U> sp& operator = (const sp<U>& other);
     template<typename U> sp& operator = (sp<U>&& other);
@@ -89,12 +104,23 @@
 
     // Operators
 
-    COMPARE(==)
-    COMPARE(!=)
-    COMPARE(>)
-    COMPARE(<)
-    COMPARE(<=)
-    COMPARE(>=)
+    COMPARE_STRONG(==)
+    COMPARE_STRONG(!=)
+    COMPARE_STRONG_FUNCTIONAL(>, std::greater)
+    COMPARE_STRONG_FUNCTIONAL(<, std::less)
+    COMPARE_STRONG_FUNCTIONAL(<=, std::less_equal)
+    COMPARE_STRONG_FUNCTIONAL(>=, std::greater_equal)
+
+    // Punt these to the wp<> implementation.
+    template<typename U>
+    inline bool operator == (const wp<U>& o) const {
+        return o == *this;
+    }
+
+    template<typename U>
+    inline bool operator != (const wp<U>& o) const {
+        return o != *this;
+    }
 
 private:    
     template<typename Y> friend class sp;
@@ -125,9 +151,8 @@
         m_ptr->incStrong(this);
 }
 
-template<typename T>
-sp<T>::sp(sp<T>&& other)
-        : m_ptr(other.m_ptr) {
+template <typename T>
+sp<T>::sp(sp<T>&& other) noexcept : m_ptr(other.m_ptr) {
     other.m_ptr = nullptr;
 }
 
@@ -169,8 +194,8 @@
     return *this;
 }
 
-template<typename T>
-sp<T>& sp<T>::operator =(sp<T>&& other) {
+template <typename T>
+sp<T>& sp<T>::operator=(sp<T>&& other) noexcept {
     T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
     if (oldPtr) oldPtr->decStrong(this);
     if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
@@ -228,9 +253,11 @@
 
 template<typename T>
 void sp<T>::clear() {
-    if (m_ptr) {
-        m_ptr->decStrong(this);
-        m_ptr = 0;
+    T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
+    if (oldPtr) {
+        oldPtr->decStrong(this);
+        if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
+        m_ptr = nullptr;
     }
 }
 
@@ -239,7 +266,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 598298d..fc67656 100644
--- a/libutils/include/utils/Thread.h
+++ b/libutils/include/utils/Thread.h
@@ -47,6 +47,7 @@
     virtual             ~Thread();
 
     // Start the thread in threadLoop() which needs to be implemented.
+    // NOLINTNEXTLINE(google-default-arguments)
     virtual status_t    run(    const char* name,
                                 int32_t priority = PRIORITY_DEFAULT,
                                 size_t stack = 0);
@@ -110,8 +111,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/Tokenizer.h b/libutils/include/utils/Tokenizer.h
index bb25f37..61c5ff7 100644
--- a/libutils/include/utils/Tokenizer.h
+++ b/libutils/include/utils/Tokenizer.h
@@ -37,7 +37,7 @@
     /**
      * Opens a file and maps it into memory.
      *
-     * Returns NO_ERROR and a tokenizer for the file, if successful.
+     * Returns OK and a tokenizer for the file, if successful.
      * Otherwise returns an error and sets outTokenizer to NULL.
      */
     static status_t open(const String8& filename, Tokenizer** outTokenizer);
@@ -45,7 +45,7 @@
     /**
      * Prepares to tokenize the contents of a string.
      *
-     * Returns NO_ERROR and a tokenizer for the string, if successful.
+     * Returns OK and a tokenizer for the string, if successful.
      * Otherwise returns an error and sets outTokenizer to NULL.
      */
     static status_t fromContents(const String8& filename,
diff --git a/libutils/include/utils/Trace.h b/libutils/include/utils/Trace.h
index 5e9229c..4b9c91e 100644
--- a/libutils/include/utils/Trace.h
+++ b/libutils/include/utils/Trace.h
@@ -49,7 +49,7 @@
     uint64_t mTag;
 };
 
-}; // namespace android
+}  // namespace android
 
 #else // !__ANDROID__
 
diff --git a/libutils/include/utils/TypeHelpers.h b/libutils/include/utils/TypeHelpers.h
index 28fbca5..1554f52 100644
--- a/libutils/include/utils/TypeHelpers.h
+++ b/libutils/include/utils/TypeHelpers.h
@@ -329,7 +329,7 @@
     return hash_type(uintptr_t(value));
 }
 
-}; // namespace android
+}  // namespace android
 
 // ---------------------------------------------------------------------------
 
diff --git a/libutils/include/utils/Unicode.h b/libutils/include/utils/Unicode.h
index 666b70f..a2aaa47 100644
--- a/libutils/include/utils/Unicode.h
+++ b/libutils/include/utils/Unicode.h
@@ -28,7 +28,6 @@
 size_t strlen16(const char16_t *);
 size_t strnlen16(const char16_t *, size_t);
 char16_t *strcpy16(char16_t *, const char16_t *);
-char16_t *strncpy16(char16_t *, const char16_t *, size_t);
 char16_t *strstr16(const char16_t*, const char16_t*);
 
 // Version of comparison that supports embedded NULs.
@@ -40,9 +39,6 @@
 // equivalent result as strcmp16 (unlike strncmp16).
 int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2);
 
-// Version of strzcmp16 for comparing strings in different endianness.
-int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2);
-
 // Standard string functions on char32_t strings.
 size_t strlen32(const char32_t *);
 size_t strnlen32(const char32_t *, size_t);
diff --git a/libutils/include/utils/Vector.h b/libutils/include/utils/Vector.h
index a1a0234..ddf71de 100644
--- a/libutils/include/utils/Vector.h
+++ b/libutils/include/utils/Vector.h
@@ -425,8 +425,7 @@
     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 af5ea02..32615b3 100644
--- a/libutils/include/utils/misc.h
+++ b/libutils/include/utils/misc.h
@@ -35,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 da28dfa..f77e189 100644
--- a/libutils/misc.cpp
+++ b/libutils/misc.cpp
@@ -23,7 +23,7 @@
 #include <utils/Log.h>
 #include <utils/Vector.h>
 
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
 #include <dlfcn.h>
 #include <vndksupport/linker.h>
 #endif
@@ -41,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
 
 #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;
@@ -70,7 +70,7 @@
 void add_sysprop_change_callback(sysprop_change_callback, int) {}
 #endif
 
-#if defined(__ANDROID__)
+#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);
@@ -85,7 +85,7 @@
 void report_sysprop_change() {
     do_report_sysprop_change();
 
-#if defined(__ANDROID__)
+#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
@@ -103,7 +103,7 @@
 #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);
diff --git a/libutils/tests/Android.bp b/libutils/tests/Android.bp
deleted file mode 100644
index 1390552..0000000
--- a/libutils/tests/Android.bp
+++ /dev/null
@@ -1,96 +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.
-//
-
-// Build the unit tests.
-
-cc_test {
-    name: "libutils_tests",
-    host_supported: true,
-
-    srcs: [
-        "BitSet_test.cpp",
-        "LruCache_test.cpp",
-        "Mutex_test.cpp",
-        "Singleton_test.cpp",
-        "String8_test.cpp",
-        "StrongPointer_test.cpp",
-        "Unicode_test.cpp",
-        "Vector_test.cpp",
-    ],
-
-    target: {
-        android: {
-            srcs: [
-                "SystemClock_test.cpp",
-            ],
-            shared_libs: [
-                "libz",
-                "liblog",
-                "libcutils",
-                "libutils",
-                "libbase",
-            ],
-        },
-        linux: {
-            srcs: [
-                "Looper_test.cpp",
-                "RefBase_test.cpp",
-            ],
-        },
-        host: {
-            static_libs: [
-                "libutils",
-                "liblog",
-                "libbase",
-            ],
-        },
-    },
-
-    required: [
-        "libutils_tests_singleton1",
-        "libutils_tests_singleton2",
-    ],
-
-    cflags: [
-        "-Wall",
-        "-Wextra",
-        "-Werror",
-        "-Wthread-safety",
-    ],
-}
-
-cc_test_library {
-    name: "libutils_tests_singleton1",
-    host_supported: true,
-    relative_install_path: "libutils_tests",
-    srcs: ["Singleton_test1.cpp"],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-}
-
-cc_test_library {
-    name: "libutils_tests_singleton2",
-    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/Looper_test.cpp b/libutils/tests/Looper_test.cpp
deleted file mode 100644
index 8ebcfaf..0000000
--- a/libutils/tests/Looper_test.cpp
+++ /dev/null
@@ -1,693 +0,0 @@
-//
-// Copyright 2010 The Android Open Source Project
-//
-
-#include <utils/Looper.h>
-#include <utils/Timers.h>
-#include <utils/StopWatch.h>
-#include <gtest/gtest.h>
-#include <unistd.h>
-#include <time.h>
-
-#include "TestHelpers.h"
-
-// # of milliseconds to fudge stopwatch measurements
-#define TIMING_TOLERANCE_MS 25
-
-namespace android {
-
-enum {
-    MSG_TEST1 = 1,
-    MSG_TEST2 = 2,
-    MSG_TEST3 = 3,
-    MSG_TEST4 = 4,
-};
-
-class DelayedWake : public DelayedTask {
-    sp<Looper> mLooper;
-
-public:
-    DelayedWake(int delayMillis, const sp<Looper> looper) :
-        DelayedTask(delayMillis), mLooper(looper) {
-    }
-
-protected:
-    virtual void doTask() {
-        mLooper->wake();
-    }
-};
-
-class DelayedWriteSignal : public DelayedTask {
-    Pipe* mPipe;
-
-public:
-    DelayedWriteSignal(int delayMillis, Pipe* pipe) :
-        DelayedTask(delayMillis), mPipe(pipe) {
-    }
-
-protected:
-    virtual void doTask() {
-        mPipe->writeSignal();
-    }
-};
-
-class CallbackHandler {
-public:
-    void setCallback(const sp<Looper>& looper, int fd, int events) {
-        looper->addFd(fd, 0, events, staticHandler, this);
-    }
-
-protected:
-    virtual ~CallbackHandler() { }
-
-    virtual int handler(int fd, int events) = 0;
-
-private:
-    static int staticHandler(int fd, int events, void* data) {
-        return static_cast<CallbackHandler*>(data)->handler(fd, events);
-    }
-};
-
-class StubCallbackHandler : public CallbackHandler {
-public:
-    int nextResult;
-    int callbackCount;
-
-    int fd;
-    int events;
-
-    explicit StubCallbackHandler(int nextResult) : nextResult(nextResult),
-            callbackCount(0), fd(-1), events(-1) {
-    }
-
-protected:
-    virtual int handler(int fd, int events) {
-        callbackCount += 1;
-        this->fd = fd;
-        this->events = events;
-        return nextResult;
-    }
-};
-
-class StubMessageHandler : public MessageHandler {
-public:
-    Vector<Message> messages;
-
-    virtual void handleMessage(const Message& message) {
-        messages.push(message);
-    }
-};
-
-class LooperTest : public testing::Test {
-protected:
-    sp<Looper> mLooper;
-
-    virtual void SetUp() {
-        mLooper = new Looper(true);
-    }
-
-    virtual void TearDown() {
-        mLooper.clear();
-    }
-};
-
-
-TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeout) {
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(100);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. equal timeout";
-    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
-            << "pollOnce result should be LOOPER_POLL_TIMEOUT";
-}
-
-TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturns) {
-    mLooper->wake();
-
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(1000);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. zero because wake() was called before waiting";
-    EXPECT_EQ(Looper::POLL_WAKE, result)
-            << "pollOnce result should be Looper::POLL_CALLBACK because loop was awoken";
-}
-
-TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturns) {
-    sp<DelayedWake> delayedWake = new DelayedWake(100, mLooper);
-    delayedWake->run("LooperTest");
-
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(1000);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. equal wake delay";
-    EXPECT_EQ(Looper::POLL_WAKE, result)
-            << "pollOnce result should be Looper::POLL_CALLBACK because loop was awoken";
-}
-
-TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturns) {
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(0);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should be approx. zero";
-    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
-            << "pollOnce result should be Looper::POLL_TIMEOUT";
-}
-
-TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturns) {
-    Pipe pipe;
-    StubCallbackHandler handler(true);
-
-    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
-
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(0);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should be approx. zero";
-    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
-            << "pollOnce result should be Looper::POLL_TIMEOUT";
-    EXPECT_EQ(0, handler.callbackCount)
-            << "callback should not have been invoked because FD was not signalled";
-}
-
-TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCallbackAndReturns) {
-    Pipe pipe;
-    StubCallbackHandler handler(true);
-
-    ASSERT_EQ(OK, pipe.writeSignal());
-    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
-
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(0);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should be approx. zero";
-    EXPECT_EQ(Looper::POLL_CALLBACK, result)
-            << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled";
-    EXPECT_EQ(1, handler.callbackCount)
-            << "callback should be invoked exactly once";
-    EXPECT_EQ(pipe.receiveFd, handler.fd)
-            << "callback should have received pipe fd as parameter";
-    EXPECT_EQ(Looper::EVENT_INPUT, handler.events)
-            << "callback should have received Looper::EVENT_INPUT as events";
-}
-
-TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturns) {
-    Pipe pipe;
-    StubCallbackHandler handler(true);
-
-    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
-
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(100);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. equal timeout";
-    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
-            << "pollOnce result should be Looper::POLL_TIMEOUT";
-    EXPECT_EQ(0, handler.callbackCount)
-            << "callback should not have been invoked because FD was not signalled";
-}
-
-TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_ImmediatelyInvokesCallbackAndReturns) {
-    Pipe pipe;
-    StubCallbackHandler handler(true);
-
-    pipe.writeSignal();
-    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
-
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(100);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    ASSERT_EQ(OK, pipe.readSignal())
-            << "signal should actually have been written";
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should be approx. zero";
-    EXPECT_EQ(Looper::POLL_CALLBACK, result)
-            << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled";
-    EXPECT_EQ(1, handler.callbackCount)
-            << "callback should be invoked exactly once";
-    EXPECT_EQ(pipe.receiveFd, handler.fd)
-            << "callback should have received pipe fd as parameter";
-    EXPECT_EQ(Looper::EVENT_INPUT, handler.events)
-            << "callback should have received Looper::EVENT_INPUT as events";
-}
-
-TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_PromptlyInvokesCallbackAndReturns) {
-    Pipe pipe;
-    StubCallbackHandler handler(true);
-    sp<DelayedWriteSignal> delayedWriteSignal = new DelayedWriteSignal(100, & pipe);
-
-    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
-    delayedWriteSignal->run("LooperTest");
-
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(1000);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    ASSERT_EQ(OK, pipe.readSignal())
-            << "signal should actually have been written";
-    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. equal signal delay";
-    EXPECT_EQ(Looper::POLL_CALLBACK, result)
-            << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled";
-    EXPECT_EQ(1, handler.callbackCount)
-            << "callback should be invoked exactly once";
-    EXPECT_EQ(pipe.receiveFd, handler.fd)
-            << "callback should have received pipe fd as parameter";
-    EXPECT_EQ(Looper::EVENT_INPUT, handler.events)
-            << "callback should have received Looper::EVENT_INPUT as events";
-}
-
-TEST_F(LooperTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) {
-    Pipe pipe;
-    StubCallbackHandler handler(true);
-
-    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
-    pipe.writeSignal(); // would cause FD to be considered signalled
-    mLooper->removeFd(pipe.receiveFd);
-
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(100);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    ASSERT_EQ(OK, pipe.readSignal())
-            << "signal should actually have been written";
-    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. equal timeout because FD was no longer registered";
-    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
-            << "pollOnce result should be Looper::POLL_TIMEOUT";
-    EXPECT_EQ(0, handler.callbackCount)
-            << "callback should not be invoked";
-}
-
-TEST_F(LooperTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedAgainLater) {
-    Pipe pipe;
-    StubCallbackHandler handler(false);
-
-    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
-
-    // First loop: Callback is registered and FD is signalled.
-    pipe.writeSignal();
-
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(0);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    ASSERT_EQ(OK, pipe.readSignal())
-            << "signal should actually have been written";
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. equal zero because FD was already signalled";
-    EXPECT_EQ(Looper::POLL_CALLBACK, result)
-            << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled";
-    EXPECT_EQ(1, handler.callbackCount)
-            << "callback should be invoked";
-
-    // Second loop: Callback is no longer registered and FD is signalled.
-    pipe.writeSignal();
-
-    stopWatch.reset();
-    result = mLooper->pollOnce(0);
-    elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    ASSERT_EQ(OK, pipe.readSignal())
-            << "signal should actually have been written";
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. equal zero because timeout was zero";
-    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
-            << "pollOnce result should be Looper::POLL_TIMEOUT";
-    EXPECT_EQ(1, handler.callbackCount)
-            << "callback should not be invoked this time";
-}
-
-TEST_F(LooperTest, PollOnce_WhenNonCallbackFdIsSignalled_ReturnsIdent) {
-    const int expectedIdent = 5;
-    void* expectedData = this;
-
-    Pipe pipe;
-
-    pipe.writeSignal();
-    mLooper->addFd(pipe.receiveFd, expectedIdent, Looper::EVENT_INPUT, NULL, expectedData);
-
-    StopWatch stopWatch("pollOnce");
-    int fd;
-    int events;
-    void* data;
-    int result = mLooper->pollOnce(100, &fd, &events, &data);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    ASSERT_EQ(OK, pipe.readSignal())
-            << "signal should actually have been written";
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should be approx. zero";
-    EXPECT_EQ(expectedIdent, result)
-            << "pollOnce result should be the ident of the FD that was signalled";
-    EXPECT_EQ(pipe.receiveFd, fd)
-            << "pollOnce should have returned the received pipe fd";
-    EXPECT_EQ(Looper::EVENT_INPUT, events)
-            << "pollOnce should have returned Looper::EVENT_INPUT as events";
-    EXPECT_EQ(expectedData, data)
-            << "pollOnce should have returned the data";
-}
-
-TEST_F(LooperTest, AddFd_WhenCallbackAdded_ReturnsOne) {
-    Pipe pipe;
-    int result = mLooper->addFd(pipe.receiveFd, 0, Looper::EVENT_INPUT, NULL, NULL);
-
-    EXPECT_EQ(1, result)
-            << "addFd should return 1 because FD was added";
-}
-
-TEST_F(LooperTest, AddFd_WhenIdentIsNegativeAndCallbackIsNull_ReturnsError) {
-    Pipe pipe;
-    int result = mLooper->addFd(pipe.receiveFd, -1, Looper::EVENT_INPUT, NULL, NULL);
-
-    EXPECT_EQ(-1, result)
-            << "addFd should return -1 because arguments were invalid";
-}
-
-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);
-
-    EXPECT_EQ(-1, result)
-            << "addFd should return -1 because arguments were invalid";
-}
-
-TEST_F(LooperTest, RemoveFd_WhenCallbackNotAdded_ReturnsZero) {
-    int result = mLooper->removeFd(1);
-
-    EXPECT_EQ(0, result)
-            << "removeFd should return 0 because FD not registered";
-}
-
-TEST_F(LooperTest, RemoveFd_WhenCallbackAddedThenRemovedTwice_ReturnsOnceFirstTimeAndReturnsZeroSecondTime) {
-    Pipe pipe;
-    StubCallbackHandler handler(false);
-    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
-
-    // First time.
-    int result = mLooper->removeFd(pipe.receiveFd);
-
-    EXPECT_EQ(1, result)
-            << "removeFd should return 1 first time because FD was registered";
-
-    // Second time.
-    result = mLooper->removeFd(pipe.receiveFd);
-
-    EXPECT_EQ(0, result)
-            << "removeFd should return 0 second time because FD was no longer registered";
-}
-
-TEST_F(LooperTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInvoked) {
-    Pipe pipe;
-    StubCallbackHandler handler1(true);
-    StubCallbackHandler handler2(true);
-
-    handler1.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
-    handler2.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT); // replace it
-    pipe.writeSignal(); // would cause FD to be considered signalled
-
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(100);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    ASSERT_EQ(OK, pipe.readSignal())
-            << "signal should actually have been written";
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. zero because FD was already signalled";
-    EXPECT_EQ(Looper::POLL_CALLBACK, result)
-            << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled";
-    EXPECT_EQ(0, handler1.callbackCount)
-            << "original handler callback should not be invoked because it was replaced";
-    EXPECT_EQ(1, handler2.callbackCount)
-            << "replacement handler callback should be invoked";
-}
-
-TEST_F(LooperTest, SendMessage_WhenOneMessageIsEnqueue_ShouldInvokeHandlerDuringNextPoll) {
-    sp<StubMessageHandler> handler = new StubMessageHandler();
-    mLooper->sendMessage(handler, Message(MSG_TEST1));
-
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(100);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. zero because message was already sent";
-    EXPECT_EQ(Looper::POLL_CALLBACK, result)
-            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
-    EXPECT_EQ(size_t(1), handler->messages.size())
-            << "handled message";
-    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
-            << "handled message";
-}
-
-TEST_F(LooperTest, SendMessage_WhenMultipleMessagesAreEnqueued_ShouldInvokeHandlersInOrderDuringNextPoll) {
-    sp<StubMessageHandler> handler1 = new StubMessageHandler();
-    sp<StubMessageHandler> handler2 = new StubMessageHandler();
-    mLooper->sendMessage(handler1, Message(MSG_TEST1));
-    mLooper->sendMessage(handler2, Message(MSG_TEST2));
-    mLooper->sendMessage(handler1, Message(MSG_TEST3));
-    mLooper->sendMessage(handler1, Message(MSG_TEST4));
-
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(1000);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. zero because message was already sent";
-    EXPECT_EQ(Looper::POLL_CALLBACK, result)
-            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
-    EXPECT_EQ(size_t(3), handler1->messages.size())
-            << "handled message";
-    EXPECT_EQ(MSG_TEST1, handler1->messages[0].what)
-            << "handled message";
-    EXPECT_EQ(MSG_TEST3, handler1->messages[1].what)
-            << "handled message";
-    EXPECT_EQ(MSG_TEST4, handler1->messages[2].what)
-            << "handled message";
-    EXPECT_EQ(size_t(1), handler2->messages.size())
-            << "handled message";
-    EXPECT_EQ(MSG_TEST2, handler2->messages[0].what)
-            << "handled message";
-}
-
-TEST_F(LooperTest, SendMessageDelayed_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) {
-    sp<StubMessageHandler> handler = new StubMessageHandler();
-    mLooper->sendMessageDelayed(ms2ns(100), handler, Message(MSG_TEST1));
-
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(1000);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "first poll should end quickly because next message timeout was computed";
-    EXPECT_EQ(Looper::POLL_WAKE, result)
-            << "pollOnce result should be Looper::POLL_WAKE due to wakeup";
-    EXPECT_EQ(size_t(0), handler->messages.size())
-            << "no message handled yet";
-
-    result = mLooper->pollOnce(1000);
-    elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_EQ(size_t(1), handler->messages.size())
-            << "handled message";
-    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
-            << "handled message";
-    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "second poll should end around the time of the delayed message dispatch";
-    EXPECT_EQ(Looper::POLL_CALLBACK, result)
-            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
-
-    result = mLooper->pollOnce(100);
-    elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "third poll should timeout";
-    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
-            << "pollOnce result should be Looper::POLL_TIMEOUT because there were no messages left";
-}
-
-TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) {
-    sp<StubMessageHandler> handler = new StubMessageHandler();
-    mLooper->sendMessageDelayed(ms2ns(-1000), handler, Message(MSG_TEST1));
-
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(100);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. zero because message was already sent";
-    EXPECT_EQ(Looper::POLL_CALLBACK, result)
-            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
-    EXPECT_EQ(size_t(1), handler->messages.size())
-            << "handled message";
-    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
-            << "handled message";
-}
-
-TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) {
-    sp<StubMessageHandler> handler = new StubMessageHandler();
-    mLooper->sendMessageDelayed(0, handler, Message(MSG_TEST1));
-
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(100);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. zero because message was already sent";
-    EXPECT_EQ(Looper::POLL_CALLBACK, result)
-            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
-    EXPECT_EQ(size_t(1), handler->messages.size())
-            << "handled message";
-    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
-            << "handled message";
-}
-
-TEST_F(LooperTest, SendMessageAtTime_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) {
-    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-    sp<StubMessageHandler> handler = new StubMessageHandler();
-    mLooper->sendMessageAtTime(now + ms2ns(100), handler, Message(MSG_TEST1));
-
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(1000);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "first poll should end quickly because next message timeout was computed";
-    EXPECT_EQ(Looper::POLL_WAKE, result)
-            << "pollOnce result should be Looper::POLL_WAKE due to wakeup";
-    EXPECT_EQ(size_t(0), handler->messages.size())
-            << "no message handled yet";
-
-    result = mLooper->pollOnce(1000);
-    elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_EQ(size_t(1), handler->messages.size())
-            << "handled message";
-    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
-            << "handled message";
-    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "second poll should end around the time of the delayed message dispatch";
-    EXPECT_EQ(Looper::POLL_CALLBACK, result)
-            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
-
-    result = mLooper->pollOnce(100);
-    elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "third poll should timeout";
-    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
-            << "pollOnce result should be Looper::POLL_TIMEOUT because there were no messages left";
-}
-
-TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) {
-    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-    sp<StubMessageHandler> handler = new StubMessageHandler();
-    mLooper->sendMessageAtTime(now - ms2ns(1000), handler, Message(MSG_TEST1));
-
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(100);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. zero because message was already sent";
-    EXPECT_EQ(Looper::POLL_CALLBACK, result)
-            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
-    EXPECT_EQ(size_t(1), handler->messages.size())
-            << "handled message";
-    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
-            << "handled message";
-}
-
-TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) {
-    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-    sp<StubMessageHandler> handler = new StubMessageHandler();
-    mLooper->sendMessageAtTime(now, handler, Message(MSG_TEST1));
-
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(100);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. zero because message was already sent";
-    EXPECT_EQ(Looper::POLL_CALLBACK, result)
-            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
-    EXPECT_EQ(size_t(1), handler->messages.size())
-            << "handled message";
-    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
-            << "handled message";
-}
-
-TEST_F(LooperTest, RemoveMessage_WhenRemovingAllMessagesForHandler_ShouldRemoveThoseMessage) {
-    sp<StubMessageHandler> handler = new StubMessageHandler();
-    mLooper->sendMessage(handler, Message(MSG_TEST1));
-    mLooper->sendMessage(handler, Message(MSG_TEST2));
-    mLooper->sendMessage(handler, Message(MSG_TEST3));
-    mLooper->removeMessages(handler);
-
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(0);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. zero because message was sent so looper was awoken";
-    EXPECT_EQ(Looper::POLL_WAKE, result)
-            << "pollOnce result should be Looper::POLL_WAKE because looper was awoken";
-    EXPECT_EQ(size_t(0), handler->messages.size())
-            << "no messages to handle";
-
-    result = mLooper->pollOnce(0);
-
-    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
-            << "pollOnce result should be Looper::POLL_TIMEOUT because there was nothing to do";
-    EXPECT_EQ(size_t(0), handler->messages.size())
-            << "no messages to handle";
-}
-
-TEST_F(LooperTest, RemoveMessage_WhenRemovingSomeMessagesForHandler_ShouldRemoveThoseMessage) {
-    sp<StubMessageHandler> handler = new StubMessageHandler();
-    mLooper->sendMessage(handler, Message(MSG_TEST1));
-    mLooper->sendMessage(handler, Message(MSG_TEST2));
-    mLooper->sendMessage(handler, Message(MSG_TEST3));
-    mLooper->sendMessage(handler, Message(MSG_TEST4));
-    mLooper->removeMessages(handler, MSG_TEST3);
-    mLooper->removeMessages(handler, MSG_TEST1);
-
-    StopWatch stopWatch("pollOnce");
-    int result = mLooper->pollOnce(0);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. zero because message was sent so looper was awoken";
-    EXPECT_EQ(Looper::POLL_CALLBACK, result)
-            << "pollOnce result should be Looper::POLL_CALLBACK because two messages were sent";
-    EXPECT_EQ(size_t(2), handler->messages.size())
-            << "no messages to handle";
-    EXPECT_EQ(MSG_TEST2, handler->messages[0].what)
-            << "handled message";
-    EXPECT_EQ(MSG_TEST4, handler->messages[1].what)
-            << "handled message";
-
-    result = mLooper->pollOnce(0);
-
-    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
-            << "pollOnce result should be Looper::POLL_TIMEOUT because there was nothing to do";
-    EXPECT_EQ(size_t(2), handler->messages.size())
-            << "no more messages to handle";
-}
-
-} // namespace android
diff --git a/libutils/tests/LruCache_test.cpp b/libutils/tests/LruCache_test.cpp
deleted file mode 100644
index 4e885bb..0000000
--- a/libutils/tests/LruCache_test.cpp
+++ /dev/null
@@ -1,459 +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 <stdlib.h>
-
-#include <android/log.h>
-#include <gtest/gtest.h>
-#include <utils/JenkinsHash.h>
-#include <utils/LruCache.h>
-
-namespace {
-
-typedef int SimpleKey;
-typedef const char* StringValue;
-
-struct ComplexKey {
-    int k;
-
-    explicit ComplexKey(int k) : k(k) {
-        instanceCount += 1;
-    }
-
-    ComplexKey(const ComplexKey& other) : k(other.k) {
-        instanceCount += 1;
-    }
-
-    ~ComplexKey() {
-        instanceCount -= 1;
-    }
-
-    bool operator ==(const ComplexKey& other) const {
-        return k == other.k;
-    }
-
-    bool operator !=(const ComplexKey& other) const {
-        return k != other.k;
-    }
-
-    static ssize_t instanceCount;
-};
-
-ssize_t ComplexKey::instanceCount = 0;
-
-struct ComplexValue {
-    int v;
-
-    explicit ComplexValue(int v) : v(v) {
-        instanceCount += 1;
-    }
-
-    ComplexValue(const ComplexValue& other) : v(other.v) {
-        instanceCount += 1;
-    }
-
-    ~ComplexValue() {
-        instanceCount -= 1;
-    }
-
-    static ssize_t instanceCount;
-};
-
-ssize_t ComplexValue::instanceCount = 0;
-
-struct KeyWithPointer {
-    int *ptr;
-    bool operator ==(const KeyWithPointer& other) const {
-        return *ptr == *other.ptr;
-    }
-};
-
-struct KeyFailsOnCopy : public ComplexKey {
-    public:
-    KeyFailsOnCopy(const KeyFailsOnCopy& key) : ComplexKey(key) {
-        ADD_FAILURE();
-    }
-    KeyFailsOnCopy(int key) : ComplexKey(key) { }
-};
-
-} // namespace
-
-
-namespace android {
-
-typedef LruCache<ComplexKey, ComplexValue> ComplexCache;
-
-template<> inline android::hash_t hash_type(const ComplexKey& value) {
-    return hash_type(value.k);
-}
-
-template<> inline android::hash_t hash_type(const KeyWithPointer& value) {
-    return hash_type(*value.ptr);
-}
-
-template<> inline android::hash_t hash_type(const KeyFailsOnCopy& value) {
-    return hash_type<ComplexKey>(value);
-}
-
-class EntryRemovedCallback : public OnEntryRemoved<SimpleKey, StringValue> {
-public:
-    EntryRemovedCallback() : callbackCount(0), lastKey(-1), lastValue(NULL) { }
-    ~EntryRemovedCallback() {}
-    void operator()(SimpleKey& k, StringValue& v) {
-        callbackCount += 1;
-        lastKey = k;
-        lastValue = v;
-    }
-    ssize_t callbackCount;
-    SimpleKey lastKey;
-    StringValue lastValue;
-};
-
-class InvalidateKeyCallback : public OnEntryRemoved<KeyWithPointer, StringValue> {
-public:
-    void operator()(KeyWithPointer& k, StringValue&) {
-        delete k.ptr;
-        k.ptr = nullptr;
-    }
-};
-
-class LruCacheTest : public testing::Test {
-protected:
-    virtual void SetUp() {
-        ComplexKey::instanceCount = 0;
-        ComplexValue::instanceCount = 0;
-    }
-
-    virtual void TearDown() {
-        ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
-    }
-
-    void assertInstanceCount(ssize_t keys, ssize_t values) {
-        if (keys != ComplexKey::instanceCount || values != ComplexValue::instanceCount) {
-            FAIL() << "Expected " << keys << " keys and " << values << " values "
-                    "but there were actually " << ComplexKey::instanceCount << " keys and "
-                    << ComplexValue::instanceCount << " values";
-        }
-    }
-};
-
-TEST_F(LruCacheTest, Empty) {
-    LruCache<SimpleKey, StringValue> cache(100);
-
-    EXPECT_EQ(NULL, cache.get(0));
-    EXPECT_EQ(0u, cache.size());
-}
-
-TEST_F(LruCacheTest, Simple) {
-    LruCache<SimpleKey, StringValue> cache(100);
-
-    cache.put(1, "one");
-    cache.put(2, "two");
-    cache.put(3, "three");
-    EXPECT_STREQ("one", cache.get(1));
-    EXPECT_STREQ("two", cache.get(2));
-    EXPECT_STREQ("three", cache.get(3));
-    EXPECT_EQ(3u, cache.size());
-}
-
-TEST_F(LruCacheTest, MaxCapacity) {
-    LruCache<SimpleKey, StringValue> cache(2);
-
-    cache.put(1, "one");
-    cache.put(2, "two");
-    cache.put(3, "three");
-    EXPECT_EQ(NULL, cache.get(1));
-    EXPECT_STREQ("two", cache.get(2));
-    EXPECT_STREQ("three", cache.get(3));
-    EXPECT_EQ(2u, cache.size());
-}
-
-TEST_F(LruCacheTest, RemoveLru) {
-    LruCache<SimpleKey, StringValue> cache(100);
-
-    cache.put(1, "one");
-    cache.put(2, "two");
-    cache.put(3, "three");
-    cache.removeOldest();
-    EXPECT_EQ(NULL, cache.get(1));
-    EXPECT_STREQ("two", cache.get(2));
-    EXPECT_STREQ("three", cache.get(3));
-    EXPECT_EQ(2u, cache.size());
-}
-
-TEST_F(LruCacheTest, GetUpdatesLru) {
-    LruCache<SimpleKey, StringValue> cache(100);
-
-    cache.put(1, "one");
-    cache.put(2, "two");
-    cache.put(3, "three");
-    EXPECT_STREQ("one", cache.get(1));
-    cache.removeOldest();
-    EXPECT_STREQ("one", cache.get(1));
-    EXPECT_EQ(NULL, cache.get(2));
-    EXPECT_STREQ("three", cache.get(3));
-    EXPECT_EQ(2u, cache.size());
-}
-
-uint32_t hash_int(int x) {
-    return JenkinsHashWhiten(JenkinsHashMix(0, x));
-}
-
-TEST_F(LruCacheTest, StressTest) {
-    const size_t kCacheSize = 512;
-    LruCache<SimpleKey, StringValue> cache(512);
-    const size_t kNumKeys = 16 * 1024;
-    const size_t kNumIters = 100000;
-    char* strings[kNumKeys];
-
-    for (size_t i = 0; i < kNumKeys; i++) {
-        strings[i] = (char *)malloc(16);
-        sprintf(strings[i], "%zu", i);
-    }
-
-    srandom(12345);
-    int hitCount = 0;
-    for (size_t i = 0; i < kNumIters; i++) {
-        int index = random() % kNumKeys;
-        uint32_t key = hash_int(index);
-        const char *val = cache.get(key);
-        if (val != NULL) {
-            EXPECT_EQ(strings[index], val);
-            hitCount++;
-        } else {
-            cache.put(key, strings[index]);
-        }
-    }
-    size_t expectedHitCount = kNumIters * kCacheSize / kNumKeys;
-    EXPECT_LT(int(expectedHitCount * 0.9), hitCount);
-    EXPECT_GT(int(expectedHitCount * 1.1), hitCount);
-    EXPECT_EQ(kCacheSize, cache.size());
-
-    for (size_t i = 0; i < kNumKeys; i++) {
-        free((void *)strings[i]);
-    }
-}
-
-TEST_F(LruCacheTest, NoLeak) {
-    ComplexCache cache(100);
-
-    cache.put(ComplexKey(0), ComplexValue(0));
-    cache.put(ComplexKey(1), ComplexValue(1));
-    EXPECT_EQ(2U, cache.size());
-    assertInstanceCount(2, 3);  // the member mNullValue counts as an instance
-}
-
-TEST_F(LruCacheTest, Clear) {
-    ComplexCache cache(100);
-
-    cache.put(ComplexKey(0), ComplexValue(0));
-    cache.put(ComplexKey(1), ComplexValue(1));
-    EXPECT_EQ(2U, cache.size());
-    assertInstanceCount(2, 3);
-    cache.clear();
-    assertInstanceCount(0, 1);
-}
-
-TEST_F(LruCacheTest, ClearNoDoubleFree) {
-    {
-        ComplexCache cache(100);
-
-        cache.put(ComplexKey(0), ComplexValue(0));
-        cache.put(ComplexKey(1), ComplexValue(1));
-        EXPECT_EQ(2U, cache.size());
-        assertInstanceCount(2, 3);
-        cache.removeOldest();
-        cache.clear();
-        assertInstanceCount(0, 1);
-    }
-    assertInstanceCount(0, 0);
-}
-
-TEST_F(LruCacheTest, ClearReuseOk) {
-    ComplexCache cache(100);
-
-    cache.put(ComplexKey(0), ComplexValue(0));
-    cache.put(ComplexKey(1), ComplexValue(1));
-    EXPECT_EQ(2U, cache.size());
-    assertInstanceCount(2, 3);
-    cache.clear();
-    assertInstanceCount(0, 1);
-    cache.put(ComplexKey(0), ComplexValue(0));
-    cache.put(ComplexKey(1), ComplexValue(1));
-    EXPECT_EQ(2U, cache.size());
-    assertInstanceCount(2, 3);
-}
-
-TEST_F(LruCacheTest, Callback) {
-    LruCache<SimpleKey, StringValue> cache(100);
-    EntryRemovedCallback callback;
-    cache.setOnEntryRemovedListener(&callback);
-
-    cache.put(1, "one");
-    cache.put(2, "two");
-    cache.put(3, "three");
-    EXPECT_EQ(3U, cache.size());
-    cache.removeOldest();
-    EXPECT_EQ(1, callback.callbackCount);
-    EXPECT_EQ(1, callback.lastKey);
-    EXPECT_STREQ("one", callback.lastValue);
-}
-
-TEST_F(LruCacheTest, CallbackOnClear) {
-    LruCache<SimpleKey, StringValue> cache(100);
-    EntryRemovedCallback callback;
-    cache.setOnEntryRemovedListener(&callback);
-
-    cache.put(1, "one");
-    cache.put(2, "two");
-    cache.put(3, "three");
-    EXPECT_EQ(3U, cache.size());
-    cache.clear();
-    EXPECT_EQ(3, callback.callbackCount);
-}
-
-TEST_F(LruCacheTest, CallbackRemovesKeyWorksOK) {
-    LruCache<KeyWithPointer, StringValue> cache(1);
-    InvalidateKeyCallback callback;
-    cache.setOnEntryRemovedListener(&callback);
-    KeyWithPointer key1;
-    key1.ptr = new int(1);
-    KeyWithPointer key2;
-    key2.ptr = new int(2);
-
-    cache.put(key1, "one");
-    // As the size of the cache is 1, the put will call the callback.
-    // Make sure everything goes smoothly even if the callback invalidates
-    // the key (b/24785286)
-    cache.put(key2, "two");
-    EXPECT_EQ(1U, cache.size());
-    EXPECT_STREQ("two", cache.get(key2));
-    cache.clear();
-}
-
-TEST_F(LruCacheTest, IteratorCheck) {
-    LruCache<int, int> cache(100);
-
-    cache.put(1, 4);
-    cache.put(2, 5);
-    cache.put(3, 6);
-    EXPECT_EQ(3U, cache.size());
-
-    LruCache<int, int>::Iterator it(cache);
-    std::unordered_set<int> returnedValues;
-    while (it.next()) {
-        int v = it.value();
-        // Check we haven't seen the value before.
-        EXPECT_TRUE(returnedValues.find(v) == returnedValues.end());
-        returnedValues.insert(v);
-    }
-    EXPECT_EQ(std::unordered_set<int>({4, 5, 6}), returnedValues);
-}
-
-TEST_F(LruCacheTest, EmptyCacheIterator) {
-    // Check that nothing crashes...
-    LruCache<int, int> cache(100);
-
-    LruCache<int, int>::Iterator it(cache);
-    std::unordered_set<int> returnedValues;
-    while (it.next()) {
-        returnedValues.insert(it.value());
-    }
-    EXPECT_EQ(std::unordered_set<int>(), returnedValues);
-}
-
-TEST_F(LruCacheTest, OneElementCacheIterator) {
-    // Check that nothing crashes...
-    LruCache<int, int> cache(100);
-    cache.put(1, 2);
-
-    LruCache<int, int>::Iterator it(cache);
-    std::unordered_set<int> returnedValues;
-    while (it.next()) {
-        returnedValues.insert(it.value());
-    }
-    EXPECT_EQ(std::unordered_set<int>({ 2 }), returnedValues);
-}
-
-TEST_F(LruCacheTest, OneElementCacheRemove) {
-    LruCache<int, int> cache(100);
-    cache.put(1, 2);
-
-    cache.remove(1);
-
-    LruCache<int, int>::Iterator it(cache);
-    std::unordered_set<int> returnedValues;
-    while (it.next()) {
-        returnedValues.insert(it.value());
-    }
-    EXPECT_EQ(std::unordered_set<int>({ }), returnedValues);
-}
-
-TEST_F(LruCacheTest, Remove) {
-    LruCache<int, int> cache(100);
-    cache.put(1, 4);
-    cache.put(2, 5);
-    cache.put(3, 6);
-
-    cache.remove(2);
-
-    LruCache<int, int>::Iterator it(cache);
-    std::unordered_set<int> returnedValues;
-    while (it.next()) {
-        returnedValues.insert(it.value());
-    }
-    EXPECT_EQ(std::unordered_set<int>({ 4, 6 }), returnedValues);
-}
-
-TEST_F(LruCacheTest, RemoveYoungest) {
-    LruCache<int, int> cache(100);
-    cache.put(1, 4);
-    cache.put(2, 5);
-    cache.put(3, 6);
-
-    cache.remove(3);
-
-    LruCache<int, int>::Iterator it(cache);
-    std::unordered_set<int> returnedValues;
-    while (it.next()) {
-        returnedValues.insert(it.value());
-    }
-    EXPECT_EQ(std::unordered_set<int>({ 4, 5 }), returnedValues);
-}
-
-TEST_F(LruCacheTest, RemoveNonMember) {
-    LruCache<int, int> cache(100);
-    cache.put(1, 4);
-    cache.put(2, 5);
-    cache.put(3, 6);
-
-    cache.remove(7);
-
-    LruCache<int, int>::Iterator it(cache);
-    std::unordered_set<int> returnedValues;
-    while (it.next()) {
-        returnedValues.insert(it.value());
-    }
-    EXPECT_EQ(std::unordered_set<int>({ 4, 5, 6 }), returnedValues);
-}
-
-TEST_F(LruCacheTest, DontCopyKeyInGet) {
-    LruCache<KeyFailsOnCopy, KeyFailsOnCopy> cache(1);
-    // Check that get doesn't copy the key
-    cache.get(KeyFailsOnCopy(0));
-}
-
-}
diff --git a/libutils/tests/Mutex_test.cpp b/libutils/tests/Mutex_test.cpp
deleted file mode 100644
index 8a1805f..0000000
--- a/libutils/tests/Mutex_test.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 <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/README.txt b/libutils/tests/README.txt
deleted file mode 100644
index ad54e57..0000000
--- a/libutils/tests/README.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-Run device tests:
-
-mma -j<whatever>
-(after adb root; adb disable-verity; adb reboot)
-adb root
-adb remount
-adb sync
-adb shell /data/nativetest/libutils_tests/libutils_tests
diff --git a/libutils/tests/RefBase_test.cpp b/libutils/tests/RefBase_test.cpp
deleted file mode 100644
index 2e0cf6e..0000000
--- a/libutils/tests/RefBase_test.cpp
+++ /dev/null
@@ -1,258 +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 <gtest/gtest.h>
-
-#include <utils/StrongPointer.h>
-#include <utils/RefBase.h>
-
-#include <thread>
-#include <atomic>
-#include <sched.h>
-#include <errno.h>
-
-// Enhanced version of StrongPointer_test, but using RefBase underneath.
-
-using namespace android;
-
-static constexpr int NITERS = 1000000;
-
-static constexpr int INITIAL_STRONG_VALUE = 1 << 28;  // Mirroring RefBase definition.
-
-class Foo : public RefBase {
-public:
-    Foo(bool* deleted_check) : mDeleted(deleted_check) {
-        *mDeleted = false;
-    }
-
-    ~Foo() {
-        *mDeleted = true;
-    }
-private:
-    bool* mDeleted;
-};
-
-TEST(RefBase, StrongMoves) {
-    bool isDeleted;
-    Foo* foo = new Foo(&isDeleted);
-    ASSERT_EQ(INITIAL_STRONG_VALUE, foo->getStrongCount());
-    ASSERT_FALSE(isDeleted) << "Already deleted...?";
-    sp<Foo> sp1(foo);
-    wp<Foo> wp1(sp1);
-    ASSERT_EQ(1, foo->getStrongCount());
-    // Weak count includes both strong and weak references.
-    ASSERT_EQ(2, foo->getWeakRefs()->getWeakCount());
-    {
-        sp<Foo> sp2 = std::move(sp1);
-        ASSERT_EQ(1, foo->getStrongCount())
-                << "std::move failed, incremented refcnt";
-        ASSERT_EQ(nullptr, sp1.get()) << "std::move failed, sp1 is still valid";
-        // The strong count isn't increasing, let's double check the old object
-        // is properly reset and doesn't early delete
-        sp1 = std::move(sp2);
-    }
-    ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!";
-    {
-        // Now let's double check it deletes on time
-        sp<Foo> sp2 = std::move(sp1);
-    }
-    ASSERT_TRUE(isDeleted) << "foo was leaked!";
-    ASSERT_TRUE(wp1.promote().get() == nullptr);
-}
-
-TEST(RefBase, WeakCopies) {
-    bool isDeleted;
-    Foo* foo = new Foo(&isDeleted);
-    EXPECT_EQ(0, foo->getWeakRefs()->getWeakCount());
-    ASSERT_FALSE(isDeleted) << "Foo (weak) already deleted...?";
-    wp<Foo> wp1(foo);
-    EXPECT_EQ(1, foo->getWeakRefs()->getWeakCount());
-    {
-        wp<Foo> wp2 = wp1;
-        ASSERT_EQ(2, foo->getWeakRefs()->getWeakCount());
-    }
-    EXPECT_EQ(1, foo->getWeakRefs()->getWeakCount());
-    ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!";
-    wp1 = nullptr;
-    ASSERT_FALSE(isDeleted) << "Deletion on wp destruction should no longer occur";
-}
-
-
-// Set up a situation in which we race with visit2AndRremove() to delete
-// 2 strong references.  Bar destructor checks that there are no early
-// deletions and prior updates are visible to destructor.
-class Bar : public RefBase {
-public:
-    Bar(std::atomic<int>* delete_count) : mVisited1(false), mVisited2(false),
-            mDeleteCount(delete_count) {
-    }
-
-    ~Bar() {
-        EXPECT_TRUE(mVisited1);
-        EXPECT_TRUE(mVisited2);
-        (*mDeleteCount)++;
-    }
-    bool mVisited1;
-    bool mVisited2;
-private:
-    std::atomic<int>* mDeleteCount;
-};
-
-static sp<Bar> buffer;
-static std::atomic<bool> bufferFull(false);
-
-// Wait until bufferFull has value val.
-static inline void waitFor(bool val) {
-    while (bufferFull != val) {}
-}
-
-cpu_set_t otherCpus;
-
-// Divide the cpus we're allowed to run on into myCpus and otherCpus.
-// Set origCpus to the processors we were originally allowed to run on.
-// Return false if origCpus doesn't include at least processors 0 and 1.
-static bool setExclusiveCpus(cpu_set_t* origCpus /* out */,
-        cpu_set_t* myCpus /* out */, cpu_set_t* otherCpus) {
-    if (sched_getaffinity(0, sizeof(cpu_set_t), origCpus) != 0) {
-        return false;
-    }
-    if (!CPU_ISSET(0,  origCpus) || !CPU_ISSET(1, origCpus)) {
-        return false;
-    }
-    CPU_ZERO(myCpus);
-    CPU_ZERO(otherCpus);
-    CPU_OR(myCpus, myCpus, origCpus);
-    CPU_OR(otherCpus, otherCpus, origCpus);
-    for (unsigned i = 0; i < CPU_SETSIZE; ++i) {
-        // I get the even cores, the other thread gets the odd ones.
-        if (i & 1) {
-            CPU_CLR(i, myCpus);
-        } else {
-            CPU_CLR(i, otherCpus);
-        }
-    }
-    return true;
-}
-
-static void visit2AndRemove() {
-    if (sched_setaffinity(0, sizeof(cpu_set_t), &otherCpus) != 0) {
-        FAIL() << "setaffinity returned:" << errno;
-    }
-    for (int i = 0; i < NITERS; ++i) {
-        waitFor(true);
-        buffer->mVisited2 = true;
-        buffer = nullptr;
-        bufferFull = false;
-    }
-}
-
-TEST(RefBase, RacingDestructors) {
-    cpu_set_t origCpus;
-    cpu_set_t myCpus;
-    // Restrict us and the helper thread to disjoint cpu sets.
-    // This prevents us from getting scheduled against each other,
-    // which would be atrociously slow.
-    if (setExclusiveCpus(&origCpus, &myCpus, &otherCpus)) {
-        std::thread t(visit2AndRemove);
-        std::atomic<int> deleteCount(0);
-        if (sched_setaffinity(0, sizeof(cpu_set_t), &myCpus) != 0) {
-            FAIL() << "setaffinity returned:" << errno;
-        }
-        for (int i = 0; i < NITERS; ++i) {
-            waitFor(false);
-            Bar* bar = new Bar(&deleteCount);
-            sp<Bar> sp3(bar);
-            buffer = sp3;
-            bufferFull = true;
-            ASSERT_TRUE(bar->getStrongCount() >= 1);
-            // Weak count includes strong count.
-            ASSERT_TRUE(bar->getWeakRefs()->getWeakCount() >= 1);
-            sp3->mVisited1 = true;
-            sp3 = nullptr;
-        }
-        t.join();
-        if (sched_setaffinity(0, sizeof(cpu_set_t), &origCpus) != 0) {
-            FAIL();
-        }
-        ASSERT_EQ(NITERS, deleteCount) << "Deletions missed!";
-    }  // Otherwise this is slow and probably pointless on a uniprocessor.
-}
-
-static wp<Bar> wpBuffer;
-static std::atomic<bool> wpBufferFull(false);
-
-// Wait until wpBufferFull has value val.
-static inline void wpWaitFor(bool val) {
-    while (wpBufferFull != val) {}
-}
-
-static void visit3AndRemove() {
-    if (sched_setaffinity(0, sizeof(cpu_set_t), &otherCpus) != 0) {
-        FAIL() << "setaffinity returned:" << errno;
-    }
-    for (int i = 0; i < NITERS; ++i) {
-        wpWaitFor(true);
-        {
-            sp<Bar> sp1 = wpBuffer.promote();
-            // We implicitly check that sp1 != NULL
-            sp1->mVisited2 = true;
-        }
-        wpBuffer = nullptr;
-        wpBufferFull = false;
-    }
-}
-
-TEST(RefBase, RacingPromotions) {
-    cpu_set_t origCpus;
-    cpu_set_t myCpus;
-    // Restrict us and the helper thread to disjoint cpu sets.
-    // This prevents us from getting scheduled against each other,
-    // which would be atrociously slow.
-    if (setExclusiveCpus(&origCpus, &myCpus, &otherCpus)) {
-        std::thread t(visit3AndRemove);
-        std::atomic<int> deleteCount(0);
-        if (sched_setaffinity(0, sizeof(cpu_set_t), &myCpus) != 0) {
-            FAIL() << "setaffinity returned:" << errno;
-        }
-        for (int i = 0; i < NITERS; ++i) {
-            Bar* bar = new Bar(&deleteCount);
-            wp<Bar> wp1(bar);
-            bar->mVisited1 = true;
-            if (i % (NITERS / 10) == 0) {
-                // Do this rarely, since it generates a log message.
-                wp1 = nullptr;  // No longer destroys the object.
-                wp1 = bar;
-            }
-            wpBuffer = wp1;
-            ASSERT_EQ(bar->getWeakRefs()->getWeakCount(), 2);
-            wpBufferFull = true;
-            // Promotion races with that in visit3AndRemove.
-            // This may or may not succeed, but it shouldn't interfere with
-            // the concurrent one.
-            sp<Bar> sp1 = wp1.promote();
-            wpWaitFor(false);  // Waits for other thread to drop strong pointer.
-            sp1 = nullptr;
-            // No strong pointers here.
-            sp1 = wp1.promote();
-            ASSERT_EQ(sp1.get(), nullptr) << "Dead wp promotion succeeded!";
-        }
-        t.join();
-        if (sched_setaffinity(0, sizeof(cpu_set_t), &origCpus) != 0) {
-            FAIL();
-        }
-        ASSERT_EQ(NITERS, deleteCount) << "Deletions missed!";
-    }  // Otherwise this is slow and probably pointless on a uniprocessor.
-}
diff --git a/libutils/tests/Singleton_test.cpp b/libutils/tests/Singleton_test.cpp
deleted file mode 100644
index 9acd3c3..0000000
--- a/libutils/tests/Singleton_test.cpp
+++ /dev/null
@@ -1,69 +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.
- */
-
-#define LOG_TAG "Singleton_test"
-
-#include <dlfcn.h>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <utils/Singleton.h>
-
-#include <gtest/gtest.h>
-
-#include "Singleton_test.h"
-
-namespace android {
-
-TEST(SingletonTest, bug35674422) {
-    std::string path = android::base::GetExecutableDirectory();
-    // libutils_tests_singleton1.so contains the ANDROID_SINGLETON_STATIC_INSTANCE
-    // definition of SingletonTestData, load it first.
-    std::string lib = android::base::StringPrintf("%s/libutils_tests_singleton1.so", path.c_str());
-    void* handle1 = dlopen(lib.c_str(), RTLD_NOW);
-    ASSERT_TRUE(handle1 != nullptr) << dlerror();
-
-    // libutils_tests_singleton2.so references SingletonTestData but should not
-    // have a definition
-    lib = android::base::StringPrintf("%s/libutils_tests_singleton2.so", path.c_str());
-    void* handle2 = dlopen(lib.c_str(), RTLD_NOW);
-    ASSERT_TRUE(handle2 != nullptr) << dlerror();
-
-    using has_fn_t = decltype(&singletonHasInstance);
-    using get_fn_t = decltype(&singletonGetInstanceContents);
-    using set_fn_t = decltype(&singletonSetInstanceContents);
-
-    has_fn_t has1 = reinterpret_cast<has_fn_t>(dlsym(handle1, "singletonHasInstance"));
-    ASSERT_TRUE(has1 != nullptr) << dlerror();
-    has_fn_t has2 = reinterpret_cast<has_fn_t>(dlsym(handle2, "singletonHasInstance"));
-    ASSERT_TRUE(has2 != nullptr) << dlerror();
-    get_fn_t get1 = reinterpret_cast<get_fn_t>(dlsym(handle1, "singletonGetInstanceContents"));
-    ASSERT_TRUE(get1 != nullptr) << dlerror();
-    get_fn_t get2 = reinterpret_cast<get_fn_t>(dlsym(handle2, "singletonGetInstanceContents"));
-    ASSERT_TRUE(get2 != nullptr) << dlerror();
-    set_fn_t set1 = reinterpret_cast<set_fn_t>(dlsym(handle2, "singletonSetInstanceContents"));
-    ASSERT_TRUE(set1 != nullptr) << dlerror();
-
-    EXPECT_FALSE(has1());
-    EXPECT_FALSE(has2());
-    set1(12345678U);
-    EXPECT_TRUE(has1());
-    EXPECT_TRUE(has2());
-    EXPECT_EQ(12345678U, get1());
-    EXPECT_EQ(12345678U, get2());
-}
-
-}
diff --git a/libutils/tests/TestHelpers.h b/libutils/tests/TestHelpers.h
deleted file mode 100644
index 6801cd7..0000000
--- a/libutils/tests/TestHelpers.h
+++ /dev/null
@@ -1,79 +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 TESTHELPERS_H
-#define TESTHELPERS_H
-
-#include <utils/threads.h>
-
-namespace android {
-
-class Pipe {
-public:
-    int sendFd;
-    int receiveFd;
-
-    Pipe() {
-        int fds[2];
-        ::pipe(fds);
-
-        receiveFd = fds[0];
-        sendFd = fds[1];
-    }
-
-    ~Pipe() {
-        if (sendFd != -1) {
-            ::close(sendFd);
-        }
-
-        if (receiveFd != -1) {
-            ::close(receiveFd);
-        }
-    }
-
-    status_t writeSignal() {
-        ssize_t nWritten = ::write(sendFd, "*", 1);
-        return nWritten == 1 ? 0 : -errno;
-    }
-
-    status_t readSignal() {
-        char buf[1];
-        ssize_t nRead = ::read(receiveFd, buf, 1);
-        return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno;
-    }
-};
-
-class DelayedTask : public Thread {
-    int mDelayMillis;
-
-public:
-    explicit DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { }
-
-protected:
-    virtual ~DelayedTask() { }
-
-    virtual void doTask() = 0;
-
-    virtual bool threadLoop() {
-        usleep(mDelayMillis * 1000);
-        doTask();
-        return false;
-    }
-};
-
-} // namespace android
-
-#endif // TESTHELPERS_H
diff --git a/libutils/tests/Vector_test.cpp b/libutils/tests/Vector_test.cpp
deleted file mode 100644
index e074a92..0000000
--- a/libutils/tests/Vector_test.cpp
+++ /dev/null
@@ -1,139 +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 "Vector_test"
-
-#define __STDC_LIMIT_MACROS
-#include <stdint.h>
-#include <unistd.h>
-
-#include <android/log.h>
-#include <gtest/gtest.h>
-#include <utils/Vector.h>
-
-namespace android {
-
-class VectorTest : public testing::Test {
-protected:
-    virtual void SetUp() {
-    }
-
-    virtual void TearDown() {
-    }
-
-public:
-};
-
-
-TEST_F(VectorTest, CopyOnWrite_CopyAndAddElements) {
-
-    Vector<int> vector;
-    Vector<int> other;
-    vector.setCapacity(8);
-
-    vector.add(1);
-    vector.add(2);
-    vector.add(3);
-
-    EXPECT_EQ(3U, vector.size());
-
-    // copy the vector
-    other = vector;
-
-    EXPECT_EQ(3U, other.size());
-
-    // add an element to the first vector
-    vector.add(4);
-
-    // make sure the sizes are correct
-    EXPECT_EQ(4U, vector.size());
-    EXPECT_EQ(3U, other.size());
-
-    // add an element to the copy
-    other.add(5);
-
-    // make sure the sizes are correct
-    EXPECT_EQ(4U, vector.size());
-    EXPECT_EQ(4U, other.size());
-
-    // make sure the content of both vectors are correct
-    EXPECT_EQ(vector[3], 4);
-    EXPECT_EQ(other[3], 5);
-}
-
-TEST_F(VectorTest, SetCapacity_Overflow) {
-  Vector<int> vector;
-  EXPECT_DEATH(vector.setCapacity(SIZE_MAX / sizeof(int) + 1), "Assertion failed");
-}
-
-TEST_F(VectorTest, SetCapacity_ShrinkBelowSize) {
-  Vector<int> vector;
-  vector.add(1);
-  vector.add(2);
-  vector.add(3);
-  vector.add(4);
-
-  vector.setCapacity(8);
-  ASSERT_EQ(8U, vector.capacity());
-  vector.setCapacity(2);
-  ASSERT_EQ(8U, vector.capacity());
-}
-
-TEST_F(VectorTest, _grow_OverflowSize) {
-  Vector<int> vector;
-  vector.add(1);
-
-  // 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");
-}
-
-TEST_F(VectorTest, _grow_OverflowCapacityDoubling) {
-  Vector<int> vector;
-
-  // 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");
-}
-
-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");
-}
-
-TEST_F(VectorTest, editArray_Shared) {
-  Vector<int> vector1;
-  vector1.add(1);
-  vector1.add(2);
-  vector1.add(3);
-  vector1.add(4);
-
-  Vector<int> vector2 = vector1;
-  ASSERT_EQ(vector1.array(), vector2.array());
-  // We must make a copy here, since we're not the exclusive owners
-  // of this array.
-  ASSERT_NE(vector1.editArray(), vector2.editArray());
-
-  // Vector doesn't implement operator ==.
-  ASSERT_EQ(vector1.size(), vector2.size());
-  for (size_t i = 0; i < vector1.size(); ++i) {
-    EXPECT_EQ(vector1[i], vector2[i]);
-  }
-}
-
-} // namespace android
diff --git a/libvndksupport/Android.bp b/libvndksupport/Android.bp
index e73b366..e5b536c 100644
--- a/libvndksupport/Android.bp
+++ b/libvndksupport/Android.bp
@@ -2,6 +2,7 @@
 
 cc_library {
     name: "libvndksupport",
+    native_bridge_supported: true,
     srcs: ["linker.c"],
     cflags: [
         "-Wall",
@@ -9,11 +10,20 @@
     ],
     local_include_dirs: ["include/vndksupport"],
     export_include_dirs: ["include"],
-    shared_libs: ["liblog"],
+    shared_libs: [
+        "libdl_android",
+        "liblog",
+    ],
+    version_script: "libvndksupport.map.txt",
+    stubs: {
+        symbol_file: "libvndksupport.map.txt",
+        versions: ["29"],
+    },
 }
 
 llndk_library {
     name: "libvndksupport",
+    native_bridge_supported: true,
     symbol_file: "libvndksupport.map.txt",
     export_include_dirs: ["include"],
 }
diff --git a/libvndksupport/include/vndksupport/linker.h b/libvndksupport/include/vndksupport/linker.h
index f509564..5f48c39 100644
--- a/libvndksupport/include/vndksupport/linker.h
+++ b/libvndksupport/include/vndksupport/linker.h
@@ -20,6 +20,16 @@
 extern "C" {
 #endif
 
+/*
+ * Returns whether the current process is a vendor process.
+ *
+ * Note that this is only checking what process is running and has nothing to
+ * do with what namespace the caller is loaded at.  For example, a VNDK-SP
+ * library loaded by SP-HAL calling this function may still get a 'false',
+ * because it is running in a system process.
+ */
+int android_is_in_vendor_process();
+
 void* android_load_sphal_library(const char* name, int flag);
 
 int android_unload_sphal_library(void* handle);
diff --git a/libvndksupport/libvndksupport.map.txt b/libvndksupport/libvndksupport.map.txt
index 16e38da..ac9a99c 100644
--- a/libvndksupport/libvndksupport.map.txt
+++ b/libvndksupport/libvndksupport.map.txt
@@ -1,7 +1,8 @@
 LIBVNDKSUPPORT {
   global:
-    android_load_sphal_library; # vndk
-    android_unload_sphal_library; # vndk
+    android_is_in_vendor_process; # vndk apex
+    android_load_sphal_library; # vndk apex
+    android_unload_sphal_library; # vndk apex
   local:
     *;
 };
diff --git a/libvndksupport/linker.c b/libvndksupport/linker.c
index bc5620b..84c2132 100644
--- a/libvndksupport/linker.c
+++ b/libvndksupport/linker.c
@@ -20,6 +20,8 @@
 
 #define LOG_TAG "vndksupport"
 #include <log/log.h>
+#include <sys/types.h>
+#include <unistd.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*);
@@ -45,6 +47,22 @@
     return vendor_namespace;
 }
 
+int android_is_in_vendor_process() {
+    // Special case init, since when init runs, ld.config.<ver>.txt hasn't been
+    // loaded (sysprop service isn't up for init to know <ver>).
+    if (getpid() == 1) {
+        return 0;
+    }
+    if (android_get_exported_namespace == NULL) {
+        ALOGD("android_get_exported_namespace() not available. Assuming system process.");
+        return 0;
+    }
+
+    // In vendor process, 'vndk' namespace is not visible, whereas in system
+    // process, it is.
+    return android_get_exported_namespace("vndk") == NULL;
+}
+
 void* android_load_sphal_library(const char* name, int flag) {
     struct android_namespace_t* vendor_namespace = get_vendor_namespace();
     if (vendor_namespace != NULL) {
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 075fb86..3843252 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -23,11 +23,36 @@
         "-D_FILE_OFFSET_BITS=64",
     ],
     cppflags: [
-        "-Wold-style-cast",
         // Incorrectly warns when C++11 empty brace {} initializer is used.
         // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
         "-Wno-missing-field-initializers",
+        "-Wconversion",
+        "-Wno-sign-conversion",
     ],
+
+    // Enable -Wold-style-cast only for non-Windows targets.  _islower_l,
+    // _isupper_l etc. in MinGW locale_win32.h (included from
+    // libcxx/include/__locale) has an old-style-cast.
+    target: {
+        not_windows: {
+            cppflags: [
+                "-Wold-style-cast",
+            ],
+        },
+    },
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+            "unsigned-integer-overflow",
+            "shift",
+            "integer-divide-by-zero",
+            "implicit-signed-integer-truncation",
+            // TODO: Fix crash when we enable this option
+            // "implicit-unsigned-integer-truncation",
+            // TODO: not tested yet.
+            // "implicit-integer-sign-change",
+        ],
+    },
 }
 
 cc_defaults {
@@ -58,9 +83,13 @@
     name: "libziparchive",
     host_supported: true,
     vendor_available: true,
+    recovery_available: true,
+    native_bridge_supported: true,
     vndk: {
         enabled: true,
     },
+    double_loadable: true,
+    export_shared_lib_headers: ["libbase"],
 
     defaults: [
         "libziparchive_defaults",
@@ -72,14 +101,6 @@
         "libz",
     ],
     target: {
-        android: {
-            shared_libs: [
-                "libutils",
-            ],
-        },
-        host: {
-            static_libs: ["libutils"],
-        },
         linux_bionic: {
             enabled: true,
         },
@@ -92,6 +113,10 @@
     host_supported: true,
     defaults: ["libziparchive_flags"],
 
+    data: [
+        "testdata/**/*",
+    ],
+
     srcs: [
         "entry_name_utils_test.cc",
         "zip_archive_test.cc",
@@ -116,6 +141,7 @@
             enabled: true,
         },
     },
+    test_suites: ["device-tests"],
 }
 
 // Performance benchmarks.
@@ -152,4 +178,5 @@
         "libbase",
         "libziparchive",
     ],
+    recovery_available: true,
 }
diff --git a/libziparchive/entry_name_utils-inl.h b/libziparchive/entry_name_utils-inl.h
index 5fc2fb4..10311b5 100644
--- a/libziparchive/entry_name_utils-inl.h
+++ b/libziparchive/entry_name_utils-inl.h
@@ -20,9 +20,15 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <limits>
+
 // Check if |length| bytes at |entry_name| constitute a valid entry name.
-// Entry names must be valid UTF-8 and must not contain '0'.
+// Entry names must be valid UTF-8 and must not contain '0'. They also must
+// fit into the central directory record.
 inline bool IsValidEntryName(const uint8_t* entry_name, const size_t length) {
+  if (length > std::numeric_limits<uint16_t>::max()) {
+    return false;
+  }
   for (size_t i = 0; i < length; ++i) {
     const uint8_t byte = entry_name[i];
     if (byte == 0) {
@@ -35,7 +41,8 @@
       return false;
     } else {
       // 2-5 byte sequences.
-      for (uint8_t first = byte << 1; first & 0x80; first <<= 1) {
+      for (uint8_t first = static_cast<uint8_t>((byte & 0x7f) << 1); first & 0x80;
+           first = static_cast<uint8_t>((first & 0x7f) << 1)) {
         ++i;
 
         // Missing continuation byte..
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index 018b1a9..e3ac114 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -14,17 +14,21 @@
  * limitations under the License.
  */
 
+#pragma once
+
 /*
  * Read-only access to Zip archives, with minimal heap allocation.
  */
-#ifndef LIBZIPARCHIVE_ZIPARCHIVE_H_
-#define LIBZIPARCHIVE_ZIPARCHIVE_H_
 
 #include <stdint.h>
 #include <string.h>
 #include <sys/cdefs.h>
 #include <sys/types.h>
-#include <utils/Compat.h>
+
+#include <string>
+#include <string_view>
+
+#include "android-base/off64_t.h"
 
 /* Zip compression methods we support */
 enum {
@@ -32,32 +36,6 @@
   kCompressDeflated = 8,  // standard deflate
 };
 
-struct ZipString {
-  const uint8_t* name;
-  uint16_t name_length;
-
-  ZipString() {}
-
-  /*
-   * entry_name has to be an c-style string with only ASCII characters.
-   */
-  explicit ZipString(const char* entry_name);
-
-  bool operator==(const ZipString& rhs) const {
-    return name && (name_length == rhs.name_length) && (memcmp(name, rhs.name, name_length) == 0);
-  }
-
-  bool StartsWith(const ZipString& prefix) const {
-    return name && (name_length >= prefix.name_length) &&
-           (memcmp(name, prefix.name, prefix.name_length) == 0);
-  }
-
-  bool EndsWith(const ZipString& suffix) const {
-    return name && (name_length >= suffix.name_length) &&
-           (memcmp(name + name_length - suffix.name_length, suffix.name, suffix.name_length) == 0);
-  }
-};
-
 /*
  * Represents information about a zip entry in a zip file.
  */
@@ -103,7 +81,8 @@
   off64_t offset;
 };
 
-typedef void* ZipArchiveHandle;
+struct ZipArchive;
+typedef ZipArchive* ZipArchiveHandle;
 
 /*
  * Open a Zip archive, and sets handle to the value of the opaque
@@ -144,11 +123,10 @@
  * this handle for any further operations without an intervening
  * call to one of the OpenArchive variants.
  */
-void CloseArchive(ZipArchiveHandle handle);
+void CloseArchive(ZipArchiveHandle archive);
 
 /*
- * Find an entry in the Zip archive, by name. |entryName| must be a null
- * terminated string, and |data| must point to a writeable memory location.
+ * Find an entry in the Zip archive, by name. |data| must be non-null.
  *
  * Returns 0 if an entry is found, and populates |data| with information
  * about this entry. Returns negative values otherwise.
@@ -162,7 +140,7 @@
  * On non-Windows platforms this method does not modify internal state and
  * can be called concurrently.
  */
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName, ZipEntry* data);
+int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName, ZipEntry* data);
 
 /*
  * Start iterating over all entries of a zip file. The order of iteration
@@ -177,8 +155,9 @@
  *
  * Returns 0 on success and negative values on failure.
  */
-int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, const ZipString* optional_prefix,
-                       const ZipString* optional_suffix);
+int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
+                       const std::string_view optional_prefix = "",
+                       const std::string_view optional_suffix = "");
 
 /*
  * Advance to the next element in the zipfile in iteration order.
@@ -186,7 +165,8 @@
  * Returns 0 on success, -1 if there are no more elements in this
  * archive and lower negative values on failure.
  */
-int32_t Next(void* cookie, ZipEntry* data, ZipString* name);
+int32_t Next(void* cookie, ZipEntry* data, std::string* name);
+int32_t Next(void* cookie, ZipEntry* data, std::string_view* name);
 
 /*
  * End iteration over all entries of a zip file and frees the memory allocated
@@ -203,7 +183,7 @@
  *
  * Returns 0 on success and negative values on failure.
  */
-int32_t ExtractEntryToFile(ZipArchiveHandle handle, ZipEntry* entry, int fd);
+int32_t ExtractEntryToFile(ZipArchiveHandle archive, ZipEntry* entry, int fd);
 
 /**
  * Uncompress a given zip entry to the memory region at |begin| and of
@@ -213,9 +193,9 @@
  *
  * Returns 0 on success and negative values on failure.
  */
-int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size);
+int32_t ExtractToMemory(ZipArchiveHandle archive, ZipEntry* entry, uint8_t* begin, uint32_t size);
 
-int GetFileDescriptor(const ZipArchiveHandle handle);
+int GetFileDescriptor(const ZipArchiveHandle archive);
 
 const char* ErrorCodeString(int32_t error_code);
 
@@ -226,7 +206,7 @@
  * Stream the uncompressed data through the supplied function,
  * passing cookie to it each time it gets called.
  */
-int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry,
+int32_t ProcessZipEntryContents(ZipArchiveHandle archive, ZipEntry* entry,
                                 ProcessZipEntryFunction func, void* cookie);
 #endif
 
@@ -272,5 +252,3 @@
 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 b4766f8..8c6ca79 100644
--- a/libziparchive/include/ziparchive/zip_archive_stream_entry.h
+++ b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
@@ -15,12 +15,13 @@
  */
 
 // Read-only stream access to Zip archives entries.
-#ifndef LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
-#define LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
+#pragma once
+
+#include <ziparchive/zip_archive.h>
 
 #include <vector>
 
-#include <ziparchive/zip_archive.h>
+#include "android-base/off64_t.h"
 
 class ZipArchiveStreamEntry {
  public:
@@ -43,5 +44,3 @@
   off64_t offset_ = 0;
   uint32_t crc32_ = 0u;
 };
-
-#endif  // LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
diff --git a/libziparchive/include/ziparchive/zip_writer.h b/libziparchive/include/ziparchive/zip_writer.h
index c350a27..a2a0dbf 100644
--- a/libziparchive/include/ziparchive/zip_writer.h
+++ b/libziparchive/include/ziparchive/zip_writer.h
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
-#ifndef LIBZIPARCHIVE_ZIPWRITER_H_
-#define LIBZIPARCHIVE_ZIPWRITER_H_
+#pragma once
 
 #include <cstdio>
 #include <ctime>
 
 #include <memory>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "android-base/macros.h"
-#include "utils/Compat.h"
+#include "android-base/off64_t.h"
 
 struct z_stream_s;
 typedef struct z_stream_s z_stream;
@@ -77,7 +77,7 @@
     uint32_t uncompressed_size;
     uint16_t last_mod_time;
     uint16_t last_mod_date;
-    uint32_t padding_length;
+    uint16_t padding_length;
     off64_t local_file_header_offset;
   };
 
@@ -91,10 +91,10 @@
   explicit ZipWriter(FILE* f);
 
   // Move constructor.
-  ZipWriter(ZipWriter&& zipWriter);
+  ZipWriter(ZipWriter&& zipWriter) noexcept;
 
   // Move assignment.
-  ZipWriter& operator=(ZipWriter&& zipWriter);
+  ZipWriter& operator=(ZipWriter&& zipWriter) noexcept;
 
   /**
    * Starts a new zip entry with the given path and flags.
@@ -102,7 +102,7 @@
    * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
    * Returns 0 on success, and an error value < 0 on failure.
    */
-  int32_t StartEntry(const char* path, size_t flags);
+  int32_t StartEntry(std::string_view path, size_t flags);
 
   /**
    * Starts a new zip entry with the given path and flags, where the
@@ -112,17 +112,17 @@
    * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
    * Returns 0 on success, and an error value < 0 on failure.
    */
-  int32_t StartAlignedEntry(const char* path, size_t flags, uint32_t alignment);
+  int32_t StartAlignedEntry(std::string_view path, size_t flags, uint32_t alignment);
 
   /**
    * Same as StartEntry(const char*, size_t), but sets a last modified time for the entry.
    */
-  int32_t StartEntryWithTime(const char* path, size_t flags, time_t time);
+  int32_t StartEntryWithTime(std::string_view path, size_t flags, time_t time);
 
   /**
    * Same as StartAlignedEntry(const char*, size_t), but sets a last modified time for the entry.
    */
-  int32_t StartAlignedEntryWithTime(const char* path, size_t flags, time_t time, uint32_t alignment);
+  int32_t StartAlignedEntryWithTime(std::string_view path, size_t flags, time_t time, uint32_t alignment);
 
   /**
    * Writes bytes to the zip file for the previously started zip entry.
@@ -162,8 +162,8 @@
 
   int32_t HandleError(int32_t error_code);
   int32_t PrepareDeflate();
-  int32_t StoreBytes(FileEntry* file, const void* data, size_t len);
-  int32_t CompressBytes(FileEntry* file, const void* data, size_t len);
+  int32_t StoreBytes(FileEntry* file, const void* data, uint32_t len);
+  int32_t CompressBytes(FileEntry* file, const void* data, uint32_t len);
   int32_t FlushCompressedBytes(FileEntry* file);
 
   enum class State {
@@ -183,5 +183,3 @@
   std::unique_ptr<z_stream, void (*)(z_stream*)> z_stream_;
   std::vector<uint8_t> buffer_;
 };
-
-#endif /* LIBZIPARCHIVE_ZIPWRITER_H_ */
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
index 6756007..426325e 100644
--- a/libziparchive/unzip.cpp
+++ b/libziparchive/unzip.cpp
@@ -17,6 +17,7 @@
 #include <errno.h>
 #include <error.h>
 #include <fcntl.h>
+#include <fnmatch.h>
 #include <getopt.h>
 #include <inttypes.h>
 #include <stdio.h>
@@ -52,9 +53,21 @@
 static uint64_t total_compressed_length = 0;
 static size_t file_count = 0;
 
-static bool Filter(const std::string& name) {
-  if (!excludes.empty() && excludes.find(name) != excludes.end()) return true;
-  if (!includes.empty() && includes.find(name) == includes.end()) return true;
+static bool ShouldInclude(const std::string& name) {
+  // Explicitly excluded?
+  if (!excludes.empty()) {
+    for (const auto& exclude : excludes) {
+      if (!fnmatch(exclude.c_str(), name.c_str(), 0)) return false;
+    }
+  }
+
+  // Implicitly included?
+  if (includes.empty()) return true;
+
+  // Explicitly included?
+  for (const auto& include : includes) {
+    if (!fnmatch(include.c_str(), name.c_str(), 0)) return true;
+  }
   return false;
 }
 
@@ -72,7 +85,7 @@
 
 static int CompressionRatio(int64_t uncompressed, int64_t compressed) {
   if (uncompressed == 0) return 0;
-  return (100LL * (uncompressed - compressed)) / uncompressed;
+  return static_cast<int>((100LL * (uncompressed - compressed)) / uncompressed);
 }
 
 static void MaybeShowHeader() {
@@ -236,16 +249,15 @@
   // libziparchive iteration order doesn't match the central directory.
   // We could sort, but that would cost extra and wouldn't match either.
   void* cookie;
-  int err = StartIteration(zah, &cookie, nullptr, nullptr);
+  int err = StartIteration(zah, &cookie);
   if (err != 0) {
     error(1, 0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err));
   }
 
   ZipEntry entry;
-  ZipString string;
-  while ((err = Next(cookie, &entry, &string)) >= 0) {
-    std::string name(string.name, string.name + string.name_length);
-    if (!Filter(name)) ProcessOne(zah, entry, name);
+  std::string name;
+  while ((err = Next(cookie, &entry, &name)) >= 0) {
+    if (ShouldInclude(name)) ProcessOne(zah, entry, name);
   }
 
   if (err < -1) error(1, 0, "failed iterating %s: %s", archive_name, ErrorCodeString(err));
@@ -260,7 +272,8 @@
 
   printf(
       "\n"
-      "Extract FILEs from ZIP archive. Default is all files.\n"
+      "Extract FILEs from ZIP archive. Default is all files. Both the include and\n"
+      "exclude (-x) lists use shell glob patterns.\n"
       "\n"
       "-d DIR	Extract into DIR\n"
       "-l	List contents (-lq excludes archive name, -lv is verbose)\n"
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 5e5e7af..c95b035 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -20,7 +20,8 @@
 
 #define LOG_TAG "ziparchive"
 
-#include <assert.h>
+#include "ziparchive/zip_archive.h"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -33,14 +34,22 @@
 #include <memory>
 #include <vector>
 
+#if defined(__APPLE__)
+#define lseek64 lseek
+#endif
+
+#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
+#include <android-base/mapped_file.h>
 #include <android-base/memory.h>
+#include <android-base/strings.h>
+#include <android-base/utf8.h>
 #include <log/log.h>
-#include <utils/Compat.h>
-#include <utils/FileMap.h>
-#include "ziparchive/zip_archive.h"
 #include "zlib.h"
 
 #include "entry_name_utils-inl.h"
@@ -53,12 +62,6 @@
 // specified in the local file header and the central directory.
 static const bool kCrcChecksEnabled = false;
 
-// This is for windows. If we don't open a file in binary mode, weird
-// things will happen.
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
 // The maximum number of bytes to scan backwards for the EOCD start.
 static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
 
@@ -99,51 +102,36 @@
   return val;
 }
 
-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;
-
-  while (len--) {
-    hash = hash * 31 + *str++;
-  }
-
-  return hash;
-#endif
+static uint32_t ComputeHash(std::string_view name) {
+  return static_cast<uint32_t>(std::hash<std::string_view>{}(name));
 }
 
 /*
  * Convert a ZipEntry to a hash table index, verifying that it's in a
  * valid range.
  */
-static int64_t EntryToIndex(const ZipString* hash_table, const uint32_t hash_table_size,
-                            const ZipString& name) {
+static int64_t EntryToIndex(const ZipStringOffset* hash_table, const uint32_t hash_table_size,
+                            std::string_view name, const uint8_t* start) {
   const uint32_t hash = ComputeHash(name);
 
   // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
   uint32_t ent = hash & (hash_table_size - 1);
-  while (hash_table[ent].name != NULL) {
-    if (hash_table[ent] == name) {
+  while (hash_table[ent].name_offset != 0) {
+    if (hash_table[ent].ToStringView(start) == name) {
       return ent;
     }
-
     ent = (ent + 1) & (hash_table_size - 1);
   }
 
-  ALOGV("Zip: Unable to find entry %.*s", name.name_length, name.name);
+  ALOGV("Zip: Unable to find entry %.*s", static_cast<int>(name.size()), name.data());
   return kEntryNotFound;
 }
 
 /*
  * Add a new entry to the hash table.
  */
-static int32_t AddToHash(ZipString* hash_table, const uint64_t hash_table_size,
-                         const ZipString& name) {
+static int32_t AddToHash(ZipStringOffset* hash_table, const uint32_t hash_table_size,
+                         std::string_view name, const uint8_t* start) {
   const uint64_t hash = ComputeHash(name);
   uint32_t ent = hash & (hash_table_size - 1);
 
@@ -151,22 +139,70 @@
    * We over-allocated the table, so we're guaranteed to find an empty slot.
    * Further, we guarantee that the hashtable size is not 0.
    */
-  while (hash_table[ent].name != NULL) {
-    if (hash_table[ent] == name) {
-      // We've found a duplicate entry. We don't accept it
-      ALOGW("Zip: Found duplicate entry %.*s", name.name_length, name.name);
+  while (hash_table[ent].name_offset != 0) {
+    if (hash_table[ent].ToStringView(start) == name) {
+      // We've found a duplicate entry. We don't accept duplicates.
+      ALOGW("Zip: Found duplicate entry %.*s", static_cast<int>(name.size()), name.data());
       return kDuplicateEntry;
     }
     ent = (ent + 1) & (hash_table_size - 1);
   }
 
-  hash_table[ent].name = name.name;
-  hash_table[ent].name_length = name.name_length;
+  // `name` has already been validated before entry.
+  const char* start_char = reinterpret_cast<const char*>(start);
+  hash_table[ent].name_offset = static_cast<uint32_t>(name.data() - start_char);
+  hash_table[ent].name_length = static_cast<uint16_t>(name.size());
   return 0;
 }
 
+#if defined(__BIONIC__)
+uint64_t GetOwnerTag(const ZipArchive* archive) {
+  return android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_ZIPARCHIVE,
+                                        reinterpret_cast<uint64_t>(archive));
+}
+#endif
+
+ZipArchive::ZipArchive(const int fd, bool assume_ownership)
+    : mapped_zip(fd),
+      close_file(assume_ownership),
+      directory_offset(0),
+      central_directory(),
+      directory_map(),
+      num_entries(0),
+      hash_table_size(0),
+      hash_table(nullptr) {
+#if defined(__BIONIC__)
+  if (assume_ownership) {
+    android_fdsan_exchange_owner_tag(fd, 0, GetOwnerTag(this));
+  }
+#endif
+}
+
+ZipArchive::ZipArchive(void* address, size_t length)
+    : mapped_zip(address, length),
+      close_file(false),
+      directory_offset(0),
+      central_directory(),
+      directory_map(),
+      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(), GetOwnerTag(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) {
+                                    off64_t file_length, uint32_t read_amount,
+                                    uint8_t* scan_buffer) {
   const off64_t search_start = file_length - read_amount;
 
   if (!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
@@ -181,7 +217,8 @@
    * doing an initial minimal read; if we don't find it, retry with a
    * second read as above.)
    */
-  int i = read_amount - sizeof(EocdRecord);
+  CHECK_LE(read_amount, std::numeric_limits<int32_t>::max());
+  int32_t i = read_amount - sizeof(EocdRecord);
   for (; i >= 0; i--) {
     if (scan_buffer[i] == 0x50) {
       uint32_t* sig_addr = reinterpret_cast<uint32_t*>(&scan_buffer[i]);
@@ -216,11 +253,6 @@
   if (static_cast<off64_t>(eocd->cd_start_offset) + eocd->cd_size > eocd_offset) {
     ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
           eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
-#if defined(__ANDROID__)
-    if (eocd->cd_start_offset + eocd->cd_size <= eocd_offset) {
-      android_errorWriteLog(0x534e4554, "31251826");
-    }
-#endif
     return kInvalidOffset;
   }
   if (eocd->num_records == 0) {
@@ -238,8 +270,7 @@
    * in archive.
    */
 
-  if (!archive->InitializeCentralDirectory(debug_file_name,
-                                           static_cast<off64_t>(eocd->cd_start_offset),
+  if (!archive->InitializeCentralDirectory(static_cast<off64_t>(eocd->cd_start_offset),
                                            static_cast<size_t>(eocd->cd_size))) {
     ALOGE("Zip: failed to intialize central directory.\n");
     return kMmapFailed;
@@ -290,9 +321,9 @@
    *
    * We start by pulling in the last part of the file.
    */
-  off64_t read_amount = kMaxEOCDSearch;
+  uint32_t read_amount = kMaxEOCDSearch;
   if (file_length < read_amount) {
-    read_amount = file_length;
+    read_amount = static_cast<uint32_t>(file_length);
   }
 
   std::vector<uint8_t> scan_buffer(read_amount);
@@ -319,10 +350,10 @@
    */
   archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
   archive->hash_table =
-      reinterpret_cast<ZipString*>(calloc(archive->hash_table_size, sizeof(ZipString)));
+      reinterpret_cast<ZipStringOffset*>(calloc(archive->hash_table_size, sizeof(ZipStringOffset)));
   if (archive->hash_table == nullptr) {
     ALOGW("Zip: unable to allocate the %u-entry hash_table, entry size: %zu",
-          archive->hash_table_size, sizeof(ZipString));
+          archive->hash_table_size, sizeof(ZipStringOffset));
     return -1;
   }
 
@@ -360,22 +391,21 @@
     const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
 
     if (file_name + file_name_length > cd_end) {
-      ALOGW(
-          "Zip: file name boundary exceeds the central directory range, file_name_length: "
-          "%" PRIx16 ", cd_length: %zu",
-          file_name_length, cd_length);
+      ALOGW("Zip: file name for entry %" PRIu16
+            " exceeds the central directory range, file_name_length: %" PRIu16 ", cd_length: %zu",
+            i, file_name_length, cd_length);
       return -1;
     }
-    /* check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters */
+    // Check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters.
     if (!IsValidEntryName(file_name, file_name_length)) {
+      ALOGW("Zip: invalid file name at entry %" PRIu16, i);
       return -1;
     }
 
-    /* add the CDE filename to the hash table */
-    ZipString entry_name;
-    entry_name.name = file_name;
-    entry_name.name_length = file_name_length;
-    const int add_result = AddToHash(archive->hash_table, archive->hash_table_size, entry_name);
+    // Add the CDE filename to the hash table.
+    std::string_view entry_name{reinterpret_cast<const char*>(file_name), file_name_length};
+    const int add_result = AddToHash(archive->hash_table, archive->hash_table_size, entry_name,
+                                     archive->central_directory.GetBasePtr());
     if (add_result != 0) {
       ALOGW("Zip: Error adding entry to hash table %d", add_result);
       return add_result;
@@ -429,7 +459,7 @@
 }
 
 int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
-  const int fd = open(fileName, O_RDONLY | O_BINARY, 0);
+  const int fd = ::android::base::utf8::open(fileName, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
   ZipArchive* archive = new ZipArchive(fd, true);
   *handle = archive;
 
@@ -451,8 +481,7 @@
 /*
  * Close a ZipArchive, closing the file and freeing the contents.
  */
-void CloseArchive(ZipArchiveHandle handle) {
-  ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
+void CloseArchive(ZipArchiveHandle archive) {
   ALOGV("Closing archive %p", archive);
   delete archive;
 }
@@ -489,19 +518,19 @@
   return 0;
 }
 
-static int32_t FindEntry(const ZipArchive* archive, const int ent, ZipEntry* data) {
+static int32_t FindEntry(const ZipArchive* archive, const int32_t ent, ZipEntry* data) {
   const uint16_t nameLen = archive->hash_table[ent].name_length;
 
   // Recover the start of the central directory entry from the filename
   // pointer.  The filename is the first entry past the fixed-size data,
   // so we can just subtract back from that.
-  const uint8_t* ptr = archive->hash_table[ent].name;
+  const uint8_t* base_ptr = archive->central_directory.GetBasePtr();
+  const uint8_t* ptr = base_ptr + archive->hash_table[ent].name_offset;
   ptr -= sizeof(CentralDirectoryRecord);
 
   // This is the base of our mmapped region, we have to sanity check that
   // the name that's in the hash table is a pointer to a location within
   // this mapped region.
-  const uint8_t* base_ptr = archive->central_directory.GetBasePtr();
   if (ptr < base_ptr || ptr > base_ptr + archive->central_directory.GetMapLength()) {
     ALOGW("Zip: Invalid entry pointer");
     return kInvalidOffset;
@@ -593,25 +622,24 @@
 
   // Check that the local file header name matches the declared
   // name in the central directory.
-  if (lfh->file_name_length == nameLen) {
-    const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
-    if (name_offset + lfh->file_name_length > cd_offset) {
-      ALOGW("Zip: Invalid declared length");
-      return kInvalidOffset;
-    }
-
-    std::vector<uint8_t> name_buf(nameLen);
-    if (!archive->mapped_zip.ReadAtOffset(name_buf.data(), nameLen, name_offset)) {
-      ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
-      return kIoError;
-    }
-
-    if (memcmp(archive->hash_table[ent].name, name_buf.data(), nameLen)) {
-      return kInconsistentInformation;
-    }
-
-  } else {
-    ALOGW("Zip: lfh name did not match central directory.");
+  if (lfh->file_name_length != nameLen) {
+    ALOGW("Zip: lfh name length did not match central directory");
+    return kInconsistentInformation;
+  }
+  const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
+  if (name_offset + lfh->file_name_length > cd_offset) {
+    ALOGW("Zip: lfh name has invalid declared length");
+    return kInvalidOffset;
+  }
+  std::vector<uint8_t> name_buf(nameLen);
+  if (!archive->mapped_zip.ReadAtOffset(name_buf.data(), nameLen, name_offset)) {
+    ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
+    return kIoError;
+  }
+  const std::string_view entry_name =
+      archive->hash_table[ent].ToStringView(archive->central_directory.GetBasePtr());
+  if (memcmp(entry_name.data(), name_buf.data(), nameLen) != 0) {
+    ALOGW("Zip: lfh name did not match central directory");
     return kInconsistentInformation;
   }
 
@@ -642,54 +670,32 @@
 }
 
 struct IterationHandle {
-  uint32_t position;
-  // We're not using vector here because this code is used in the Windows SDK
-  // where the STL is not available.
-  ZipString prefix;
-  ZipString suffix;
   ZipArchive* archive;
 
-  IterationHandle(const ZipString* in_prefix, const ZipString* in_suffix) {
-    if (in_prefix) {
-      uint8_t* name_copy = new uint8_t[in_prefix->name_length];
-      memcpy(name_copy, in_prefix->name, in_prefix->name_length);
-      prefix.name = name_copy;
-      prefix.name_length = in_prefix->name_length;
-    } else {
-      prefix.name = NULL;
-      prefix.name_length = 0;
-    }
-    if (in_suffix) {
-      uint8_t* name_copy = new uint8_t[in_suffix->name_length];
-      memcpy(name_copy, in_suffix->name, in_suffix->name_length);
-      suffix.name = name_copy;
-      suffix.name_length = in_suffix->name_length;
-    } else {
-      suffix.name = NULL;
-      suffix.name_length = 0;
-    }
-  }
+  std::string prefix;
+  std::string suffix;
 
-  ~IterationHandle() {
-    delete[] prefix.name;
-    delete[] suffix.name;
-  }
+  uint32_t position = 0;
+
+  IterationHandle(ZipArchive* archive, std::string_view in_prefix, std::string_view in_suffix)
+      : archive(archive), prefix(in_prefix), suffix(in_suffix) {}
 };
 
-int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, const ZipString* optional_prefix,
-                       const ZipString* optional_suffix) {
-  ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
-
+int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
+                       const std::string_view optional_prefix,
+                       const std::string_view optional_suffix) {
   if (archive == NULL || archive->hash_table == NULL) {
     ALOGW("Zip: Invalid ZipArchiveHandle");
     return kInvalidHandle;
   }
 
-  IterationHandle* cookie = new IterationHandle(optional_prefix, optional_suffix);
-  cookie->position = 0;
-  cookie->archive = archive;
+  if (optional_prefix.size() > static_cast<size_t>(UINT16_MAX) ||
+      optional_suffix.size() > static_cast<size_t>(UINT16_MAX)) {
+    ALOGW("Zip: prefix/suffix too long");
+    return kInvalidEntryName;
+  }
 
-  *cookie_ptr = cookie;
+  *cookie_ptr = new IterationHandle(archive, optional_prefix, optional_suffix);
   return 0;
 }
 
@@ -697,26 +703,36 @@
   delete reinterpret_cast<IterationHandle*>(cookie);
 }
 
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName, ZipEntry* data) {
-  const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
-  if (entryName.name_length == 0) {
-    ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
+int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName,
+                  ZipEntry* data) {
+  if (entryName.empty() || entryName.size() > static_cast<size_t>(UINT16_MAX)) {
+    ALOGW("Zip: Invalid filename of length %zu", entryName.size());
     return kInvalidEntryName;
   }
 
-  const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size, entryName);
-
+  const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size, entryName,
+                                   archive->central_directory.GetBasePtr());
   if (ent < 0) {
-    ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
-    return ent;
+    ALOGV("Zip: Could not find entry %.*s", static_cast<int>(entryName.size()), entryName.data());
+    return static_cast<int32_t>(ent);  // kEntryNotFound is safe to truncate.
   }
-
-  return FindEntry(archive, ent, data);
+  // We know there are at most hash_table_size entries, safe to truncate.
+  return FindEntry(archive, static_cast<uint32_t>(ent), data);
 }
 
-int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
+int32_t Next(void* cookie, ZipEntry* data, std::string* name) {
+  std::string_view sv;
+  int32_t result = Next(cookie, data, &sv);
+  if (result == 0 && name) {
+    *name = std::string(sv);
+  }
+  return result;
+}
+
+int32_t Next(void* cookie, ZipEntry* data, std::string_view* name) {
   IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
   if (handle == NULL) {
+    ALOGW("Zip: Null ZipArchiveHandle");
     return kInvalidHandle;
   }
 
@@ -728,19 +744,17 @@
 
   const uint32_t currentOffset = handle->position;
   const uint32_t hash_table_length = archive->hash_table_size;
-  const ZipString* hash_table = archive->hash_table;
-
+  const ZipStringOffset* hash_table = archive->hash_table;
   for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
-    if (hash_table[i].name != NULL &&
-        (handle->prefix.name_length == 0 || hash_table[i].StartsWith(handle->prefix)) &&
-        (handle->suffix.name_length == 0 || hash_table[i].EndsWith(handle->suffix))) {
+    const std::string_view entry_name =
+        hash_table[i].ToStringView(archive->central_directory.GetBasePtr());
+    if (hash_table[i].name_offset != 0 && (android::base::StartsWith(entry_name, handle->prefix) &&
+                                           android::base::EndsWith(entry_name, handle->suffix))) {
       handle->position = (i + 1);
       const int error = FindEntry(archive, i, data);
-      if (!error) {
-        name->name = hash_table[i].name;
-        name->name_length = hash_table[i].name_length;
+      if (!error && name) {
+        *name = entry_name;
       }
-
       return error;
     }
   }
@@ -758,7 +772,7 @@
 
   virtual bool Append(uint8_t* buf, size_t buf_size) override {
     if (bytes_written_ + buf_size > size_) {
-      ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", size_,
+      ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", size_,
             bytes_written_ + buf_size);
       return false;
     }
@@ -793,7 +807,6 @@
       return FileWriter{};
     }
 
-    int result = 0;
 #if defined(__linux__)
     if (declared_length > 0) {
       // Make sure we have enough space on the volume to extract the compressed
@@ -805,7 +818,7 @@
       // EOPNOTSUPP error when issued in other filesystems.
       // Hence, check for the return error code before concluding that the
       // disk does not have enough space.
-      result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
+      long 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",
               static_cast<int64_t>(declared_length), static_cast<int64_t>(current_offset),
@@ -823,7 +836,7 @@
 
     // Block device doesn't support ftruncate(2).
     if (!S_ISBLK(sb.st_mode)) {
-      result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
+      long result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
       if (result == -1) {
         ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
               static_cast<int64_t>(declared_length + current_offset), strerror(errno));
@@ -834,7 +847,7 @@
     return FileWriter(fd, declared_length);
   }
 
-  FileWriter(FileWriter&& other)
+  FileWriter(FileWriter&& other) noexcept
       : fd_(other.fd_),
         declared_length_(other.declared_length_),
         total_bytes_written_(other.total_bytes_written_) {
@@ -845,7 +858,7 @@
 
   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_,
+      ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", declared_length_,
             total_bytes_written_ + buf_size);
       return false;
     }
@@ -854,7 +867,7 @@
     if (result) {
       total_bytes_written_ += buf_size;
     } else {
-      ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno));
+      ALOGW("Zip: unable to write %zu bytes to file; %s", buf_size, strerror(errno));
     }
 
     return result;
@@ -942,16 +955,16 @@
   std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
 
   const bool compute_crc = (crc_out != nullptr);
-  uint64_t crc = 0;
+  uLong crc = 0;
   uint32_t remaining_bytes = compressed_length;
   do {
     /* read as much as we can */
     if (zstream.avail_in == 0) {
-      const size_t read_size = (remaining_bytes > kBufSize) ? kBufSize : remaining_bytes;
+      const uint32_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));
+        ALOGW("Zip: inflate read failed, getSize = %u: %s", read_size, strerror(errno));
         return kIoError;
       }
 
@@ -975,7 +988,8 @@
       if (!writer->Append(&write_buf[0], write_size)) {
         return kIoError;
       } else if (compute_crc) {
-        crc = crc32(crc, &write_buf[0], write_size);
+        DCHECK_LE(write_size, kBufSize);
+        crc = crc32(crc, &write_buf[0], static_cast<uint32_t>(write_size));
       }
 
       zstream.next_out = &write_buf[0];
@@ -983,7 +997,7 @@
     }
   } while (zerr == Z_OK);
 
-  assert(zerr == Z_STREAM_END); /* other errors should've been caught */
+  CHECK_EQ(zerr, Z_STREAM_END); /* other errors should've been caught */
 
   // NOTE: zstream.adler is always set to 0, because we're using the -MAX_WBITS
   // "feature" of zlib to tell it there won't be a zlib file header. zlib
@@ -1020,17 +1034,17 @@
 
   const uint32_t length = entry->uncompressed_length;
   uint32_t count = 0;
-  uint64_t crc = 0;
+  uLong 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.
-    const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
+    const uint32_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
 
     // 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",
+      ALOGW("CopyFileToFile: copy read failed, block_size = %u, offset = %" PRId64 ": %s",
             block_size, static_cast<int64_t>(offset), strerror(errno));
       return kIoError;
     }
@@ -1047,8 +1061,7 @@
   return 0;
 }
 
-int32_t ExtractToWriter(ZipArchiveHandle handle, ZipEntry* entry, zip_archive::Writer* writer) {
-  ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
+int32_t ExtractToWriter(ZipArchiveHandle archive, ZipEntry* entry, zip_archive::Writer* writer) {
   const uint16_t method = entry->method;
 
   // this should default to kUnknownCompressionMethod.
@@ -1076,18 +1089,18 @@
   return return_value;
 }
 
-int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size) {
+int32_t ExtractToMemory(ZipArchiveHandle archive, ZipEntry* entry, uint8_t* begin, uint32_t size) {
   MemoryWriter writer(begin, size);
-  return ExtractToWriter(handle, entry, &writer);
+  return ExtractToWriter(archive, entry, &writer);
 }
 
-int32_t ExtractEntryToFile(ZipArchiveHandle handle, ZipEntry* entry, int fd) {
+int32_t ExtractEntryToFile(ZipArchiveHandle archive, ZipEntry* entry, int fd) {
   auto writer = FileWriter::Create(fd, entry);
   if (!writer.IsValid()) {
     return kIoError;
   }
 
-  return ExtractToWriter(handle, entry, &writer);
+  return ExtractToWriter(archive, entry, &writer);
 }
 
 const char* ErrorCodeString(int32_t error_code) {
@@ -1104,14 +1117,8 @@
   return "Unknown return code";
 }
 
-int GetFileDescriptor(const ZipArchiveHandle handle) {
-  return reinterpret_cast<ZipArchive*>(handle)->mapped_zip.GetFileDescriptor();
-}
-
-ZipString::ZipString(const char* entry_name) : name(reinterpret_cast<const uint8_t*>(entry_name)) {
-  size_t len = strlen(entry_name);
-  CHECK_LE(len, static_cast<size_t>(UINT16_MAX));
-  name_length = static_cast<uint16_t>(len);
+int GetFileDescriptor(const ZipArchiveHandle archive) {
+  return archive->mapped_zip.GetFileDescriptor();
 }
 
 #if !defined(_WIN32)
@@ -1129,10 +1136,10 @@
   void* cookie_;
 };
 
-int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry,
+int32_t ProcessZipEntryContents(ZipArchiveHandle archive, ZipEntry* entry,
                                 ProcessZipEntryFunction func, void* cookie) {
   ProcessWriter writer(func, cookie);
-  return ExtractToWriter(handle, entry, &writer);
+  return ExtractToWriter(archive, entry, &writer);
 }
 
 #endif  //! defined(_WIN32)
@@ -1191,16 +1198,14 @@
   length_ = cd_size;
 }
 
-bool ZipArchive::InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
-                                            size_t cd_size) {
+bool ZipArchive::InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size) {
   if (mapped_zip.HasFd()) {
-    if (!directory_map->create(debug_file_name, mapped_zip.GetFileDescriptor(), cd_start_offset,
-                               cd_size, true /* read only */)) {
-      return false;
-    }
+    directory_map = android::base::MappedFile::FromFd(mapped_zip.GetFileDescriptor(),
+                                                      cd_start_offset, cd_size, PROT_READ);
+    if (!directory_map) return false;
 
-    CHECK_EQ(directory_map->getDataLength(), cd_size);
-    central_directory.Initialize(directory_map->getDataPtr(), 0 /*offset*/, cd_size);
+    CHECK_EQ(directory_map->size(), cd_size);
+    central_directory.Initialize(directory_map->data(), 0 /*offset*/, cd_size);
   } else {
     if (mapped_zip.GetBasePtr() == nullptr) {
       ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer\n");
diff --git a/libziparchive/zip_archive_benchmark.cpp b/libziparchive/zip_archive_benchmark.cpp
index 46aa5a6..09d3b8a 100644
--- a/libziparchive/zip_archive_benchmark.cpp
+++ b/libziparchive/zip_archive_benchmark.cpp
@@ -55,10 +55,10 @@
 
   // In order to walk through all file names in the archive, look for a name
   // that does not exist in the archive.
-  ZipString name("thisFileNameDoesNotExist");
+  std::string_view name("thisFileNameDoesNotExist");
 
   // Start the benchmark.
-  while (state.KeepRunning()) {
+  for (auto _ : state) {
     OpenArchive(temp_file->path, &handle);
     FindEntry(handle, name, &data);
     CloseArchive(handle);
@@ -71,11 +71,11 @@
   ZipArchiveHandle handle;
   void* iteration_cookie;
   ZipEntry data;
-  ZipString name;
+  std::string name;
 
-  while (state.KeepRunning()) {
+  for (auto _ : state) {
     OpenArchive(temp_file->path, &handle);
-    StartIteration(handle, &iteration_cookie, nullptr, nullptr);
+    StartIteration(handle, &iteration_cookie);
     while (Next(iteration_cookie, &data, &name) == 0) {
     }
     EndIteration(iteration_cookie);
@@ -84,4 +84,27 @@
 }
 BENCHMARK(Iterate_all_files);
 
+static void StartAlignedEntry(benchmark::State& state) {
+  TemporaryFile file;
+  FILE* fp = fdopen(file.fd, "w");
+
+  ZipWriter writer(fp);
+
+  auto alignment = uint32_t(state.range(0));
+  std::string name = "name";
+  int counter = 0;
+  for (auto _ : state) {
+    writer.StartAlignedEntry(name + std::to_string(counter++), 0, alignment);
+    state.PauseTiming();
+    writer.WriteBytes("hola", 4);
+    writer.FinishEntry();
+    state.ResumeTiming();
+  }
+
+  writer.Finish();
+  fclose(fp);
+}
+BENCHMARK(StartAlignedEntry)->Arg(2)->Arg(16)->Arg(1024)->Arg(4096);
+
+
 BENCHMARK_MAIN();
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 18e0229..30a1d72 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-#ifndef LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
-#define LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
+#pragma once
+
+#include <ziparchive/zip_archive.h>
 
 #include <stdint.h>
 #include <stdlib.h>
@@ -24,9 +25,8 @@
 #include <memory>
 #include <vector>
 
-#include <utils/FileMap.h>
-#include <ziparchive/zip_archive.h>
 #include "android-base/macros.h"
+#include "android-base/mapped_file.h"
 
 static const char* kErrorMessages[] = {
     "Success",
@@ -136,6 +136,26 @@
   size_t length_;
 };
 
+/**
+ * More space efficient string representation of strings in an mmaped zipped
+ * file than std::string_view. Using std::string_view as an entry in the
+ * ZipArchive hash table wastes space. std::string_view stores a pointer to a
+ * string (on 64 bit, 8 bytes) and the length to read from that pointer,
+ * 2 bytes. Because of alignment, the structure consumes 16 bytes, wasting
+ * 6 bytes.
+ *
+ * ZipStringOffset stores a 4 byte offset from a fixed location in the memory
+ * mapped file instead of the entire address, consuming 8 bytes with alignment.
+ */
+struct ZipStringOffset {
+  uint32_t name_offset;
+  uint16_t name_length;
+
+  const std::string_view ToStringView(const uint8_t* start) const {
+    return std::string_view{reinterpret_cast<const char*>(start + name_offset), name_length};
+  }
+};
+
 struct ZipArchive {
   // open Zip archive
   mutable MappedZipFile mapped_zip;
@@ -144,7 +164,7 @@
   // mapped central directory area
   off64_t directory_offset;
   CentralDirectory central_directory;
-  std::unique_ptr<android::FileMap> directory_map;
+  std::unique_ptr<android::base::MappedFile> directory_map;
 
   // number of entries in the Zip archive
   uint16_t num_entries;
@@ -154,38 +174,11 @@
   // allocate so the maximum number entries can never be higher than
   // ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
   uint32_t hash_table_size;
-  ZipString* hash_table;
+  ZipStringOffset* 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(const int fd, bool assume_ownership);
+  ZipArchive(void* address, size_t length);
+  ~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() {
-    if (close_file && mapped_zip.GetFileDescriptor() >= 0) {
-      close(mapped_zip.GetFileDescriptor());
-    }
-
-    free(hash_table);
-  }
-
-  bool InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
-                                  size_t cd_size);
+  bool InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size);
 };
-
-#endif  // LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
diff --git a/libziparchive/zip_archive_stream_entry.cc b/libziparchive/zip_archive_stream_entry.cc
index 9ec89b1..1ec95b6 100644
--- a/libziparchive/zip_archive_stream_entry.cc
+++ b/libziparchive/zip_archive_stream_entry.cc
@@ -27,6 +27,7 @@
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <log/log.h>
 
 #include <ziparchive/zip_archive.h>
@@ -77,6 +78,12 @@
 }
 
 const std::vector<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() {
+  // Simple sanity check. The vector should *only* be handled by this code. A caller
+  // should not const-cast and modify the capacity. This may invalidate next_out.
+  //
+  // Note: it would be better to store the results of data() across Read calls.
+  CHECK_EQ(data_.capacity(), kBufSize);
+
   if (length_ == 0) {
     return nullptr;
   }
@@ -97,7 +104,8 @@
   if (bytes < data_.size()) {
     data_.resize(bytes);
   }
-  computed_crc32_ = crc32(computed_crc32_, data_.data(), data_.size());
+  computed_crc32_ = static_cast<uint32_t>(
+      crc32(computed_crc32_, data_.data(), static_cast<uint32_t>(data_.size())));
   length_ -= bytes;
   offset_ += bytes;
   return &data_;
@@ -192,9 +200,15 @@
 }
 
 const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
+  // Simple sanity check. The vector should *only* be handled by this code. A caller
+  // should not const-cast and modify the capacity. This may invalidate next_out.
+  //
+  // Note: it would be better to store the results of data() across Read calls.
+  CHECK_EQ(out_.capacity(), kBufSize);
+
   if (z_stream_.avail_out == 0) {
     z_stream_.next_out = out_.data();
-    z_stream_.avail_out = out_.size();
+    z_stream_.avail_out = static_cast<uint32_t>(out_.size());
     ;
   }
 
@@ -203,7 +217,9 @@
       if (compressed_length_ == 0) {
         return nullptr;
       }
-      size_t bytes = (compressed_length_ > in_.size()) ? in_.size() : compressed_length_;
+      DCHECK_LE(in_.size(), std::numeric_limits<uint32_t>::max());  // Should be buf size = 64k.
+      uint32_t bytes = (compressed_length_ > in_.size()) ? static_cast<uint32_t>(in_.size())
+                                                         : compressed_length_;
       ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
       errno = 0;
       if (!archive->mapped_zip.ReadAtOffset(in_.data(), bytes, offset_)) {
@@ -230,14 +246,16 @@
 
     if (z_stream_.avail_out == 0) {
       uncompressed_length_ -= out_.size();
-      computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size());
+      computed_crc32_ = static_cast<uint32_t>(
+          crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
       return &out_;
     }
     if (zerr == Z_STREAM_END) {
       if (z_stream_.avail_out != 0) {
         // Resize the vector down to the actual size of the data.
         out_.resize(out_.size() - z_stream_.avail_out);
-        computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size());
+        computed_crc32_ = static_cast<uint32_t>(
+            crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
         uncompressed_length_ -= out_.size();
         return &out_;
       }
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index ff73eb2..8781ab7 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -27,14 +27,14 @@
 #include <vector>
 
 #include <android-base/file.h>
-#include <android-base/test_utils.h>
+#include <android-base/logging.h>
+#include <android-base/mapped_file.h>
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
-#include <utils/FileMap.h>
 #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 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();
-}
-
 TEST(ziparchive, Open) {
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
@@ -112,7 +107,27 @@
   close(fd);
 }
 
-static void AssertIterationOrder(const ZipString* prefix, const ZipString* suffix,
+TEST(ziparchive, Iteration_std_string_view) {
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+  void* iteration_cookie;
+  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
+
+  ZipEntry data;
+  std::vector<std::string_view> names;
+  std::string_view name;
+  while (Next(iteration_cookie, &data, &name) == 0) names.push_back(name);
+
+  // Assert that the names are as expected.
+  std::vector<std::string_view> expected_names{"a.txt", "b.txt", "b/", "b/c.txt", "b/d.txt"};
+  std::sort(names.begin(), names.end());
+  ASSERT_EQ(expected_names, names);
+
+  CloseArchive(handle);
+}
+
+static void AssertIterationOrder(const std::string_view prefix, const std::string_view suffix,
                                  const std::vector<std::string>& expected_names_sorted) {
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
@@ -123,10 +138,10 @@
   ZipEntry data;
   std::vector<std::string> names;
 
-  ZipString name;
+  std::string 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));
+    names.push_back(name);
   }
 
   // End of iteration.
@@ -142,30 +157,26 @@
   static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/", "b/c.txt",
                                                                   "b/d.txt"};
 
-  AssertIterationOrder(nullptr, nullptr, kExpectedMatchesSorted);
+  AssertIterationOrder("", "", kExpectedMatchesSorted);
 }
 
 TEST(ziparchive, IterationWithPrefix) {
-  ZipString prefix("b/");
   static const std::vector<std::string> kExpectedMatchesSorted = {"b/", "b/c.txt", "b/d.txt"};
 
-  AssertIterationOrder(&prefix, nullptr, kExpectedMatchesSorted);
+  AssertIterationOrder("b/", "", kExpectedMatchesSorted);
 }
 
 TEST(ziparchive, IterationWithSuffix) {
-  ZipString suffix(".txt");
   static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/c.txt",
                                                                   "b/d.txt"};
 
-  AssertIterationOrder(nullptr, &suffix, kExpectedMatchesSorted);
+  AssertIterationOrder("", ".txt", kExpectedMatchesSorted);
 }
 
 TEST(ziparchive, IterationWithPrefixAndSuffix) {
-  ZipString prefix("b");
-  ZipString suffix(".txt");
   static const std::vector<std::string> kExpectedMatchesSorted = {"b.txt", "b/c.txt", "b/d.txt"};
 
-  AssertIterationOrder(&prefix, &suffix, kExpectedMatchesSorted);
+  AssertIterationOrder("b", ".txt", kExpectedMatchesSorted);
 }
 
 TEST(ziparchive, IterationWithBadPrefixAndSuffix) {
@@ -173,12 +184,10 @@
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   void* iteration_cookie;
-  ZipString prefix("x");
-  ZipString suffix("y");
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix));
+  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, "x", "y"));
 
   ZipEntry data;
-  ZipString name;
+  std::string name;
 
   // End of iteration.
   ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
@@ -191,9 +200,7 @@
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   ZipEntry data;
-  ZipString name;
-  SetZipString(&name, kATxtName);
-  ASSERT_EQ(0, FindEntry(handle, name, &data));
+  ASSERT_EQ(0, FindEntry(handle, kATxtName, &data));
 
   // Known facts about a.txt, from zipinfo -v.
   ASSERT_EQ(63, data.offset);
@@ -204,9 +211,28 @@
   ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
 
   // An entry that doesn't exist. Should be a negative return code.
-  ZipString absent_name;
-  SetZipString(&absent_name, kNonexistentTxtName);
-  ASSERT_LT(FindEntry(handle, absent_name, &data), 0);
+  ASSERT_LT(FindEntry(handle, kNonexistentTxtName, &data), 0);
+
+  CloseArchive(handle);
+}
+
+TEST(ziparchive, FindEntry_empty) {
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+  ZipEntry data;
+  ASSERT_EQ(kInvalidEntryName, FindEntry(handle, "", &data));
+
+  CloseArchive(handle);
+}
+
+TEST(ziparchive, FindEntry_too_long) {
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+  std::string very_long_name(65536, 'x');
+  ZipEntry data;
+  ASSERT_EQ(kInvalidEntryName, FindEntry(handle, very_long_name, &data));
 
   CloseArchive(handle);
 }
@@ -216,9 +242,9 @@
   ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
 
   void* iteration_cookie;
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, nullptr, nullptr));
+  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
 
-  ZipString name;
+  std::string name;
   ZipEntry data;
 
   ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
@@ -233,9 +259,7 @@
 
   // An entry that's deflated.
   ZipEntry data;
-  ZipString a_name;
-  SetZipString(&a_name, kATxtName);
-  ASSERT_EQ(0, FindEntry(handle, a_name, &data));
+  ASSERT_EQ(0, FindEntry(handle, kATxtName, &data));
   const uint32_t a_size = data.uncompressed_length;
   ASSERT_EQ(a_size, kATxtContents.size());
   uint8_t* buffer = new uint8_t[a_size];
@@ -244,9 +268,7 @@
   delete[] buffer;
 
   // An entry that's stored.
-  ZipString b_name;
-  SetZipString(&b_name, kBTxtName);
-  ASSERT_EQ(0, FindEntry(handle, b_name, &data));
+  ASSERT_EQ(0, FindEntry(handle, kBTxtName, &data));
   const uint32_t b_size = data.uncompressed_length;
   ASSERT_EQ(b_size, kBTxtContents.size());
   buffer = new uint8_t[b_size];
@@ -298,12 +320,10 @@
   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
 
   ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle));
+  ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
 
   ZipEntry entry;
-  ZipString empty_name;
-  SetZipString(&empty_name, kEmptyTxtName);
-  ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
+  ASSERT_EQ(0, FindEntry(handle, kEmptyTxtName, &entry));
   ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
   uint8_t buffer[1];
   ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
@@ -323,17 +343,15 @@
   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
                                         sizeof(kAbZip) - 1));
   ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle));
+  ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle, false));
 
   ZipEntry entry;
-  ZipString ab_name;
-  SetZipString(&ab_name, kAbTxtName);
-  ASSERT_EQ(0, FindEntry(handle, ab_name, &entry));
+  ASSERT_EQ(0, FindEntry(handle, kAbTxtName, &entry));
   ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
 
   // Extract the entry to memory.
   std::vector<uint8_t> buffer(kAbUncompressedSize);
-  ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], buffer.size()));
+  ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], static_cast<uint32_t>(buffer.size())));
 
   // Extract the entry to a file.
   TemporaryFile tmp_output_file;
@@ -348,7 +366,7 @@
   // Read the file back to a buffer and make sure the contents are
   // the same as the memory buffer we extracted directly to.
   std::vector<uint8_t> file_contents(kAbUncompressedSize);
-  ASSERT_EQ(0, lseek64(tmp_output_file.fd, 0, SEEK_SET));
+  ASSERT_EQ(0, lseek(tmp_output_file.fd, 0, SEEK_SET));
   ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0], file_contents.size()));
   ASSERT_EQ(file_contents, buffer);
 
@@ -370,7 +388,7 @@
   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
 
   ZipArchiveHandle handle;
-  ASSERT_GT(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle));
+  ASSERT_GT(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
 }
 
 TEST(ziparchive, ExtractToFile) {
@@ -385,14 +403,12 @@
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   ZipEntry entry;
-  ZipString name;
-  SetZipString(&name, kATxtName);
-  ASSERT_EQ(0, FindEntry(handle, name, &entry));
+  ASSERT_EQ(0, FindEntry(handle, kATxtName, &entry));
   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
 
   // Assert that the first 8 bytes of the file haven't been clobbered.
   uint8_t read_buffer[data_size];
-  ASSERT_EQ(0, lseek64(tmp_file.fd, 0, SEEK_SET));
+  ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
   ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, read_buffer, data_size));
   ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
 
@@ -404,7 +420,7 @@
 
   // Assert that the total length of the file is sane
   ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
-            lseek64(tmp_file.fd, 0, SEEK_END));
+            lseek(tmp_file.fd, 0, SEEK_END));
 }
 
 #if !defined(_WIN32)
@@ -416,17 +432,15 @@
   ASSERT_EQ(0, fstat(fd, &sb));
 
   // Memory map the file first and open the archive from the memory region.
-  android::FileMap file_map;
-  file_map.create(zip_path.c_str(), fd, 0 /*offset*/, sb.st_size, true);
+  auto file_map{
+      android::base::MappedFile::FromFd(fd, 0, static_cast<size_t>(sb.st_size), PROT_READ)};
   ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFromMemory(file_map.getDataPtr(), file_map.getDataLength(),
-                                     zip_path.c_str(), &handle));
+  ASSERT_EQ(0,
+            OpenArchiveFromMemory(file_map->data(), file_map->size(), zip_path.c_str(), &handle));
 
   // Assert one entry can be found and extracted correctly.
-  std::string BINARY_PATH("META-INF/com/google/android/update-binary");
-  ZipString binary_path(BINARY_PATH.c_str());
   ZipEntry binary_entry;
-  ASSERT_EQ(0, FindEntry(handle, binary_path, &binary_entry));
+  ASSERT_EQ(0, FindEntry(handle, "META-INF/com/google/android/update-binary", &binary_entry));
   TemporaryFile tmp_binary;
   ASSERT_NE(-1, tmp_binary.fd);
   ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
@@ -435,9 +449,7 @@
 
 static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
                                  bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
-  ZipString name;
-  SetZipString(&name, entry_name);
-  ASSERT_EQ(0, FindEntry(handle, name, entry));
+  ASSERT_EQ(0, FindEntry(handle, entry_name, entry));
   std::unique_ptr<ZipArchiveStreamEntry> stream;
   if (raw) {
     stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry));
@@ -490,7 +502,8 @@
 
   std::vector<uint8_t> cmp_data(entry.uncompressed_length);
   ASSERT_EQ(entry.uncompressed_length, read_data.size());
-  ASSERT_EQ(0, ExtractToMemory(handle, &entry, cmp_data.data(), cmp_data.size()));
+  ASSERT_EQ(
+      0, ExtractToMemory(handle, &entry, cmp_data.data(), static_cast<uint32_t>(cmp_data.size())));
   ASSERT_TRUE(memcmp(read_data.data(), cmp_data.data(), read_data.size()) == 0);
 
   CloseArchive(handle);
@@ -581,17 +594,13 @@
   ASSERT_NE(-1, tmp_file.fd);
   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &zip_data[0], zip_data.size()));
   ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle));
+  ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle, false));
 
   // This function expects a variant of kDataDescriptorZipFile, for look for
   // an entry whose name is "name" and whose size is 12 (contents =
   // "abdcdefghijk").
   ZipEntry entry;
-  ZipString name;
-  std::string name_str = "name";
-  SetZipString(&name, name_str);
-
-  ASSERT_EQ(0, FindEntry(handle, name, &entry));
+  ASSERT_EQ(0, FindEntry(handle, "name", &entry));
   ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length);
 
   entry_out->resize(12);
@@ -689,7 +698,7 @@
   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &kZipFileWithBrokenLfhSignature[0],
                                         kZipFileWithBrokenLfhSignature.size()));
   ZipArchiveHandle handle;
-  ASSERT_EQ(-1, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle));
+  ASSERT_EQ(-1, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle, false));
 }
 
 class VectorReader : public zip_archive::Reader {
@@ -739,8 +748,8 @@
 };
 
 TEST(ziparchive, Inflate) {
-  const uint32_t compressed_length = kATxtContentsCompressed.size();
-  const uint32_t uncompressed_length = kATxtContents.size();
+  const uint32_t compressed_length = static_cast<uint32_t>(kATxtContentsCompressed.size());
+  const uint32_t uncompressed_length = static_cast<uint32_t>(kATxtContents.size());
 
   const VectorReader reader(kATxtContentsCompressed);
   {
@@ -778,41 +787,3 @@
     ASSERT_EQ(0u, writer.GetOutput().size());
   }
 }
-
-int main(int argc, char** argv) {
-  ::testing::InitGoogleTest(&argc, argv);
-
-  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;
-    }
-
-    if (c == 't') {
-      test_data_dir = optarg;
-    }
-  }
-
-  if (test_data_dir.size() == 0) {
-    printf("Test data flag (--test_data_dir) required\n\n");
-    return -1;
-  }
-
-  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;
-  }
-
-  return RUN_ALL_TESTS();
-}
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 6ad3366..198154b 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -26,15 +26,17 @@
 #include <vector>
 
 #include "android-base/logging.h"
-#include "utils/Compat.h"
-#include "utils/Log.h"
 
 #include "entry_name_utils-inl.h"
 #include "zip_archive_common.h"
 
-#if !defined(powerof2)
-#define powerof2(x) ((((x)-1) & (x)) == 0)
-#endif
+#undef powerof2
+#define powerof2(x)                                               \
+  ({                                                              \
+    __typeof__(x) _x = (x);                                       \
+    __typeof__(x) _x2;                                            \
+    __builtin_add_overflow(_x, -1, &_x2) ? 1 : ((_x2 & _x) == 0); \
+  })
 
 /* Zip compression methods we support */
 enum {
@@ -97,7 +99,7 @@
   }
 }
 
-ZipWriter::ZipWriter(ZipWriter&& writer)
+ZipWriter::ZipWriter(ZipWriter&& writer) noexcept
     : file_(writer.file_),
       seekable_(writer.seekable_),
       current_offset_(writer.current_offset_),
@@ -109,7 +111,7 @@
   writer.state_ = State::kError;
 }
 
-ZipWriter& ZipWriter::operator=(ZipWriter&& writer) {
+ZipWriter& ZipWriter::operator=(ZipWriter&& writer) noexcept {
   file_ = writer.file_;
   seekable_ = writer.seekable_;
   current_offset_ = writer.current_offset_;
@@ -128,7 +130,7 @@
   return error_code;
 }
 
-int32_t ZipWriter::StartEntry(const char* path, size_t flags) {
+int32_t ZipWriter::StartEntry(std::string_view path, size_t flags) {
   uint32_t alignment = 0;
   if (flags & kAlign32) {
     flags &= ~kAlign32;
@@ -137,11 +139,11 @@
   return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
 }
 
-int32_t ZipWriter::StartAlignedEntry(const char* path, size_t flags, uint32_t alignment) {
+int32_t ZipWriter::StartAlignedEntry(std::string_view path, size_t flags, uint32_t alignment) {
   return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
 }
 
-int32_t ZipWriter::StartEntryWithTime(const char* path, size_t flags, time_t time) {
+int32_t ZipWriter::StartEntryWithTime(std::string_view path, size_t flags, time_t time) {
   uint32_t alignment = 0;
   if (flags & kAlign32) {
     flags &= ~kAlign32;
@@ -167,8 +169,8 @@
     year = 80;
   }
 
-  *out_date = (year - 80) << 9 | (ptm->tm_mon + 1) << 5 | ptm->tm_mday;
-  *out_time = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
+  *out_date = static_cast<uint16_t>((year - 80) << 9 | (ptm->tm_mon + 1) << 5 | ptm->tm_mday);
+  *out_time = static_cast<uint16_t>(ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1);
 }
 
 static void CopyFromFileEntry(const ZipWriter::FileEntry& src, bool use_data_descriptor,
@@ -191,16 +193,22 @@
   dst->compression_method = src.compression_method;
   dst->last_mod_time = src.last_mod_time;
   dst->last_mod_date = src.last_mod_date;
-  dst->file_name_length = src.path.size();
+  DCHECK_LE(src.path.size(), std::numeric_limits<uint16_t>::max());
+  dst->file_name_length = static_cast<uint16_t>(src.path.size());
   dst->extra_field_length = src.padding_length;
 }
 
-int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags, time_t time,
+int32_t ZipWriter::StartAlignedEntryWithTime(std::string_view path, size_t flags, time_t time,
                                              uint32_t alignment) {
   if (state_ != State::kWritingZip) {
     return kInvalidState;
   }
 
+  // Can only have 16535 entries because of zip records.
+  if (files_.size() == std::numeric_limits<uint16_t>::max()) {
+    return HandleError(kIoError);
+  }
+
   if (flags & kAlign32) {
     return kInvalidAlign32Flag;
   }
@@ -208,10 +216,17 @@
   if (powerof2(alignment) == 0) {
     return kInvalidAlignment;
   }
+  if (alignment > std::numeric_limits<uint16_t>::max()) {
+    return kInvalidAlignment;
+  }
 
   FileEntry file_entry = {};
   file_entry.local_file_header_offset = current_offset_;
   file_entry.path = path;
+  // No support for larger than 4GB files.
+  if (file_entry.local_file_header_offset > std::numeric_limits<uint32_t>::max()) {
+    return HandleError(kIoError);
+  }
 
   if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(file_entry.path.data()),
                         file_entry.path.size())) {
@@ -232,13 +247,24 @@
   ExtractTimeAndDate(time, &file_entry.last_mod_time, &file_entry.last_mod_date);
 
   off_t offset = current_offset_ + sizeof(LocalFileHeader) + file_entry.path.size();
-  std::vector<char> zero_padding;
+  // prepare a pre-zeroed memory page in case when we need to pad some aligned data.
+  static constexpr auto kPageSize = 4096;
+  static constexpr char kSmallZeroPadding[kPageSize] = {};
+  // use this buffer if our preallocated one is too small
+  std::vector<char> zero_padding_big;
+  const char* zero_padding = nullptr;
+
   if (alignment != 0 && (offset & (alignment - 1))) {
     // Pad the extra field so the data will be aligned.
-    uint16_t padding = alignment - (offset % alignment);
+    uint16_t padding = static_cast<uint16_t>(alignment - (offset % alignment));
     file_entry.padding_length = padding;
     offset += padding;
-    zero_padding.resize(padding, 0);
+    if (padding <= std::size(kSmallZeroPadding)) {
+        zero_padding = kSmallZeroPadding;
+    } else {
+        zero_padding_big.resize(padding, 0);
+        zero_padding = zero_padding_big.data();
+    }
   }
 
   LocalFileHeader header = {};
@@ -250,11 +276,11 @@
     return HandleError(kIoError);
   }
 
-  if (fwrite(path, sizeof(*path), file_entry.path.size(), file_) != file_entry.path.size()) {
+  if (fwrite(path.data(), 1, path.size(), file_) != path.size()) {
     return HandleError(kIoError);
   }
 
-  if (file_entry.padding_length != 0 && fwrite(zero_padding.data(), 1, file_entry.padding_length,
+  if (file_entry.padding_length != 0 && fwrite(zero_padding, 1, file_entry.padding_length,
                                                file_) != file_entry.padding_length) {
     return HandleError(kIoError);
   }
@@ -303,16 +329,17 @@
 
   if (zerr != Z_OK) {
     if (zerr == Z_VERSION_ERROR) {
-      ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
+      LOG(ERROR) << "Installed zlib is not compatible with linked version (" << ZLIB_VERSION << ")";
       return HandleError(kZlibError);
     } else {
-      ALOGE("deflateInit2 failed (zerr=%d)", zerr);
+      LOG(ERROR) << "deflateInit2 failed (zerr=" << zerr << ")";
       return HandleError(kZlibError);
     }
   }
 
   z_stream_->next_out = buffer_.data();
-  z_stream_->avail_out = buffer_.size();
+  DCHECK_EQ(buffer_.size(), kBufSize);
+  z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
   return kNoError;
 }
 
@@ -320,25 +347,31 @@
   if (state_ != State::kWritingEntry) {
     return HandleError(kInvalidState);
   }
+  // Need to be able to mark down data correctly.
+  if (len + static_cast<uint64_t>(current_file_entry_.uncompressed_size) >
+      std::numeric_limits<uint32_t>::max()) {
+    return HandleError(kIoError);
+  }
+  uint32_t len32 = static_cast<uint32_t>(len);
 
   int32_t result = kNoError;
   if (current_file_entry_.compression_method & kCompressDeflated) {
-    result = CompressBytes(&current_file_entry_, data, len);
+    result = CompressBytes(&current_file_entry_, data, len32);
   } else {
-    result = StoreBytes(&current_file_entry_, data, len);
+    result = StoreBytes(&current_file_entry_, data, len32);
   }
 
   if (result != kNoError) {
     return result;
   }
 
-  current_file_entry_.crc32 =
-      crc32(current_file_entry_.crc32, reinterpret_cast<const Bytef*>(data), len);
-  current_file_entry_.uncompressed_size += len;
+  current_file_entry_.crc32 = static_cast<uint32_t>(
+      crc32(current_file_entry_.crc32, reinterpret_cast<const Bytef*>(data), len32));
+  current_file_entry_.uncompressed_size += len32;
   return kNoError;
 }
 
-int32_t ZipWriter::StoreBytes(FileEntry* file, const void* data, size_t len) {
+int32_t ZipWriter::StoreBytes(FileEntry* file, const void* data, uint32_t len) {
   CHECK(state_ == State::kWritingEntry);
 
   if (fwrite(data, 1, len, file_) != len) {
@@ -349,7 +382,7 @@
   return kNoError;
 }
 
-int32_t ZipWriter::CompressBytes(FileEntry* file, const void* data, size_t len) {
+int32_t ZipWriter::CompressBytes(FileEntry* file, const void* data, uint32_t len) {
   CHECK(state_ == State::kWritingEntry);
   CHECK(z_stream_);
   CHECK(z_stream_->next_out != nullptr);
@@ -377,7 +410,8 @@
 
       // Reset the output buffer for the next input.
       z_stream_->next_out = buffer_.data();
-      z_stream_->avail_out = buffer_.size();
+      DCHECK_EQ(buffer_.size(), kBufSize);
+      z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
     }
   }
   return kNoError;
@@ -402,7 +436,8 @@
     current_offset_ += write_bytes;
 
     z_stream_->next_out = buffer_.data();
-    z_stream_->avail_out = buffer_.size();
+    DCHECK_EQ(buffer_.size(), kBufSize);
+    z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
   }
   if (zerr != Z_STREAM_END) {
     return HandleError(kZlibError);
@@ -489,7 +524,11 @@
     cdr.crc32 = file.crc32;
     cdr.compressed_size = file.compressed_size;
     cdr.uncompressed_size = file.uncompressed_size;
-    cdr.file_name_length = file.path.size();
+    // Checked in IsValidEntryName.
+    DCHECK_LE(file.path.size(), std::numeric_limits<uint16_t>::max());
+    cdr.file_name_length = static_cast<uint16_t>(file.path.size());
+    // Checked in StartAlignedEntryWithTime.
+    DCHECK_LE(file.local_file_header_offset, std::numeric_limits<uint32_t>::max());
     cdr.local_file_header_offset = static_cast<uint32_t>(file.local_file_header_offset);
     if (fwrite(&cdr, sizeof(cdr), 1, file_) != 1) {
       return HandleError(kIoError);
@@ -506,10 +545,15 @@
   er.eocd_signature = EocdRecord::kSignature;
   er.disk_num = 0;
   er.cd_start_disk = 0;
-  er.num_records_on_disk = files_.size();
-  er.num_records = files_.size();
-  er.cd_size = current_offset_ - startOfCdr;
-  er.cd_start_offset = startOfCdr;
+  // Checked when adding entries.
+  DCHECK_LE(files_.size(), std::numeric_limits<uint16_t>::max());
+  er.num_records_on_disk = static_cast<uint16_t>(files_.size());
+  er.num_records = static_cast<uint16_t>(files_.size());
+  if (current_offset_ > std::numeric_limits<uint32_t>::max()) {
+    return HandleError(kIoError);
+  }
+  er.cd_size = static_cast<uint32_t>(current_offset_ - startOfCdr);
+  er.cd_start_offset = static_cast<uint32_t>(startOfCdr);
 
   if (fwrite(&er, sizeof(er), 1, file_) != 1) {
     return HandleError(kIoError);
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index c284273..c3da23c 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -62,7 +62,7 @@
   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
   EXPECT_EQ(kCompressStored, data.method);
   EXPECT_EQ(0u, data.has_data_descriptor);
   EXPECT_EQ(strlen(expected), data.compressed_length);
@@ -95,19 +95,19 @@
 
   ZipEntry data;
 
-  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
   EXPECT_EQ(kCompressStored, data.method);
   EXPECT_EQ(2u, data.compressed_length);
   ASSERT_EQ(2u, data.uncompressed_length);
   ASSERT_TRUE(AssertFileEntryContentsEq("he", handle, &data));
 
-  ASSERT_EQ(0, FindEntry(handle, ZipString("file/file.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "file/file.txt", &data));
   EXPECT_EQ(kCompressStored, data.method);
   EXPECT_EQ(3u, data.compressed_length);
   ASSERT_EQ(3u, data.uncompressed_length);
   ASSERT_TRUE(AssertFileEntryContentsEq("llo", handle, &data));
 
-  ASSERT_EQ(0, FindEntry(handle, ZipString("file/file2.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "file/file2.txt", &data));
   EXPECT_EQ(kCompressStored, data.method);
   EXPECT_EQ(0u, data.compressed_length);
   EXPECT_EQ(0u, data.uncompressed_length);
@@ -129,7 +129,7 @@
   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
   EXPECT_EQ(0, data.offset & 0x03);
 
   CloseArchive(handle);
@@ -163,7 +163,7 @@
   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
   EXPECT_EQ(0, data.offset & 0x03);
 
   struct tm mod = data.GetModificationTime();
@@ -191,7 +191,7 @@
   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
   EXPECT_EQ(0, data.offset & 0xfff);
 
   CloseArchive(handle);
@@ -213,7 +213,7 @@
   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
   EXPECT_EQ(0, data.offset & 0xfff);
 
   struct tm mod = data.GetModificationTime();
@@ -241,7 +241,7 @@
   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
   EXPECT_EQ(kCompressDeflated, data.method);
   ASSERT_EQ(4u, data.uncompressed_length);
   ASSERT_TRUE(AssertFileEntryContentsEq("helo", handle, &data));
@@ -257,7 +257,7 @@
   std::vector<uint8_t> buffer(kBufSize);
   size_t prev = 1;
   for (size_t i = 0; i < kBufSize; i++) {
-    buffer[i] = i + prev;
+    buffer[i] = static_cast<uint8_t>(i + prev);
     prev = i;
   }
 
@@ -273,13 +273,14 @@
   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
   EXPECT_EQ(kCompressDeflated, data.method);
   EXPECT_EQ(kBufSize, data.uncompressed_length);
 
   std::vector<uint8_t> decompress(kBufSize);
   memset(decompress.data(), 0, kBufSize);
-  ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(), decompress.size()));
+  ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(),
+                               static_cast<uint32_t>(decompress.size())));
   EXPECT_EQ(0, memcmp(decompress.data(), buffer.data(), kBufSize))
       << "Input buffer and output buffer are different.";
 
@@ -339,12 +340,12 @@
   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, ZipString("keep.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "keep.txt", &data));
   ASSERT_TRUE(AssertFileEntryContentsEq(kKeepThis, handle, &data));
 
-  ASSERT_NE(0, FindEntry(handle, ZipString("drop.txt"), &data));
+  ASSERT_NE(0, FindEntry(handle, "drop.txt", &data));
 
-  ASSERT_EQ(0, FindEntry(handle, ZipString("replace.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "replace.txt", &data));
   ASSERT_TRUE(AssertFileEntryContentsEq(kReplaceWithThis, handle, &data));
 
   CloseArchive(handle);
@@ -391,7 +392,7 @@
   actual.resize(expected.size());
 
   uint8_t* buffer = reinterpret_cast<uint8_t*>(&*actual.begin());
-  if (ExtractToMemory(handle, zip_entry, buffer, actual.size()) != 0) {
+  if (ExtractToMemory(handle, zip_entry, buffer, static_cast<uint32_t>(actual.size())) != 0) {
     return ::testing::AssertionFailure() << "failed to extract entry";
   }
 
diff --git a/llkd/Android.bp b/llkd/Android.bp
new file mode 100644
index 0000000..62a637d
--- /dev/null
+++ b/llkd/Android.bp
@@ -0,0 +1,53 @@
+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"],
+
+    product_variables: {
+        debuggable: {
+            cppflags: ["-D__PTRACE_ENABLED__"],
+        },
+    },
+}
+
+cc_binary {
+    name: "llkd",
+
+    srcs: [
+        "llkd.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+    ],
+    static_libs: [
+        "libllkd",
+    ],
+    cflags: ["-Werror"],
+
+    init_rc: ["llkd.rc"],
+    product_variables: {
+        debuggable: {
+            init_rc: ["llkd-debuggable.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..191f988
--- /dev/null
+++ b/llkd/README.md
@@ -0,0 +1,199 @@
+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
+----------
+
+There are two detection scenarios. Persistent D or Z state, and persistent
+stack signature.
+
+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.
+
+For usedebug releases only, persistent stack signature checking is enabled.
+If a thread in any state but Z, has a persistent listed ro.llk.stack kernel
+symbol always being reported, even if there is forward scheduling progress, for
+longer than ro.llk.timeout_ms, or ro.llk.stack.timeout_ms, then issue a kill
+to the process.  If another scan shows the same process continues to exist,
+then have a confirmed live-lock condition and need to panic.  There is no
+ABA detection since forward scheduling progress is allowed, thus the condition
+for the symbols are:
+
+- Check is looking for " __symbol__+0x" or " __symbol__.cfi+0x" in
+  /proc/__pid__/stack.
+- The __symbol__ should be rare and short lived enough that on a typical
+  system the function is seen at most only once in a sample over the timeout
+  period of ro.llk.stack.timeout_ms, samples occur every ro.llk.check_ms. This
+  can be the only way to prevent a false trigger as there is no ABA protection.
+- Persistent continuously when the live lock condition exists.
+- Should be just below the function that is calling the lock that could
+  contend, because if the lock is below or in the symbol function, the
+  symbol will show in all affected processes, not just the one that
+  caused the lockup.
+
+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.  For the stack symbol checking,
+there is an additional process blacklist so that we do not incide sepolicy
+violations on services that block ptrace operations.
+
+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
+------------------
+
+The following are the Android Properties llkd respond to.
+*prop*_ms named properties are in milliseconds.
+Properties that use comma (*,*) separator for lists, use a leading separator to
+preserve default and add or subtract entries with (*optional*) plus (*+*) and
+minus (*-*) prefixes respectively.
+For these lists, the string "*false*" is synonymous with an *empty* list,
+and *blank* or *missing* resorts to the specified *default* value.
+
+#### ro.config.low_ram
+device is configured with limited memory.
+
+#### ro.debuggable
+device is configured for userdebug or eng build.
+
+#### ro.llk.sysrq_t
+default not ro.config.low_ram, or ro.debuggable if property is "eng".
+if true do 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.stack.timeout_ms
+default ro.llk.timeout_ms,
+checking for persistent stack symbols maximum timelimit.
+Only active on userdebug or eng builds.
+
+#### ro.llk.check_ms
+default 2 minutes samples of threads for D or Z.
+
+#### ro.llk.stack
+default cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable
+comma separated list of kernel symbols.
+Look for kernel stack symbols that if ever persistently present can
+indicate a subsystem is locked up.
+Beware, check does not on purpose do forward scheduling ABA except by polling
+every ro.llk_check_ms over the period ro.llk.stack.timeout_ms, so stack symbol
+should be exceptionally rare and fleeting.
+One must be convinced that it is virtually *impossible* for symbol to show up
+persistently in all samples of the stack.
+Again, looks for a match for either " **symbol**+0x" or " **symbol**.cfi+0x"
+in stack expansion.
+Only available on userdebug or eng builds, limited privileges due to security
+concerns on user builds prevents this checking.
+
+#### ro.llk.blacklist.process
+default 0,1,2 (kernel, init and [kthreadd]) plus process names
+init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,
+[watchdogd],[watchdogd/0],...,[watchdogd/***get_nprocs**-1*].
+Do not watch these processes.  A process can be comm, cmdline or pid reference.
+NB: automated default here can be larger than the current maximum property
+size of 92.
+NB: false is a very very very unlikely process to want to blacklist.
+
+#### ro.llk.blacklist.parent
+default 0,2,adbd&[setsid] (kernel, [kthreadd] and adbd *only for zombie setsid*).
+Do not watch processes that have this parent.
+An ampersand (*&*) separator is used to specify that the parent is ignored
+only in combination with the target child process.
+Ampersand was selected because it is never part of a process name,
+however a setprop in the shell requires it to be escaped or quoted;
+init rc file where this is normally specified does not have this issue.
+A parent or target processes can be specified as comm, cmdline or pid reference.
+
+#### ro.llk.blacklist.uid
+default *empty* or false, comma separated list of uid numbers or names.
+Do not watch processes that match this uid.
+
+#### ro.llk.blacklist.process.stack
+default process names init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd.
+This subset of processes are not monitored for live lock stack signatures.
+Also prevents the sepolicy violation associated with processes that block
+ptrace, as these can not be checked anyways.
+Only active on userdebug and eng builds.
+
+Architectural Concerns
+----------------------
+
+- built-in [khungtask] daemon is too generic and trips on driver code that
+  sits around in D state too much.  To switch to S instead makes the task(s)
+  killable, so the drivers should be able to resurrect them if needed.
+- Properties are limited to 92 characters.
+- 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 as they should be baked into the product.
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
new file mode 100644
index 0000000..3586ca1
--- /dev/null
+++ b/llkd/include/llkd.h
@@ -0,0 +1,88 @@
+/*
+ * 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_ENABLE_SYSRQ_T_PROPERTY    "ro.llk.sysrq_t"
+#define LLK_ENABLE_SYSRQ_T_DEFAULT     true
+#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_STACK_TIMEOUT_MS_PROPERTY  "ro.llk.stack.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_CHECK_STACK_PROPERTY       "ro.llk.stack"
+#define LLK_CHECK_STACK_DEFAULT        \
+    "cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable"
+#define LLK_BLACKLIST_PROCESS_PROPERTY "ro.llk.blacklist.process"
+#define LLK_BLACKLIST_PROCESS_DEFAULT  \
+    "0,1,2,init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
+#define LLK_BLACKLIST_PARENT_PROPERTY  "ro.llk.blacklist.parent"
+#define LLK_BLACKLIST_PARENT_DEFAULT   "0,2,[kthreadd],adbd&[setsid]"
+#define LLK_BLACKLIST_UID_PROPERTY     "ro.llk.blacklist.uid"
+#define LLK_BLACKLIST_UID_DEFAULT      ""
+#define LLK_BLACKLIST_STACK_PROPERTY   "ro.llk.blacklist.process.stack"
+#define LLK_BLACKLIST_STACK_DEFAULT    "init,lmkd.llkd,llkd,keystore,ueventd,apexd"
+/* 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..b26ad4d
--- /dev/null
+++ b/llkd/libllkd.cpp
@@ -0,0 +1,1417 @@
+/*
+ * 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 <string.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;
+using namespace std::literals;
+
+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 {                                               // enum of state indexes
+    llkStateD,                                       // Persistent 'D' state
+    llkStateZ,                                       // Persistent 'Z' state
+#ifdef __PTRACE_ENABLED__                            // Extra privileged states
+    llkStateStack,                                   // stack signature
+#endif                                               // End of extra privilege
+    llkNumStates,                                    // Maxumum number of states
+};                                                   // 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 llkEnableSysrqT = LLK_ENABLE_SYSRQ_T_DEFAULT;   // sysrq stack trace dump
+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);
+#ifdef __PTRACE_ENABLED__
+// list of stack symbols to search for persistence.
+std::unordered_set<std::string> llkCheckStackSymbols;
+#endif
+
+// 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 parent and target processes to skip. default:
+// adbd *and* [setsid]
+std::unordered_map<std::string, std::unordered_set<std::string>> llkBlacklistParentAndChild;
+// list of uids, and uid names, to skip, default nothing
+std::unordered_set<std::string> llkBlacklistUid;
+#ifdef __PTRACE_ENABLED__
+// list of names to skip stack checking. "init", "lmkd", "llkd", "keystore" or
+// "logd" (if not userdebug).
+std::unordered_set<std::string> llkBlacklistStack;
+#endif
+
+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::ParseUint(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.
+#ifdef __PTRACE_ENABLED__          // Privileged state checking
+    milliseconds count_stack;      // duration where stack is stagnant.
+#endif                             // End privilege
+    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 ?)
+#ifdef __PTRACE_ENABLED__          // Privileged state checking
+    char stack;                    // index in llkCheckStackSymbols for matches
+#endif                             // and with maximum index PROP_VALUE_MAX/2.
+    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(0ms),
+#ifdef __PTRACE_ENABLED__
+          count_stack(0ms),
+#endif
+          pid(pid),
+          ppid(ppid),
+          uid(-1),
+          time(time),
+          state(state),
+#ifdef __PTRACE_ENABLED__
+          stack(-1),
+#endif
+          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 = '?';
+#ifdef __PTRACE_ENABLED__
+        count_stack = 0ms;
+        stack = -1;
+#endif
+        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, const std::string& message = "") {
+    if (!message.empty()) LOG(ERROR) << message;
+    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
+        return;
+    }
+    // Wish could ::sync() here, if storage is locked up, we will not continue.
+    if (dump) {
+        // Show all locks that are held
+        android::base::WriteStringToFd("d", sysrqTriggerFd);
+        // Show all waiting tasks
+        android::base::WriteStringToFd("w", sysrqTriggerFd);
+        // This can trigger hardware watchdog, that is somewhat _ok_.
+        // But useless if pstore configured for <256KB, low ram devices ...
+        if (llkEnableSysrqT) {
+            android::base::WriteStringToFd("t", sysrqTriggerFd);
+            // Show all locks that are held (in case 't' overflows ramoops)
+            android::base::WriteStringToFd("d", sysrqTriggerFd);
+            // Show all waiting tasks (in case 't' overflows ramoops)
+            android::base::WriteStringToFd("w", sysrqTriggerFd);
+        }
+        ::usleep(200000);  // let everything settle
+    }
+    // SysRq message matches kernel format, and propagates through bootstat
+    // ultimately to the boot reason into panic,livelock,<state>.
+    llkWriteStringToFile(message + (message.empty() ? "" : "\n") +
+                                 "SysRq : Trigger a crash : 'livelock,"s + state + "'\n",
+                         "/dev/kmsg");
+    // Because panic is such a serious thing to do, let us
+    // make sure that the tid being inspected still exists!
+    auto piddir = procdir + std::to_string(tid) + "/stat";
+    if (access(piddir.c_str(), F_OK) != 0) {
+        PLOG(WARNING) << piddir;
+        return;
+    }
+    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) {
+    LOG(FATAL) << "alarm";
+    // NOTREACHED
+    llkPanicKernel(true, ::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 (const auto& entry : blacklist) {
+        if (!ret.empty()) ret += ",";
+        ret += entry;
+    }
+    return ret;
+}
+
+std::string llkFormat(
+        const std::unordered_map<std::string, std::unordered_set<std::string>>& blacklist,
+        bool leading_comma = false) {
+    std::string ret;
+    for (const auto& entry : blacklist) {
+        for (const auto& target : entry.second) {
+            if (leading_comma || !ret.empty()) ret += ",";
+            ret += entry.first + "&" + target;
+        }
+    }
+    return ret;
+}
+
+// This function parses the properties as a list, incorporating the supplied
+// default.  A leading comma separator means preserve the defaults and add
+// entries (with an optional leading + sign), or removes entries with a leading
+// - sign.
+//
+// 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& prop, const std::string& def) {
+    auto s = android::base::GetProperty(prop, def);
+    constexpr char separators[] = ", \t:;";
+    if (!s.empty() && (s != def) && strchr(separators, s[0])) s = def + s;
+
+    std::unordered_set<std::string> result;
+
+    // Special case, allow boolean false to empty the list, otherwise expected
+    // source of input from android::base::GetProperty will supply the default
+    // value on empty content in the property.
+    if (s == "false") return result;
+
+    size_t base = 0;
+    while (s.size() > base) {
+        auto found = s.find_first_of(separators, base);
+        // Only emplace unique content, empty entries are not an option
+        if (found != base) {
+            switch (s[base]) {
+                case '-':
+                    ++base;
+                    if (base >= s.size()) break;
+                    if (base != found) {
+                        auto have = result.find(s.substr(base, found - base));
+                        if (have != result.end()) result.erase(have);
+                    }
+                    break;
+                case '+':
+                    ++base;
+                    if (base >= s.size()) break;
+                    if (base == found) break;
+                    // FALLTHRU (for gcc, lint, pcc, etc; following for clang)
+                    FALLTHROUGH_INTENDED;
+                default:
+                    result.emplace(s.substr(base, found - base));
+                    break;
+            }
+        }
+        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.empty() || blacklist.empty()) return false;
+
+    return blacklist.find(name) != blacklist.end();
+}
+
+bool llkSkipProc(proc* procp,
+                 const std::unordered_set<std::string>& blacklist = llkBlacklistProcess) {
+    if (!procp) return false;
+    if (llkSkipName(std::to_string(procp->pid), blacklist)) return true;
+    if (llkSkipName(procp->getComm(), blacklist)) return true;
+    if (llkSkipName(procp->getCmdline(), blacklist)) return true;
+    if (llkSkipName(android::base::Basename(procp->getCmdline()), blacklist)) return true;
+    return false;
+}
+
+const std::unordered_set<std::string>& llkSkipName(
+        const std::string& name,
+        const std::unordered_map<std::string, std::unordered_set<std::string>>& blacklist) {
+    static const std::unordered_set<std::string> empty;
+    if (name.empty() || blacklist.empty()) return empty;
+    auto found = blacklist.find(name);
+    if (found == blacklist.end()) return empty;
+    return found->second;
+}
+
+bool llkSkipPproc(proc* pprocp, proc* procp,
+                  const std::unordered_map<std::string, std::unordered_set<std::string>>&
+                          blacklist = llkBlacklistParentAndChild) {
+    if (!pprocp || !procp || blacklist.empty()) return false;
+    if (llkSkipProc(procp, llkSkipName(std::to_string(pprocp->pid), blacklist))) return true;
+    if (llkSkipProc(procp, llkSkipName(pprocp->getComm(), blacklist))) return true;
+    if (llkSkipProc(procp, llkSkipName(pprocp->getCmdline(), blacklist))) return true;
+    return llkSkipProc(procp,
+                       llkSkipName(android::base::Basename(pprocp->getCmdline()), blacklist));
+}
+
+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;
+}
+
+#ifdef __PTRACE_ENABLED__
+bool llkCheckStack(proc* procp, const std::string& piddir) {
+    if (llkCheckStackSymbols.empty()) return false;
+    if (procp->state == 'Z') {  // No brains for Zombies
+        procp->stack = -1;
+        procp->count_stack = 0ms;
+        return false;
+    }
+
+    // Don't check process that are known to block ptrace, save sepolicy noise.
+    if (llkSkipProc(procp, llkBlacklistStack)) return false;
+    auto kernel_stack = ReadFile(piddir + "/stack");
+    if (kernel_stack.empty()) {
+        LOG(VERBOSE) << piddir << "/stack empty comm=" << procp->getComm()
+                     << " cmdline=" << procp->getCmdline();
+        return false;
+    }
+    // A scheduling incident that should not reset count_stack
+    if (kernel_stack.find(" cpu_worker_pools+0x") != std::string::npos) return false;
+    char idx = -1;
+    char match = -1;
+    std::string matched_stack_symbol = "<unknown>";
+    for (const auto& stack : llkCheckStackSymbols) {
+        if (++idx < 0) break;
+        if ((kernel_stack.find(" "s + stack + "+0x") != std::string::npos) ||
+            (kernel_stack.find(" "s + stack + ".cfi+0x") != std::string::npos)) {
+            match = idx;
+            matched_stack_symbol = stack;
+            break;
+        }
+    }
+    if (procp->stack != match) {
+        procp->stack = match;
+        procp->count_stack = 0ms;
+        return false;
+    }
+    if (match == char(-1)) return false;
+    procp->count_stack += llkCycle;
+    if (procp->count_stack < llkStateTimeoutMs[llkStateStack]) return false;
+    LOG(WARNING) << "Found " << matched_stack_symbol << " in stack for pid " << procp->pid;
+    return true;
+}
+#endif
+
+// 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.empty()) {
+        // /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.empty()) {
+            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_SYSRQ_T_PROPERTY "=" << llkFormat(llkEnableSysrqT) << "\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"
+#ifdef __PTRACE_ENABLED__
+              << LLK_STACK_TIMEOUT_MS_PROPERTY "=" << llkFormat(llkStateTimeoutMs[llkStateStack])
+              << "\n"
+#endif
+              << LLK_CHECK_MS_PROPERTY "=" << llkFormat(llkCheckMs) << "\n"
+#ifdef __PTRACE_ENABLED__
+              << LLK_CHECK_STACK_PROPERTY "=" << llkFormat(llkCheckStackSymbols) << "\n"
+              << LLK_BLACKLIST_STACK_PROPERTY "=" << llkFormat(llkBlacklistStack) << "\n"
+#endif
+              << LLK_BLACKLIST_PROCESS_PROPERTY "=" << llkFormat(llkBlacklistProcess) << "\n"
+              << LLK_BLACKLIST_PARENT_PROPERTY "=" << llkFormat(llkBlacklistParent)
+              << llkFormat(llkBlacklistParentAndChild, true) << "\n"
+              << LLK_BLACKLIST_UID_PROPERTY "=" << llkFormat(llkBlacklistUid);
+}
+
+void* llkThread(void* obj) {
+    prctl(PR_SET_DUMPABLE, 0);
+
+    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();
+    auto dump = true;
+    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.empty()) {
+                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;
+            }
+#ifdef __PTRACE_ENABLED__
+            // if no stack monitoring, we can quickly exit here
+            if (!llkIsMonitorState(state) && llkCheckStackSymbols.empty()) {
+                continue;
+            }
+#else
+            if (!llkIsMonitorState(state)) continue;
+#endif
+            if ((tid == myTid) || llkSkipPid(tid)) {
+                continue;
+            }
+            if (llkSkipPpid(ppid)) {
+                break;
+            }
+
+            auto process_comm = procp->getComm();
+            if (llkSkipName(process_comm)) {
+                continue;
+            }
+            if (llkSkipName(procp->getCmdline())) {
+                break;
+            }
+            if (llkSkipName(android::base::Basename(procp->getCmdline()))) {
+                break;
+            }
+
+            auto pprocp = llkTidLookup(ppid);
+            if (pprocp == nullptr) {
+                pprocp = llkTidAlloc(ppid, ppid, 0, "", 0, '?');
+            }
+            if (pprocp) {
+                if (llkSkipPproc(pprocp, procp)) break;
+                if (llkSkipProc(pprocp, llkBlacklistParent)) break;
+            } else {
+                if (llkSkipName(std::to_string(ppid), llkBlacklistParent)) break;
+            }
+
+            if ((llkBlacklistUid.size() != 0) && llkSkipUid(procp->getUid())) {
+                continue;
+            }
+
+            // ABA mitigation watching last time schedule activity happened
+            llkCheckSchedUpdate(procp, piddir);
+
+#ifdef __PTRACE_ENABLED__
+            auto stuck = llkCheckStack(procp, piddir);
+            if (llkIsMonitorState(state)) {
+                if (procp->count >= llkStateTimeoutMs[(state == 'Z') ? llkStateZ : llkStateD]) {
+                    stuck = true;
+                } else if (procp->count != 0ms) {
+                    LOG(VERBOSE) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->"
+                                 << pid << "->" << tid << ' ' << process_comm;
+                }
+            }
+            if (!stuck) continue;
+#else
+            if (procp->count >= llkStateTimeoutMs[(state == 'Z') ? llkStateZ : llkStateD]) {
+                if (procp->count != 0ms) {
+                    LOG(VERBOSE) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->"
+                                 << pid << "->" << tid << ' ' << process_comm;
+                }
+                continue;
+            }
+#endif
+
+            // 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 << ' ' << process_comm << " [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).
+                    default:
+                        LOG(WARNING) << state << ' ' << llkFormat(procp->count) << ' ' << pid
+                                     << "->" << tid << ' ' << process_comm << " [kill]";
+                        if ((llkKillOneProcess(llkTidLookup(pid), procp) >= 0) ||
+                            (llkKillOneProcess(pid, state, tid) >= 0) ||
+                            (llkKillOneProcess(procp, procp) >= 0) ||
+                            (llkKillOneProcess(tid, state, tid) >= 0)) {
+                            continue;
+                        }
+                        break;
+                }
+            }
+            // We are here because we have confirmed kernel live-lock
+            const auto message = state + " "s + llkFormat(procp->count) + " " +
+                                 std::to_string(ppid) + "->" + std::to_string(pid) + "->" +
+                                 std::to_string(tid) + " " + process_comm + " [panic]";
+            llkPanicKernel(dump, tid,
+                           (state == 'Z') ? "zombie" : (state == 'D') ? "driver" : "sleeping",
+                           message);
+            dump = false;
+        }
+        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.empty()) ppidCmdline = "(" + ppidCmdline + ")";
+                std::string pidCmdline;
+                if (p->second.pid != p->second.tid) {
+                    pidCmdline = llkProcGetName(p->second.pid, nullptr, p->second.getCmdline());
+                    if (!pidCmdline.empty()) pidCmdline = "(" + pidCmdline + ")";
+                }
+                std::string tidCmdline =
+                    llkProcGetName(p->second.tid, p->second.getComm(), p->second.getCmdline());
+                if (!tidCmdline.empty()) 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 llkCheckEng(const std::string& property) {
+    return android::base::GetProperty(property, "eng") == "eng";
+}
+
+bool llkInit(const char* threadname) {
+    auto debuggable = android::base::GetBoolProperty("ro.debuggable", false);
+    llkLowRam = android::base::GetBoolProperty("ro.config.low_ram", false);
+    llkEnableSysrqT &= !llkLowRam;
+    if (debuggable) {
+        llkEnableSysrqT |= llkCheckEng(LLK_ENABLE_SYSRQ_T_PROPERTY);
+        if (!LLK_ENABLE_DEFAULT) {  // NB: default is currently true ...
+            llkEnable |= llkCheckEng(LLK_ENABLE_PROPERTY);
+            khtEnable |= llkCheckEng(KHT_ENABLE_PROPERTY);
+        }
+    }
+    llkEnableSysrqT = android::base::GetBoolProperty(LLK_ENABLE_SYSRQ_T_PROPERTY, llkEnableSysrqT);
+    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);
+#ifdef __PTRACE_ENABLED__
+    llkStateTimeoutMs[llkStateStack] = GetUintProperty(LLK_STACK_TIMEOUT_MS_PROPERTY, llkTimeoutMs);
+#endif
+    llkCheckMs = GetUintProperty(LLK_CHECK_MS_PROPERTY, llkCheckMs);
+    llkValidate();  // validate all (effectively minus llkTimeoutMs)
+#ifdef __PTRACE_ENABLED__
+    if (debuggable) {
+        llkCheckStackSymbols = llkSplit(LLK_CHECK_STACK_PROPERTY, LLK_CHECK_STACK_DEFAULT);
+    }
+    std::string defaultBlacklistStack(LLK_BLACKLIST_STACK_DEFAULT);
+    if (!debuggable) defaultBlacklistStack += ",logd,/system/bin/logd";
+    llkBlacklistStack = llkSplit(LLK_BLACKLIST_STACK_PROPERTY, defaultBlacklistStack);
+#endif
+    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 += ","s + threadname;
+    }
+    for (int cpu = 1; cpu < get_nprocs_conf(); ++cpu) {
+        defaultBlacklistProcess += ",[watchdog/" + std::to_string(cpu) + "]";
+    }
+    llkBlacklistProcess = llkSplit(LLK_BLACKLIST_PROCESS_PROPERTY, defaultBlacklistProcess);
+    if (!llkSkipName("[khungtaskd]")) {  // ALWAYS ignore as special
+        llkBlacklistProcess.emplace("[khungtaskd]");
+    }
+    llkBlacklistParent = llkSplit(LLK_BLACKLIST_PARENT_PROPERTY,
+                                  std::to_string(kernelPid) + "," + std::to_string(kthreaddPid) +
+                                          "," LLK_BLACKLIST_PARENT_DEFAULT);
+    // derive llkBlacklistParentAndChild by moving entries with '&' from above
+    for (auto it = llkBlacklistParent.begin(); it != llkBlacklistParent.end();) {
+        auto pos = it->find('&');
+        if (pos == std::string::npos) {
+            ++it;
+            continue;
+        }
+        auto parent = it->substr(0, pos);
+        auto child = it->substr(pos + 1);
+        it = llkBlacklistParent.erase(it);
+
+        auto found = llkBlacklistParentAndChild.find(parent);
+        if (found == llkBlacklistParentAndChild.end()) {
+            llkBlacklistParentAndChild.emplace(std::make_pair(
+                    std::move(parent), std::unordered_set<std::string>({std::move(child)})));
+        } else {
+            found->second.emplace(std::move(child));
+        }
+    }
+
+    llkBlacklistUid = llkSplit(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-debuggable.rc b/llkd/llkd-debuggable.rc
new file mode 100644
index 0000000..724cb5e
--- /dev/null
+++ b/llkd/llkd-debuggable.rc
@@ -0,0 +1,19 @@
+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}
+
+service llkd-1 /system/bin/llkd
+    class late_start
+    disabled
+    user llkd
+    group llkd readproc
+    capabilities KILL IPC_LOCK SYS_PTRACE DAC_OVERRIDE
+    file /dev/kmsg w
+    file /proc/sysrq-trigger w
+    writepid /dev/cpuset/system-background/tasks
diff --git a/llkd/llkd.cpp b/llkd/llkd.cpp
new file mode 100644
index 0000000..1920198
--- /dev/null
+++ b/llkd/llkd.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 <sys/prctl.h>
+#include <unistd.h>
+
+#include <chrono>
+
+#include <android-base/logging.h>
+
+using namespace std::chrono;
+
+int main(int, char**) {
+    prctl(PR_SET_DUMPABLE, 0);
+
+    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..b1f96a8
--- /dev/null
+++ b/llkd/llkd.rc
@@ -0,0 +1,45 @@
+# 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.llk.enable=true
+    setprop llk.enable true
+
+on property:llk.enable=1
+    setprop llk.enable true
+
+on property:llk.enable=0
+    setprop llk.enable false
+
+on property:ro.khungtask.enable=true
+    setprop khungtask.enable true
+
+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-${ro.debuggable:-0}
+
+service llkd-0 /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..96079cc
--- /dev/null
+++ b/llkd/tests/llkd_test.cpp
@@ -0,0 +1,370 @@
+/*
+ * 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 <signal.h>
+#include <stdint.h>
+#include <sys/prctl.h>
+#include <sys/stat.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-0");
+        execute("stop llkd-1");
+        rest();
+        std::string setprop("setprop ");
+        // Manually check that SyS_openat is _added_ to the list when restarted
+        execute((setprop + LLK_CHECK_STACK_PROPERTY + " ,SyS_openat").c_str());
+        rest();
+        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();
+        if (!default_enable) {
+            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-1");
+        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
+                                                  : (state == 'S') ? LLK_STACK_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);
+}
+
+TEST(llkd, sleep) {
+    if (checkKill("kernel_panic,sysrq,livelock,sleeping")) {
+        return;
+    }
+    if (!android::base::GetBoolProperty("ro.debuggable", false)) {
+        GTEST_LOG_WARNING << "Features not available on user builds\n";
+    }
+
+    const auto period = llkdSleepPeriod('S');
+
+    /* Create a Persistent SyS_openat for single-ended pipe */
+    static constexpr char stack_pipe_file[] = "/dev/stack_pipe_file";
+    unlink(stack_pipe_file);
+    auto pipe_ret = mknod(stack_pipe_file, S_IFIFO | 0666, 0);
+    ASSERT_LE(0, pipe_ret);
+
+    auto child_pid = fork();
+    ASSERT_LE(0, child_pid);
+    if (!child_pid) {
+        child_pid = fork();
+        ASSERT_LE(0, child_pid);
+        if (!child_pid) {
+            sleep(period.count());
+            auto fd = open(stack_pipe_file, O_RDONLY | O_CLOEXEC);
+            close(fd);
+            exit(0);
+        } else {
+            auto fd = open(stack_pipe_file, O_WRONLY | O_CLOEXEC);
+            close(fd);
+            exit(42);
+        }
+    }
+
+    waitForPid(child_pid);
+
+    unlink(stack_pipe_file);
+}
+
+// b/120983740
+TEST(llkd, adbd_and_setsid) {
+    if (checkKill("kernel_panic,sysrq,livelock,zombie")) {
+        return;
+    }
+    const auto period = llkdSleepPeriod('S');
+
+    // expect llkd.zombie to trigger, but not for adbd&[setsid]
+    // Create a Persistent Zombie setsid Process
+    pid_t child_pid = fork();
+    ASSERT_LE(0, child_pid);
+    if (!child_pid) {
+        prctl(PR_SET_NAME, "adbd");
+        auto zombie_pid = fork();
+        ASSERT_LE(0, zombie_pid);
+        if (!zombie_pid) {
+            prctl(PR_SET_NAME, "setsid");
+            sleep(1);
+            exit(0);
+        }
+        sleep(period.count());
+        exit(42);
+    }
+
+    // Reverse of waitForPid, do _not_ expect kill
+    int wstatus;
+    ASSERT_LE(0, waitpid(child_pid, &wstatus, 0));
+    EXPECT_TRUE(WIFEXITED(wstatus));
+    if (WIFEXITED(wstatus)) {
+        EXPECT_EQ(42, WEXITSTATUS(wstatus));
+    }
+    ASSERT_FALSE(WIFSIGNALED(wstatus)) << "[   INFO   ] signo=" << WTERMSIG(wstatus);
+}
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
index 0474ff5..4c5ca03 100644
--- a/lmkd/Android.bp
+++ b/lmkd/Android.bp
@@ -3,8 +3,10 @@
 
     srcs: ["lmkd.c"],
     shared_libs: [
-        "liblog",
         "libcutils",
+        "liblog",
+        "libprocessgroup",
+        "libpsi",
     ],
     static_libs: [
         "libstatslogc",
@@ -20,6 +22,7 @@
             ],
         },
     },
+    logtags: ["event.logtags"],
 }
 
 cc_library_static {
diff --git a/lmkd/event.logtags b/lmkd/event.logtags
new file mode 100644
index 0000000..065c6db
--- /dev/null
+++ b/lmkd/event.logtags
@@ -0,0 +1,38 @@
+# 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
+#
+# 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
+# s: Number of seconds (monotonic time)
+# Default value for data of type int/long is 2 (bytes).
+#
+# TODO: generate ".java" and ".h" files with integer constants from this file.
+
+# for meminfo logs
+10195355 meminfo (MemFree|1),(Cached|1),(SwapCached|1),(Buffers|1),(Shmem|1),(Unevictable|1),(SwapTotal|1),(SwapFree|1),(ActiveAnon|1),(InactiveAnon|1),(ActiveFile|1),(InactiveFile|1),(SReclaimable|1),(SUnreclaim|1),(KernelStack|1),(PageTables|1),(ION_heap|1),(ION_heap_pool|1),(CmaFree|1)
diff --git a/lmkd/include/lmkd.h b/lmkd/include/lmkd.h
index e8f51da..59377dd 100644
--- a/lmkd/include/lmkd.h
+++ b/lmkd/include/lmkd.h
@@ -31,6 +31,7 @@
     LMK_PROCPRIO,    /* Register a process and set its oom_adj_score */
     LMK_PROCREMOVE,  /* Unregister a process */
     LMK_PROCPURGE,   /* Purge all registered processes */
+    LMK_GETKILLCNT,  /* Get number of kills */
 };
 
 /*
@@ -49,7 +50,7 @@
 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) {
+static inline enum lmk_cmd lmkd_pack_get_cmd(LMKD_CTRL_PACKET pack) {
     return (enum lmk_cmd)ntohl(pack[0]);
 }
 
@@ -63,8 +64,8 @@
  * 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) {
+static 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]);
 }
@@ -73,9 +74,8 @@
  * 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) {
+static 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) {
@@ -98,8 +98,7 @@
  * 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) {
+static 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]);
@@ -109,8 +108,7 @@
  * 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) {
+static 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);
@@ -127,8 +125,8 @@
  * 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) {
+static inline void lmkd_pack_get_procremove(LMKD_CTRL_PACKET packet,
+                                            struct lmk_procremove* params) {
     params->pid = (pid_t)ntohl(packet[1]);
 }
 
@@ -136,8 +134,8 @@
  * 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) {
+static 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);
@@ -147,11 +145,49 @@
  * Prepare LMK_PROCPURGE packet and return packet size in bytes.
  * Warning: no checks performed, caller should ensure valid parameters.
  */
-inline size_t lmkd_pack_set_procpurge(LMKD_CTRL_PACKET packet) {
+static inline size_t lmkd_pack_set_procpurge(LMKD_CTRL_PACKET packet) {
     packet[0] = htonl(LMK_PROCPURGE);
     return sizeof(int);
 }
 
+/* LMK_GETKILLCNT packet payload */
+struct lmk_getkillcnt {
+    int min_oomadj;
+    int max_oomadj;
+};
+
+/*
+ * For LMK_GETKILLCNT packet get its payload.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+static inline void lmkd_pack_get_getkillcnt(LMKD_CTRL_PACKET packet,
+                                            struct lmk_getkillcnt* params) {
+    params->min_oomadj = ntohl(packet[1]);
+    params->max_oomadj = ntohl(packet[2]);
+}
+
+/*
+ * Prepare LMK_GETKILLCNT packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+static inline size_t lmkd_pack_set_getkillcnt(LMKD_CTRL_PACKET packet,
+                                              struct lmk_getkillcnt* params) {
+    packet[0] = htonl(LMK_GETKILLCNT);
+    packet[1] = htonl(params->min_oomadj);
+    packet[2] = htonl(params->max_oomadj);
+    return 3 * sizeof(int);
+}
+
+/*
+ * Prepare LMK_GETKILLCNT reply packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+static inline size_t lmkd_pack_set_getkillcnt_repl(LMKD_CTRL_PACKET packet, int kill_cnt) {
+    packet[0] = htonl(LMK_GETKILLCNT);
+    packet[1] = htonl(kill_cnt);
+    return 2 * sizeof(int);
+}
+
 __END_DECLS
 
 #endif /* _LMKD_H_ */
diff --git a/lmkd/libpsi/Android.bp b/lmkd/libpsi/Android.bp
new file mode 100644
index 0000000..8a97094
--- /dev/null
+++ b/lmkd/libpsi/Android.bp
@@ -0,0 +1,22 @@
+cc_library_headers {
+    name: "libpsi_headers",
+    export_include_dirs: ["include"],
+}
+
+cc_library {
+    name: "libpsi",
+    srcs: ["psi.c"],
+    shared_libs: [
+        "liblog"
+    ],
+    header_libs: [
+        "libpsi_headers",
+    ],
+    export_header_lib_headers: [
+        "libpsi_headers",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/lmkd/libpsi/OWNERS b/lmkd/libpsi/OWNERS
new file mode 100644
index 0000000..b15bb48
--- /dev/null
+++ b/lmkd/libpsi/OWNERS
@@ -0,0 +1 @@
+surenb@google.com
diff --git a/lmkd/libpsi/include/psi/psi.h b/lmkd/libpsi/include/psi/psi.h
new file mode 100644
index 0000000..cd49e8b
--- /dev/null
+++ b/lmkd/libpsi/include/psi/psi.h
@@ -0,0 +1,68 @@
+/*
+ * 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_PSI_H__
+#define __ANDROID_PSI_H__
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+enum psi_stall_type {
+    PSI_SOME,
+    PSI_FULL,
+    PSI_TYPE_COUNT
+};
+
+/*
+ * Initializes psi monitor.
+ * stall_type, threshold_us and window_us are monitor parameters
+ * When successful, the function returns file descriptor that can
+ * be used with poll/epoll syscalls to wait for EPOLLPRI events.
+ * When unsuccessful, the function returns -1 and errno is set
+ * appropriately.
+ */
+int init_psi_monitor(enum psi_stall_type stall_type,
+        int threshold_us, int window_us);
+
+/*
+ * Registers psi monitor file descriptor fd on the epoll instance
+ * referred to by the file descriptor epollfd.
+ * data parameter will be associated with event's epoll_data.ptr
+ * member.
+ */
+int register_psi_monitor(int epollfd, int fd, void* data);
+
+/*
+ * Unregisters psi monitor file descriptor fd from the epoll instance
+ * referred to by the file descriptor epollfd.
+ */
+int unregister_psi_monitor(int epollfd, int fd);
+
+/*
+ * Destroys psi monitor.
+ * fd is the file descriptor returned by psi monitor initialization
+ * routine.
+ * Note that if user process exits without calling this routine
+ * kernel will destroy the monitor as its lifetime is linked to
+ * the file descriptor.
+ */
+void destroy_psi_monitor(int fd);
+
+__END_DECLS
+
+#endif  // __ANDROID_PSI_H__
diff --git a/lmkd/libpsi/psi.c b/lmkd/libpsi/psi.c
new file mode 100644
index 0000000..f4d5d18
--- /dev/null
+++ b/lmkd/libpsi/psi.c
@@ -0,0 +1,99 @@
+/*
+ * 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 "libpsi"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/epoll.h>
+
+#include <log/log.h>
+#include "psi/psi.h"
+
+#define PSI_MON_FILE_MEMORY "/proc/pressure/memory"
+
+static const char* stall_type_name[] = {
+        "some",
+        "full",
+};
+
+int init_psi_monitor(enum psi_stall_type stall_type,
+             int threshold_us, int window_us) {
+    int fd;
+    int res;
+    char buf[256];
+
+    fd = TEMP_FAILURE_RETRY(open(PSI_MON_FILE_MEMORY, O_WRONLY | O_CLOEXEC));
+    if (fd < 0) {
+        ALOGE("No kernel psi monitor support (errno=%d)", errno);
+        return -1;
+    }
+
+    switch (stall_type) {
+    case (PSI_SOME):
+    case (PSI_FULL):
+        res = snprintf(buf, sizeof(buf), "%s %d %d",
+            stall_type_name[stall_type], threshold_us, window_us);
+        break;
+    default:
+        ALOGE("Invalid psi stall type: %d", stall_type);
+        errno = EINVAL;
+        goto err;
+    }
+
+    if (res >= (ssize_t)sizeof(buf)) {
+        ALOGE("%s line overflow for psi stall type '%s'",
+            PSI_MON_FILE_MEMORY, stall_type_name[stall_type]);
+        errno = EINVAL;
+        goto err;
+    }
+
+    res = TEMP_FAILURE_RETRY(write(fd, buf, strlen(buf) + 1));
+    if (res < 0) {
+        ALOGE("%s write failed for psi stall type '%s'; errno=%d",
+            PSI_MON_FILE_MEMORY, stall_type_name[stall_type], errno);
+        goto err;
+    }
+
+    return fd;
+
+err:
+    close(fd);
+    return -1;
+}
+
+int register_psi_monitor(int epollfd, int fd, void* data) {
+    int res;
+    struct epoll_event epev;
+
+    epev.events = EPOLLPRI;
+    epev.data.ptr = data;
+    res = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &epev);
+    if (res < 0) {
+        ALOGE("epoll_ctl for psi monitor failed; errno=%d", errno);
+    }
+    return res;
+}
+
+int unregister_psi_monitor(int epollfd, int fd) {
+    return epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL);
+}
+
+void destroy_psi_monitor(int fd) {
+    if (fd >= 0) {
+        close(fd);
+    }
+}
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 92cdaa0..48140b8 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -19,8 +19,10 @@
 #include <dirent.h>
 #include <errno.h>
 #include <inttypes.h>
+#include <pwd.h>
 #include <sched.h>
 #include <signal.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/cdefs.h>
@@ -32,6 +34,7 @@
 #include <sys/sysinfo.h>
 #include <sys/time.h>
 #include <sys/types.h>
+#include <time.h>
 #include <unistd.h>
 
 #include <cutils/properties.h>
@@ -39,6 +42,9 @@
 #include <cutils/sockets.h>
 #include <lmkd.h>
 #include <log/log.h>
+#include <log/log_event_list.h>
+#include <log/log_time.h>
+#include <psi/psi.h>
 #include <system/thread_defs.h>
 
 #ifdef LMKD_LOG_STATS
@@ -75,19 +81,45 @@
 #define MEMINFO_PATH "/proc/meminfo"
 #define LINE_MAX 128
 
+/* Android Logger event logtags (see event.logtags) */
+#define MEMINFO_LOG_TAG 10195355
+
+/* 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)
 
+#define TARGET_UPDATE_MIN_INTERVAL_MS 1000
+
+#define NS_PER_MS (NS_PER_SEC / MS_PER_SEC)
+#define US_PER_MS (US_PER_SEC / MS_PER_SEC)
+
+/* Defined as ProcessList.SYSTEM_ADJ in ProcessList.java */
+#define SYSTEM_ADJ (-900)
+
 #define STRINGIFY(x) STRINGIFY_INTERNAL(x)
 #define STRINGIFY_INTERNAL(x) #x
 
+/*
+ * PSI monitor tracking window size.
+ * PSI monitor generates events at most once per window,
+ * therefore we poll memory state for the duration of
+ * PSI_WINDOW_SIZE_MS after the event happens.
+ */
+#define PSI_WINDOW_SIZE_MS 1000
+/* Polling period after initial PSI signal */
+#define PSI_POLL_PERIOD_MS 10
+/* Poll for the duration of one window after initial PSI signal */
+#define PSI_POLL_COUNT (PSI_WINDOW_SIZE_MS / PSI_POLL_PERIOD_MS)
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+
 #define FAIL_REPORT_RLIMIT_MS 1000
 
 /* 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 levels */
@@ -109,6 +141,11 @@
     int64_t max_nr_free_pages;
 } low_pressure_mem = { -1, -1 };
 
+struct psi_threshold {
+    enum psi_stall_type stall_type;
+    int threshold_ms;
+};
+
 static int level_oomadj[VMPRESS_LEVEL_COUNT];
 static int mpevfd[VMPRESS_LEVEL_COUNT] = { -1, -1, -1 };
 static bool debug_process_killing;
@@ -120,6 +157,15 @@
 static unsigned long kill_timeout_ms;
 static bool use_minfree_levels;
 static bool per_app_memcg;
+static int swap_free_low_percentage;
+static bool use_psi_monitors = false;
+static struct psi_threshold psi_thresholds[VMPRESS_LEVEL_COUNT] = {
+    { PSI_SOME, 70 },    /* 70ms out of 1sec for partial stall */
+    { PSI_SOME, 100 },   /* 100ms out of 1sec for partial stall */
+    { PSI_FULL, 70 },    /* 70ms out of 1sec for complete stall */
+};
+
+static android_log_context ctx;
 
 /* data required to handle events */
 struct event_handler_info {
@@ -143,8 +189,8 @@
 /* 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)
+/* 3 memory pressure levels, 1 ctrl listen socket, 2 ctrl data socket, 1 lmk events */
+#define MAX_EPOLL_EVENTS (2 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT)
 static int epollfd;
 static int maxevents;
 
@@ -198,8 +244,19 @@
     MI_BUFFERS,
     MI_SHMEM,
     MI_UNEVICTABLE,
+    MI_TOTAL_SWAP,
     MI_FREE_SWAP,
-    MI_DIRTY,
+    MI_ACTIVE_ANON,
+    MI_INACTIVE_ANON,
+    MI_ACTIVE_FILE,
+    MI_INACTIVE_FILE,
+    MI_SRECLAIMABLE,
+    MI_SUNRECLAIM,
+    MI_KERNEL_STACK,
+    MI_PAGE_TABLES,
+    MI_ION_HELP,
+    MI_ION_HELP_POOL,
+    MI_CMA_FREE,
     MI_FIELD_COUNT
 };
 
@@ -210,8 +267,19 @@
     "Buffers:",
     "Shmem:",
     "Unevictable:",
+    "SwapTotal:",
     "SwapFree:",
-    "Dirty:",
+    "Active(anon):",
+    "Inactive(anon):",
+    "Active(file):",
+    "Inactive(file):",
+    "SReclaimable:",
+    "SUnreclaim:",
+    "KernelStack:",
+    "PageTables:",
+    "ION_heap:",
+    "ION_heap_pool:",
+    "CmaFree:",
 };
 
 union meminfo {
@@ -222,8 +290,19 @@
         int64_t buffers;
         int64_t shmem;
         int64_t unevictable;
+        int64_t total_swap;
         int64_t free_swap;
-        int64_t dirty;
+        int64_t active_anon;
+        int64_t inactive_anon;
+        int64_t active_file;
+        int64_t inactive_file;
+        int64_t sreclaimable;
+        int64_t sunreclaimable;
+        int64_t kernel_stack;
+        int64_t page_tables;
+        int64_t ion_heap;
+        int64_t ion_heap_pool;
+        int64_t cma_free;
         /* fields below are calculated rather than read from the file */
         int64_t nr_file_pages;
     } field;
@@ -264,7 +343,20 @@
 #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
 
 #define ADJTOSLOT(adj) ((adj) + -OOM_SCORE_ADJ_MIN)
-static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1];
+#define ADJTOSLOT_COUNT (ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1)
+static struct adjslot_list procadjslot_list[ADJTOSLOT_COUNT];
+
+#define MAX_DISTINCT_OOM_ADJ 32
+#define KILLCNT_INVALID_IDX 0xFF
+/*
+ * Because killcnt array is sparse a two-level indirection is used
+ * to keep the size small. killcnt_idx stores index of the element in
+ * killcnt array. Index KILLCNT_INVALID_IDX indicates an unused slot.
+ */
+static uint8_t killcnt_idx[ADJTOSLOT_COUNT];
+static uint16_t killcnt[MAX_DISTINCT_OOM_ADJ];
+static int killcnt_free_idx = 0;
+static uint32_t killcnt_total = 0;
 
 /* PAGE_SIZE / 1024 */
 static long page_k;
@@ -346,7 +438,7 @@
         data->fd = -1;
         return -1;
     }
-    ALOG_ASSERT((size_t)size < buf_size - 1, data->filename " too large");
+    ALOG_ASSERT((size_t)size < buf_size - 1, "%s too large", data->filename);
     buf[size] = 0;
 
     return 0;
@@ -425,24 +517,38 @@
     return 0;
 }
 
-static void writefilestring(const 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 inline long get_time_diff_ms(struct timespec *from,
+                                    struct timespec *to) {
+    return (to->tv_sec - from->tv_sec) * (long)MS_PER_SEC +
+           (to->tv_nsec - from->tv_nsec) / (long)NS_PER_MS;
 }
 
 static void cmd_procprio(LMKD_CTRL_PACKET packet) {
@@ -451,6 +557,8 @@
     char val[20];
     int soft_limit_mult;
     struct lmk_procprio params;
+    bool is_system_server;
+    struct passwd *pwdrec;
 
     lmkd_pack_get_procprio(packet, &params);
 
@@ -460,14 +568,23 @@
         return;
     }
 
+    /* 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);
-    writefilestring(path, val);
-
-    if (use_inkernel_interface)
+    if (!writefilestring(path, val, false)) {
+        ALOGW("Failed to open %s; errno=%d: process %d might have been killed",
+              path, errno, params.pid);
+        /* If this file does not exist the process is dead. */
         return;
+    }
 
-    if (low_ram_device) {
+    if (use_inkernel_interface) {
+        return;
+    }
+
+    if (per_app_memcg) {
         if (params.oomadj >= 900) {
             soft_limit_mult = 0;
         } else if (params.oomadj >= 800) {
@@ -485,7 +602,7 @@
         } else if (params.oomadj >= 300) {
             soft_limit_mult = 1;
         } else if (params.oomadj >= 200) {
-            soft_limit_mult = 2;
+            soft_limit_mult = 8;
         } else if (params.oomadj >= 100) {
             soft_limit_mult = 10;
         } else if (params.oomadj >=   0) {
@@ -496,11 +613,19 @@
             soft_limit_mult = 64;
         }
 
-        snprintf(path, sizeof(path),
-             "/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes",
-             params.uid, params.pid);
+        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);
-        writefilestring(path, val);
+
+        /*
+         * 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);
@@ -525,8 +650,9 @@
 static void cmd_procremove(LMKD_CTRL_PACKET packet) {
     struct lmk_procremove params;
 
-    if (use_inkernel_interface)
+    if (use_inkernel_interface) {
         return;
+    }
 
     lmkd_pack_get_procremove(packet, &params);
     /*
@@ -561,21 +687,116 @@
     memset(&pidhash[0], 0, sizeof(pidhash));
 }
 
+static void inc_killcnt(int oomadj) {
+    int slot = ADJTOSLOT(oomadj);
+    uint8_t idx = killcnt_idx[slot];
+
+    if (idx == KILLCNT_INVALID_IDX) {
+        /* index is not assigned for this oomadj */
+        if (killcnt_free_idx < MAX_DISTINCT_OOM_ADJ) {
+            killcnt_idx[slot] = killcnt_free_idx;
+            killcnt[killcnt_free_idx] = 1;
+            killcnt_free_idx++;
+        } else {
+            ALOGW("Number of distinct oomadj levels exceeds %d",
+                MAX_DISTINCT_OOM_ADJ);
+        }
+    } else {
+        /*
+         * wraparound is highly unlikely and is detectable using total
+         * counter because it has to be equal to the sum of all counters
+         */
+        killcnt[idx]++;
+    }
+    /* increment total kill counter */
+    killcnt_total++;
+}
+
+static int get_killcnt(int min_oomadj, int max_oomadj) {
+    int slot;
+    int count = 0;
+
+    if (min_oomadj > max_oomadj)
+        return 0;
+
+    /* special case to get total kill count */
+    if (min_oomadj > OOM_SCORE_ADJ_MAX)
+        return killcnt_total;
+
+    while (min_oomadj <= max_oomadj &&
+           (slot = ADJTOSLOT(min_oomadj)) < ADJTOSLOT_COUNT) {
+        uint8_t idx = killcnt_idx[slot];
+        if (idx != KILLCNT_INVALID_IDX) {
+            count += killcnt[idx];
+        }
+        min_oomadj++;
+    }
+
+    return count;
+}
+
+static int cmd_getkillcnt(LMKD_CTRL_PACKET packet) {
+    struct lmk_getkillcnt params;
+
+    if (use_inkernel_interface) {
+        /* kernel driver does not expose this information */
+        return 0;
+    }
+
+    lmkd_pack_get_getkillcnt(packet, &params);
+
+    return get_killcnt(params.min_oomadj, params.max_oomadj);
+}
+
 static void cmd_target(int ntargets, LMKD_CTRL_PACKET packet) {
     int i;
     struct lmk_target target;
+    char minfree_str[PROPERTY_VALUE_MAX];
+    char *pstr = minfree_str;
+    char *pend = minfree_str + sizeof(minfree_str);
+    static struct timespec last_req_tm;
+    struct timespec curr_tm;
 
-    if (ntargets > (int)ARRAY_SIZE(lowmem_adj))
+    if (ntargets < 1 || ntargets > (int)ARRAY_SIZE(lowmem_adj))
         return;
 
+    /*
+     * Ratelimit minfree updates to once per TARGET_UPDATE_MIN_INTERVAL_MS
+     * to prevent DoS attacks
+     */
+    if (clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm) != 0) {
+        ALOGE("Failed to get current time");
+        return;
+    }
+
+    if (get_time_diff_ms(&last_req_tm, &curr_tm) <
+        TARGET_UPDATE_MIN_INTERVAL_MS) {
+        ALOGE("Ignoring frequent updated to lmkd limits");
+        return;
+    }
+
+    last_req_tm = curr_tm;
+
     for (i = 0; i < ntargets; i++) {
         lmkd_pack_get_target(packet, i, &target);
         lowmem_minfree[i] = target.minfree;
         lowmem_adj[i] = target.oom_adj_score;
+
+        pstr += snprintf(pstr, pend - pstr, "%d:%d,", target.minfree,
+            target.oom_adj_score);
+        if (pstr >= pend) {
+            /* if no more space in the buffer then terminate the loop */
+            pstr = pend;
+            break;
+        }
     }
 
     lowmem_targets_size = ntargets;
 
+    /* Override the last extra comma */
+    pstr[-1] = '\0';
+    property_set("sys.lmk.minfree_levels", minfree_str);
+
     if (has_inkernel_module) {
         char minfreestr[128];
         char killpriostr[128];
@@ -597,8 +818,8 @@
             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);
     }
 }
 
@@ -631,12 +852,28 @@
     return ret;
 }
 
+static int ctrl_data_write(int dsock_idx, char *buf, size_t bufsz) {
+    int ret = 0;
+
+    ret = TEMP_FAILURE_RETRY(write(data_sock[dsock_idx].sock, buf, bufsz));
+
+    if (ret == -1) {
+        ALOGE("control data socket write failed; errno=%d", errno);
+    } else if (ret == 0) {
+        ALOGE("Got EOF on control data socket");
+        ret = -1;
+    }
+
+    return ret;
+}
+
 static void ctrl_command_handler(int dsock_idx) {
     LMKD_CTRL_PACKET packet;
     int len;
     enum lmk_cmd cmd;
     int nargs;
     int targets;
+    int kill_cnt;
 
     len = ctrl_data_read(dsock_idx, (char *)packet, CTRL_PACKET_MAX_SIZE);
     if (len <= 0)
@@ -674,6 +911,14 @@
             goto wronglen;
         cmd_procpurge();
         break;
+    case LMK_GETKILLCNT:
+        if (nargs != 2)
+            goto wronglen;
+        kill_cnt = cmd_getkillcnt(packet);
+        len = lmkd_pack_set_getkillcnt_repl(packet, kill_cnt);
+        if (ctrl_data_write(dsock_idx, (char *)packet, len) != len)
+            return;
+        break;
     default:
         ALOGE("Received unknown command code %d", cmd);
         return;
@@ -762,7 +1007,7 @@
 }
 
 static int memory_stat_from_cgroup(struct memory_stat* mem_st, int pid, uid_t uid) {
-    FILE* fp;
+    FILE *fp;
     char buf[PATH_MAX];
 
     snprintf(buf, sizeof(buf), MEMCG_PROCESS_MEMORY_STAT_PATH, uid, pid);
@@ -803,19 +1048,20 @@
 
     // field 10 is pgfault
     // field 12 is pgmajfault
+    // field 22 is starttime
     // field 24 is rss_in_pages
-    int64_t pgfault = 0, pgmajfault = 0, rss_in_pages = 0;
+    int64_t pgfault = 0, pgmajfault = 0, starttime = 0, rss_in_pages = 0;
     if (sscanf(buffer,
                "%*u %*s %*s %*d %*d %*d %*d %*d %*d %" SCNd64 " %*d "
                "%" SCNd64 " %*d %*u %*u %*d %*d %*d %*d %*d %*d "
-               "%*d %*d %" SCNd64 "",
-               &pgfault, &pgmajfault, &rss_in_pages) != 3) {
+               "%" SCNd64 " %*d %" SCNd64 "",
+               &pgfault, &pgmajfault, &starttime, &rss_in_pages) != 4) {
         return -1;
     }
     mem_st->pgfault = pgfault;
     mem_st->pgmajfault = pgmajfault;
     mem_st->rss_in_bytes = (rss_in_pages * PAGE_SIZE);
-
+    mem_st->process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
     return 0;
 }
 #endif
@@ -959,6 +1205,15 @@
     return 0;
 }
 
+static void meminfo_log(union meminfo *mi) {
+    for (int field_idx = 0; field_idx < MI_FIELD_COUNT; field_idx++) {
+        android_log_write_int32(ctx, (int32_t)min(mi->arr[field_idx] * page_k, INT32_MAX));
+    }
+
+    android_log_write_list(ctx, LOG_ID_EVENTS);
+    android_log_reset(ctx);
+}
+
 static int proc_get_size(int pid) {
     char path[PATH_MAX];
     char line[LINE_MAX];
@@ -967,6 +1222,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)
@@ -990,6 +1246,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)
@@ -1041,7 +1298,8 @@
 
     snprintf(proc_path, sizeof(proc_path), "/proc/%d/task", pid);
     if (!(d = opendir(proc_path))) {
-        ALOGW("Failed to open %s; errno=%d: process pid(%d) might have died", proc_path, errno, pid);
+        ALOGW("Failed to open %s; errno=%d: process pid(%d) might have died", proc_path, errno,
+              pid);
         return;
     }
 
@@ -1071,7 +1329,7 @@
 static int last_killed_pid = -1;
 
 /* Kill one process specified by procp.  Returns the size of the process killed */
-static int kill_one_process(struct proc* procp) {
+static int kill_one_process(struct proc* procp, int min_oom_score) {
     int pid = procp->pid;
     uid_t uid = procp->uid;
     char *taskname;
@@ -1082,6 +1340,9 @@
 #ifdef LMKD_LOG_STATS
     struct memory_stat mem_st = {};
     int memory_stat_parse_result = -1;
+#else
+    /* To prevent unused parameter warning */
+    (void)(min_oom_score);
 #endif
 
     taskname = proc_get_name(pid);
@@ -1111,6 +1372,7 @@
 
     set_process_group_and_prio(pid, SP_FOREGROUND, ANDROID_PRIORITY_HIGHEST);
 
+    inc_killcnt(procp->oomadj);
     ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB",
         taskname, pid, uid, procp->oomadj, tasksize * page_k);
 
@@ -1126,10 +1388,12 @@
         if (memory_stat_parse_result == 0) {
             stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname,
                     procp->oomadj, mem_st.pgfault, mem_st.pgmajfault, mem_st.rss_in_bytes,
-                    mem_st.cache_in_bytes, mem_st.swap_in_bytes);
+                    mem_st.cache_in_bytes, mem_st.swap_in_bytes, mem_st.process_start_time_ns,
+                    min_oom_score);
         } else if (enable_stats_log) {
             stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname, procp->oomadj,
-                                          -1, -1, tasksize * BYTES_IN_KILOBYTE, -1, -1);
+                                          -1, -1, tasksize * BYTES_IN_KILOBYTE, -1, -1, -1,
+                                          min_oom_score);
         }
 #endif
         result = tasksize;
@@ -1145,14 +1409,12 @@
 }
 
 /*
- * 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.
+ * Find one process to kill at or above the given oom_adj level.
+ * Returns size of the killed process.
  */
-static int find_and_kill_processes(int min_score_adj, int pages_to_free) {
+static int find_and_kill_process(int min_score_adj) {
     int i;
-    int killed_size;
-    int pages_freed = 0;
+    int killed_size = 0;
 
 #ifdef LMKD_LOG_STATS
     bool lmk_state_change_start = false;
@@ -1168,7 +1430,7 @@
             if (!procp)
                 break;
 
-            killed_size = kill_one_process(procp);
+            killed_size = kill_one_process(procp, min_score_adj);
             if (killed_size >= 0) {
 #ifdef LMKD_LOG_STATS
                 if (enable_stats_log && !lmk_state_change_start) {
@@ -1177,20 +1439,12 @@
                                                   LMK_STATE_CHANGE_START);
                 }
 #endif
-
-                pages_freed += killed_size;
-                if (pages_freed >= pages_to_free) {
-
-#ifdef LMKD_LOG_STATS
-                    if (enable_stats_log && lmk_state_change_start) {
-                        stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED,
-                                LMK_STATE_CHANGE_STOP);
-                    }
-#endif
-                    return pages_freed;
-                }
+                break;
             }
         }
+        if (killed_size) {
+            break;
+        }
     }
 
 #ifdef LMKD_LOG_STATS
@@ -1199,7 +1453,7 @@
     }
 #endif
 
-    return pages_freed;
+    return killed_size;
 }
 
 static int64_t get_memory_usage(struct reread_data *file_data) {
@@ -1259,14 +1513,9 @@
         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 bool is_kill_pending(void) {
     char buf[24];
+
     if (last_killed_pid < 0) {
         return false;
     }
@@ -1289,13 +1538,12 @@
     enum vmpressure_level lvl;
     union meminfo mi;
     union zoneinfo zi;
-    struct timeval curr_tm;
-    static struct timeval last_kill_tm;
+    struct timespec curr_tm;
+    static struct timespec last_kill_tm;
     static unsigned long kill_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,
@@ -1306,21 +1554,31 @@
         .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 (debug_process_killing) {
+        ALOGI("%s memory pressure event is triggered", level_name[level]);
+    }
+
+    if (!use_psi_monitors) {
+        /*
+         * 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;
+            }
         }
     }
 
-    gettimeofday(&curr_tm, NULL);
+    if (clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm) != 0) {
+        ALOGE("Failed to get current time");
+        return;
+    }
+
     if (kill_timeout_ms) {
         // If we're within the timeout, see if there's pending reclaim work
         // from the last killed process. If there is (as evidenced by
@@ -1373,9 +1631,6 @@
             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;
     }
 
@@ -1408,51 +1663,44 @@
         }
     }
 
-    // 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]);
+    // If we still have enough swap space available, check if we want to
+    // ignore/downgrade pressure events.
+    if (mi.field.free_swap >=
+        mi.field.total_swap * swap_free_low_percentage / 100) {
+        // 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);
         }
-        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_oomadj[level], 0) == 0) {
+        if (find_and_kill_process(level_oomadj[level]) == 0) {
             if (debug_process_killing) {
                 ALOGI("Nothing to kill");
             }
+        } else {
+            meminfo_log(&mi);
         }
     } else {
         int pages_freed;
-        static struct timeval last_report_tm;
+        static struct timespec last_report_tm;
         static unsigned long report_skip_count = 0;
 
         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 (mi.field.nr_free_pages >= low_pressure_mem.max_nr_free_pages) {
                 if (debug_process_killing) {
                     ALOGI("Ignoring pressure since more memory is "
                         "available (%" PRId64 ") than watermark (%" PRId64 ")",
@@ -1463,7 +1711,7 @@
             min_score_adj = level_oomadj[level];
         }
 
-        pages_freed = find_and_kill_processes(min_score_adj, 0);
+        pages_freed = find_and_kill_process(min_score_adj);
 
         if (pages_freed == 0) {
             /* Rate limit kill reports when nothing was reclaimed */
@@ -1476,16 +1724,19 @@
             last_kill_tm = curr_tm;
         }
 
+        /* Log meminfo whenever we kill or when report rate limit allows */
+        meminfo_log(&mi);
+
         if (use_minfree_levels) {
-            ALOGI("Killing to reclaim %ldkB, reclaimed %ldkB, cache(%ldkB) and "
+            ALOGI("Reclaimed %ldkB, cache(%ldkB) and "
                 "free(%" PRId64 "kB)-reserved(%" PRId64 "kB) below min(%ldkB) for oom_adj %d",
-                pages_to_free * page_k, pages_freed * page_k,
+                pages_freed * page_k,
                 other_file * page_k, mi.field.nr_free_pages * page_k,
                 zi.field.totalreserve_pages * page_k,
                 minfree * page_k, min_score_adj);
         } else {
-            ALOGI("Killing to reclaim %ldkB, reclaimed %ldkB at oom_adj %d",
-                pages_to_free * page_k, pages_freed * page_k, min_score_adj);
+            ALOGI("Reclaimed %ldkB at oom_adj %d",
+                pages_freed * page_k, min_score_adj);
         }
 
         if (report_skip_count > 0) {
@@ -1497,6 +1748,54 @@
     }
 }
 
+static bool init_mp_psi(enum vmpressure_level level) {
+    int fd = init_psi_monitor(psi_thresholds[level].stall_type,
+        psi_thresholds[level].threshold_ms * US_PER_MS,
+        PSI_WINDOW_SIZE_MS * US_PER_MS);
+
+    if (fd < 0) {
+        return false;
+    }
+
+    vmpressure_hinfo[level].handler = mp_event_common;
+    vmpressure_hinfo[level].data = level;
+    if (register_psi_monitor(epollfd, fd, &vmpressure_hinfo[level]) < 0) {
+        destroy_psi_monitor(fd);
+        return false;
+    }
+    maxevents++;
+    mpevfd[level] = fd;
+
+    return true;
+}
+
+static void destroy_mp_psi(enum vmpressure_level level) {
+    int fd = mpevfd[level];
+
+    if (unregister_psi_monitor(epollfd, fd) < 0) {
+        ALOGE("Failed to unregister psi monitor for %s memory pressure; errno=%d",
+            level_name[level], errno);
+    }
+    destroy_psi_monitor(fd);
+    mpevfd[level] = -1;
+}
+
+static bool init_psi_monitors() {
+    if (!init_mp_psi(VMPRESS_LEVEL_LOW)) {
+        return false;
+    }
+    if (!init_mp_psi(VMPRESS_LEVEL_MEDIUM)) {
+        destroy_mp_psi(VMPRESS_LEVEL_LOW);
+        return false;
+    }
+    if (!init_mp_psi(VMPRESS_LEVEL_CRITICAL)) {
+        destroy_mp_psi(VMPRESS_LEVEL_MEDIUM);
+        destroy_mp_psi(VMPRESS_LEVEL_LOW);
+        return false;
+    }
+    return true;
+}
+
 static bool init_mp_common(enum vmpressure_level level) {
     int mpfd;
     int evfd;
@@ -1507,6 +1806,7 @@
     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);
@@ -1563,6 +1863,74 @@
     return false;
 }
 
+#ifdef LMKD_LOG_STATS
+static int kernel_poll_fd = -1;
+
+static void poll_kernel() {
+    if (kernel_poll_fd == -1) {
+        // not waiting
+        return;
+    }
+
+    while (1) {
+        char rd_buf[256];
+        int bytes_read =
+                TEMP_FAILURE_RETRY(pread(kernel_poll_fd, (void*)rd_buf, sizeof(rd_buf), 0));
+        if (bytes_read <= 0) break;
+        rd_buf[bytes_read] = '\0';
+
+        int64_t pid;
+        int64_t uid;
+        int64_t group_leader_pid;
+        int64_t min_flt;
+        int64_t maj_flt;
+        int64_t rss_in_pages;
+        int16_t oom_score_adj;
+        int16_t min_score_adj;
+        int64_t starttime;
+        char* taskname = 0;
+        int fields_read = sscanf(rd_buf,
+                                 "%" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64
+                                 " %" SCNd64 " %" SCNd16 " %" SCNd16 " %" SCNd64 "\n%m[^\n]",
+                                 &pid, &uid, &group_leader_pid, &min_flt, &maj_flt, &rss_in_pages,
+                                 &oom_score_adj, &min_score_adj, &starttime, &taskname);
+
+        /* only the death of the group leader process is logged */
+        if (fields_read == 10 && group_leader_pid == pid) {
+            int64_t process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
+            stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname, oom_score_adj,
+                                          min_flt, maj_flt, rss_in_pages * PAGE_SIZE, 0, 0,
+                                          process_start_time_ns, min_score_adj);
+        }
+
+        free(taskname);
+    }
+}
+
+static struct event_handler_info kernel_poll_hinfo = {0, poll_kernel};
+
+static void init_poll_kernel() {
+    struct epoll_event epev;
+    kernel_poll_fd =
+            TEMP_FAILURE_RETRY(open("/proc/lowmemorykiller", O_RDONLY | O_NONBLOCK | O_CLOEXEC));
+
+    if (kernel_poll_fd < 0) {
+        ALOGE("kernel lmk event file could not be opened; errno=%d", kernel_poll_fd);
+        return;
+    }
+
+    epev.events = EPOLLIN;
+    epev.data.ptr = (void*)&kernel_poll_hinfo;
+    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, kernel_poll_fd, &epev) != 0) {
+        ALOGE("epoll_ctl for lmk events failed; errno=%d", errno);
+        close(kernel_poll_fd);
+        kernel_poll_fd = -1;
+    } else {
+        maxevents++;
+    }
+}
+#endif
+
 static int init(void) {
     struct epoll_event epev;
     int i;
@@ -1610,13 +1978,28 @@
 
     if (use_inkernel_interface) {
         ALOGI("Using in-kernel low memory killer interface");
+#ifdef LMKD_LOG_STATS
+        if (enable_stats_log) {
+            init_poll_kernel();
+        }
+#endif
     } else {
-        if (!init_mp_common(VMPRESS_LEVEL_LOW) ||
+        /* Try to use psi monitor first if kernel has it */
+        use_psi_monitors = property_get_bool("ro.lmk.use_psi", true) &&
+            init_psi_monitors();
+        /* Fall back to vmpressure */
+        if (!use_psi_monitors &&
+            (!init_mp_common(VMPRESS_LEVEL_LOW) ||
             !init_mp_common(VMPRESS_LEVEL_MEDIUM) ||
-            !init_mp_common(VMPRESS_LEVEL_CRITICAL)) {
+            !init_mp_common(VMPRESS_LEVEL_CRITICAL))) {
             ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
             return -1;
         }
+        if (use_psi_monitors) {
+            ALOGI("Using psi monitors for memory pressure detection");
+        } else {
+            ALOGI("Using vmpressure for memory pressure detection");
+        }
     }
 
     for (i = 0; i <= ADJTOSLOT(OOM_SCORE_ADJ_MAX); i++) {
@@ -1624,19 +2007,44 @@
         procadjslot_list[i].prev = &procadjslot_list[i];
     }
 
+    memset(killcnt_idx, KILLCNT_INVALID_IDX, sizeof(killcnt_idx));
+
     return 0;
 }
 
 static void mainloop(void) {
     struct event_handler_info* handler_info;
+    struct event_handler_info* poll_handler = NULL;
+    struct timespec last_report_tm, curr_tm;
     struct epoll_event *evt;
+    long delay = -1;
+    int polling = 0;
 
     while (1) {
         struct epoll_event events[maxevents];
         int nevents;
         int i;
 
-        nevents = epoll_wait(epollfd, events, maxevents, -1);
+        if (polling) {
+            /* Calculate next timeout */
+            clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm);
+            delay = get_time_diff_ms(&last_report_tm, &curr_tm);
+            delay = (delay < PSI_POLL_PERIOD_MS) ?
+                PSI_POLL_PERIOD_MS - delay : PSI_POLL_PERIOD_MS;
+
+            /* Wait for events until the next polling timeout */
+            nevents = epoll_wait(epollfd, events, maxevents, delay);
+
+            clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm);
+            if (get_time_diff_ms(&last_report_tm, &curr_tm) >= PSI_POLL_PERIOD_MS) {
+                polling--;
+                poll_handler->handler(poll_handler->data, 0);
+                last_report_tm = curr_tm;
+            }
+        } else {
+            /* Wait for events with no timeout */
+            nevents = epoll_wait(epollfd, events, maxevents, -1);
+        }
 
         if (nevents == -1) {
             if (errno == EINTR)
@@ -1671,6 +2079,17 @@
             if (evt->data.ptr) {
                 handler_info = (struct event_handler_info*)evt->data.ptr;
                 handler_info->handler(handler_info->data, evt->events);
+
+                if (use_psi_monitors && handler_info->handler == mp_event_common) {
+                    /*
+                     * Poll for the duration of PSI_WINDOW_SIZE_MS after the
+                     * initial PSI event because psi events are rate-limited
+                     * at one per sec.
+                     */
+                    polling = PSI_POLL_COUNT;
+                    poll_handler = handler_info;
+                    clock_gettime(CLOCK_MONOTONIC_COARSE, &last_report_tm);
+                }
             }
         }
     }
@@ -1704,31 +2123,50 @@
         (unsigned long)property_get_int32("ro.lmk.kill_timeout_ms", 0);
     use_minfree_levels =
         property_get_bool("ro.lmk.use_minfree_levels", false);
-    per_app_memcg = property_get_bool("ro.config.per_app_memcg", low_ram_device);
+    per_app_memcg =
+        property_get_bool("ro.config.per_app_memcg", low_ram_device);
+    swap_free_low_percentage =
+        property_get_int32("ro.lmk.swap_free_low_percentage", 10);
+
+    ctx = create_android_logger(MEMINFO_LOG_TAG);
+
 #ifdef LMKD_LOG_STATS
     statslog_init(&log_ctx, &enable_stats_log);
 #endif
 
-    // 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.
-    if (mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT) && errno != EINVAL)
-        ALOGW("mlockall failed: errno=%d", errno);
+    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));
+            }
 
-    sched_setscheduler(0, SCHED_FIFO, &param);
-    if (!init())
+            /* CAP_NICE required */
+            if (sched_setscheduler(0, SCHED_FIFO, &param)) {
+                ALOGW("set SCHED_FIFO failed %s", strerror(errno));
+            }
+        }
+
         mainloop();
+    }
 
 #ifdef LMKD_LOG_STATS
     statslog_destroy(&log_ctx);
 #endif
 
+    android_log_destroy(&ctx);
+
     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/statslog.c b/lmkd/statslog.c
index 66d1164..0c230ae 100644
--- a/lmkd/statslog.c
+++ b/lmkd/statslog.c
@@ -65,7 +65,8 @@
 stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
                               char const* process_name, int32_t oom_score, int64_t pgfault,
                               int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
-                              int64_t swap_in_bytes) {
+                              int64_t swap_in_bytes, int64_t process_start_time_ns,
+                              int32_t min_oom_score) {
     assert(ctx != NULL);
     int ret = -EINVAL;
     if (!ctx) {
@@ -113,5 +114,13 @@
         return ret;
     }
 
+    if ((ret = android_log_write_int64(ctx, process_start_time_ns)) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int32(ctx, min_oom_score)) < 0) {
+        return ret;
+    }
+
     return write_to_logger(ctx, LOG_ID_STATS);
 }
diff --git a/lmkd/statslog.h b/lmkd/statslog.h
index 8458480..2edba7a 100644
--- a/lmkd/statslog.h
+++ b/lmkd/statslog.h
@@ -64,6 +64,7 @@
     int64_t rss_in_bytes;
     int64_t cache_in_bytes;
     int64_t swap_in_bytes;
+    int64_t process_start_time_ns;
 };
 
 #define MEMCG_PROCESS_MEMORY_STAT_PATH "/dev/memcg/apps/uid_%u/pid_%u/memory.stat"
@@ -87,7 +88,8 @@
 stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
                               char const* process_name, int32_t oom_score, int64_t pgfault,
                               int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
-                              int64_t swap_in_bytes);
+                              int64_t swap_in_bytes, int64_t process_start_time_ns,
+                              int32_t min_oom_score);
 
 __END_DECLS
 
diff --git a/lmkd/tests/Android.bp b/lmkd/tests/Android.bp
index cbf44e9..4e845fd 100644
--- a/lmkd/tests/Android.bp
+++ b/lmkd/tests/Android.bp
@@ -18,6 +18,7 @@
     shared_libs: [
         "libbase",
         "liblog",
+        "libcutils",
     ],
 
     static_libs: [
diff --git a/lmkd/tests/lmkd_test.cpp b/lmkd/tests/lmkd_test.cpp
index c2ad74a..f54b25c 100644
--- a/lmkd/tests/lmkd_test.cpp
+++ b/lmkd/tests/lmkd_test.cpp
@@ -215,6 +215,13 @@
     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);
 
@@ -287,12 +294,6 @@
         GTEST_LOG_(INFO) << "Must be userdebug build, terminating test";
         return;
     }
-    // check if in-kernel LMK driver is present
-    if (!access(INKERNEL_MINFREE_PATH, W_OK)) {
-        GTEST_LOG_(INFO) << "Must not have kernel lowmemorykiller driver,"
-                         << " terminating test";
-        return;
-    }
 
     // if respawned test process then run the test and exit (no analysis)
     if (getenv(LMKDTEST_RESPAWN_FLAG) != NULL) {
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
index 01beb53..5030b15 100644
--- a/logcat/Android.bp
+++ b/logcat/Android.bp
@@ -1,5 +1,5 @@
 //
-// Copyright (C) 2006-2017 The Android Open Source Project
+// 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.
@@ -24,32 +24,20 @@
     ],
     shared_libs: [
         "libbase",
-        "libcutils",
-        "liblog",
         "libpcrecpp",
+        "libprocessgroup",
     ],
+    static_libs: ["liblog"],
     logtags: ["event.logtags"],
 }
 
-cc_library {
-    name: "liblogcat",
-
-    defaults: ["logcat_defaults"],
-    srcs: [
-        "logcat.cpp",
-        "getopt_long.cpp",
-        "logcat_system.cpp",
-    ],
-    export_include_dirs: ["include"],
-}
-
 cc_binary {
     name: "logcat",
 
     defaults: ["logcat_defaults"],
-    shared_libs: ["liblogcat"],
     srcs: [
         "logcat_main.cpp",
+        "logcat.cpp",
     ],
 }
 
@@ -57,9 +45,9 @@
     name: "logcatd",
 
     defaults: ["logcat_defaults"],
-    shared_libs: ["liblogcat"],
     srcs: [
         "logcatd_main.cpp",
+        "logcat.cpp",
     ],
 }
 
diff --git a/logcat/Android.mk b/logcat/Android.mk
deleted file mode 100644
index a716993..0000000
--- a/logcat/Android.mk
+++ /dev/null
@@ -1,5 +0,0 @@
-# Copyright 2006-2014 The Android Open Source Project
-
-LOCAL_PATH := $(call my-dir)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logcat/event.logtags b/logcat/event.logtags
index 750761f..3a1d36f 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -113,8 +113,11 @@
 # graphics timestamp
 # 60100 - 60199 reserved for surfaceflinger
 
-# 0 for screen off, 1 for screen on, 2 for key-guard done
-70000 screen_toggled (screen_state|1|5)
+# audio
+# 61000 - 61199 reserved for audioserver
+
+# com.android.server.policy
+# 70000 - 70199 reserved for PhoneWindowManager and other policies
 
 # aggregation service
 70200 aggregation (aggregation time|2|3)
diff --git a/logcat/getopt_long.cpp b/logcat/getopt_long.cpp
deleted file mode 100644
index da99906..0000000
--- a/logcat/getopt_long.cpp
+++ /dev/null
@@ -1,401 +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 <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 ff85f54..6e38d95 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -14,12 +14,16 @@
  * limitations under the License.
  */
 
+#include "logcat.h"
+
+#include <android-base/macros.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>
@@ -38,19 +42,18 @@
 #include <atomic>
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include <android-base/file.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#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 <processgroup/sched_policy.h>
 #include <system/thread_defs.h>
 
 #include <pcrecpp.h>
@@ -475,12 +478,13 @@
                     "  -G <size>, --buffer-size=<size>\n"
                     "                  Set size of log ring buffer, may suffix with K or M.\n"
                     "  -L, --last      Dump logs from prior to last reboot\n"
-                    // Leave security (Device Owner only installations) and
-                    // kernel (userdebug and eng) buffers undocumented.
                     "  -b <buffer>, --buffer=<buffer>         Request alternate ring buffer, 'main',\n"
                     "                  'system', 'radio', 'events', 'crash', 'default' or 'all'.\n"
+                    "                  Additionally, 'kernel' for userdebug and eng builds, and\n"
+                    "                  'security' for Device Owner installations.\n"
                     "                  Multiple -b parameters or comma separated list of buffers are\n"
-                    "                  allowed. Buffers interleaved. Default -b main,system,crash.\n"
+                    "                  allowed. Buffers interleaved.\n"
+                    "                  Default -b main,system,crash,kernel.\n"
                     "  -B, --binary    Output the log in binary.\n"
                     "  -S, --statistics                       Output statistics.\n"
                     "  -p, --prune     Print prune white and ~black list. Service is specified as\n"
@@ -564,23 +568,14 @@
     return android_log_setPrintFormat(context->logformat, format);
 }
 
-static const char multipliers[][2] = { { "" }, { "K" }, { "M" }, { "G" } };
-
-static unsigned long value_of_size(unsigned long value) {
-    for (unsigned i = 0;
-         (i < sizeof(multipliers) / sizeof(multipliers[0])) && (value >= 1024);
-         value /= 1024, ++i)
-        ;
-    return value;
-}
-
-static const char* multiplier_of_size(unsigned long value) {
-    unsigned i;
+static std::pair<unsigned long, const char*> format_of_size(unsigned long value) {
+    static const char multipliers[][3] = {{""}, {"Ki"}, {"Mi"}, {"Gi"}};
+    size_t i;
     for (i = 0;
          (i < sizeof(multipliers) / sizeof(multipliers[0])) && (value >= 1024);
          value /= 1024, ++i)
         ;
-    return multipliers[i];
+    return std::make_pair(value, multipliers[i]);
 }
 
 // String to unsigned int, returns -1 if it fails
@@ -854,14 +849,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 +891,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 +912,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 +935,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;
 
@@ -976,34 +961,29 @@
             case 't':
                 got_t = true;
                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
-            // FALLTHRU
+                FALLTHROUGH_INTENDED;
             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,30 +995,28 @@
                 break;
 
             case 'e':
-                context->regex = new pcrecpp::RE(optctx.optarg);
+                context->regex = new pcrecpp::RE(optarg);
                 break;
 
             case 'm': {
-                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;
                 }
-            // FALLTHRU
+                FALLTHROUGH_INTENDED;
 
             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;
                 }
@@ -1047,15 +1025,15 @@
                     case 'g':
                     case 'G':
                         setLogSize *= 1024;
-                    // FALLTHRU
+                        FALLTHROUGH_INTENDED;
                     case 'm':
                     case 'M':
                         setLogSize *= 1024;
-                    // FALLTHRU
+                        FALLTHROUGH_INTENDED;
                     case 'k':
                     case 'K':
                         setLogSize *= 1024;
-                    // FALLTHRU
+                        FALLTHROUGH_INTENDED;
                     case '\0':
                         break;
 
@@ -1071,19 +1049,18 @@
             } break;
 
             case 'p':
-                if (!optctx.optarg) {
+                if (!optarg) {
                     getPruneList = true;
                     break;
                 }
-            // FALLTHRU
+                FALLTHROUGH_INTENDED;
 
             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
@@ -1147,40 +1124,33 @@
 
             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();
                 char* sv = nullptr;  // protect against -ENOMEM above
                 while (!!(arg = strtok_r(arg, delimiters, &sv))) {
@@ -1300,8 +1270,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':
@@ -1310,8 +1279,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;
         }
     }
@@ -1345,6 +1313,10 @@
             dev = dev->next = new log_device_t("crash", false);
             context->devCount++;
         }
+        if (android_name_to_log_id("kernel") == LOG_ID_KERNEL) {
+            dev = dev->next = new log_device_t("kernel", false);
+            context->devCount++;
+        }
     }
 
     if (!!context->logRotateSizeKBytes && !context->outputFileName) {
@@ -1400,7 +1372,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");
 
@@ -1416,7 +1388,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
@@ -1498,12 +1470,14 @@
             if ((size < 0) || (readable < 0)) {
                 reportErrorName(&getSizeFail, dev->device, allSelected);
             } else {
+                auto size_format = format_of_size(size);
+                auto readable_format = format_of_size(readable);
                 std::string str = android::base::StringPrintf(
-                       "%s: ring buffer is %ld%sb (%ld%sb consumed),"
-                         " max entry is %db, max payload is %db\n",
+                       "%s: ring buffer is %lu %sB (%lu %sB consumed),"
+                         " max entry is %d B, max payload is %d B\n",
                        dev->device,
-                       value_of_size(size), multiplier_of_size(size),
-                       value_of_size(readable), multiplier_of_size(readable),
+                       size_format.first, size_format.second,
+                       readable_format.first, readable_format.second,
                        (int)LOGGER_ENTRY_MAX_LEN,
                        (int)LOGGER_ENTRY_MAX_PAYLOAD);
                 TEMP_FAILURE_RETRY(write(context->output_fd,
@@ -1707,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)) {
-        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)) {
-        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;
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 6dfd110..0000000
--- a/logcat/logcat_system.cpp
+++ /dev/null
@@ -1,153 +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 <fcntl.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;
-    }
-
-    int duped = dup(fd);
-    FILE* retval = fdopen(duped, "reb");
-    if (!retval) {
-        close(duped);
-        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.rc b/logcat/logcatd.rc
index 07040b0..25104eb 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -4,10 +4,15 @@
 # Make sure any property changes are only performed with /data mounted, after
 # post-fs-data state because otherwise behavior is undefined. The exceptions
 # are device adjustments for logcatd service properties (persist.* overrides
-# notwithstanding) for logd.logpersistd.size and logd.logpersistd.buffer.
+# notwithstanding) for logd.logpersistd.size logd.logpersistd.rotate_kbytes and
+# logd.logpersistd.buffer.
 
 # persist to non-persistent trampolines to permit device properties can be
 # overridden when /data mounts, or during runtime.
+on property:persist.logd.logpersistd.count=*
+    # expect /init to report failure if property empty (default)
+    setprop persist.logd.logpersistd.size ${persist.logd.logpersistd.count}
+
 on property:persist.logd.logpersistd.size=256
     setprop persist.logd.logpersistd.size ""
     setprop logd.logpersistd.size ""
@@ -16,6 +21,14 @@
     # expect /init to report failure if property empty (default)
     setprop logd.logpersistd.size ${persist.logd.logpersistd.size}
 
+on property:persist.logd.logpersistd.rotate_kbytes=1024
+    setprop persist.logd.logpersistd.rotate_kbytes ""
+    setprop logd.logpersistd.rotate_kbytes ""
+
+on property:persist.logd.logpersistd.rotate_kbytes=*
+   # expect /init to report failure if property empty (default)
+   setprop logd.logpersistd.rotate_kbytes ${persist.logd.logpersistd.rotate_kbytes}
+
 on property:persist.logd.logpersistd.buffer=all
     setprop persist.logd.logpersistd.buffer ""
     setprop logd.logpersistd.buffer ""
@@ -54,7 +67,7 @@
     stop logcatd
 
 # logcatd service
-service logcatd /system/bin/logcatd -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
+service logcatd /system/bin/logcatd -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r ${logd.logpersistd.rotate_kbytes:-1024} -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
     class late_start
     disabled
     # logd for write to /data/misc/logd, log group for read from log daemon
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.bp b/logcat/tests/Android.bp
new file mode 100644
index 0000000..ab84150
--- /dev/null
+++ b/logcat/tests/Android.bp
@@ -0,0 +1,57 @@
+//
+// Copyright (C) 2013-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.
+//
+
+cc_defaults {
+    name: "logcat-tests-defaults",
+    cflags: [
+        "-fstack-protector-all",
+        "-g",
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-fno-builtin",
+    ],
+}
+
+// -----------------------------------------------------------------------------
+// Benchmarks
+// ----------------------------------------------------------------------------
+
+// Build benchmarks for the device. Run with:
+//   adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks
+cc_benchmark {
+    name: "logcat-benchmarks",
+    defaults: ["logcat-tests-defaults"],
+    srcs: ["logcat_benchmark.cpp"],
+    shared_libs: ["libbase"],
+}
+
+// -----------------------------------------------------------------------------
+// Unit tests.
+// -----------------------------------------------------------------------------
+
+// Build tests for the device (with .so). Run with:
+//   adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests
+cc_test {
+    name: "logcat-unit-tests",
+    defaults: ["logcat-tests-defaults"],
+    shared_libs: ["libbase"],
+    static_libs: ["liblog"],
+    srcs: [
+        "logcat_test.cpp",
+        "logcatd_test.cpp",
+    ],
+}
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
deleted file mode 100644
index defd3c4..0000000
--- a/logcat/tests/Android.mk
+++ /dev/null
@@ -1,64 +0,0 @@
-#
-# Copyright (C) 2013-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 := logcat-
-test_tags := tests
-
-test_c_flags := \
-    -fstack-protector-all \
-    -g \
-    -Wall -Wextra \
-    -Werror \
-    -fno-builtin \
-
-# -----------------------------------------------------------------------------
-# Benchmarks
-# ----------------------------------------------------------------------------
-
-benchmark_src_files := \
-    logcat_benchmark.cpp \
-    exec_benchmark.cpp \
-
-# Build benchmarks for the device. Run with:
-#   adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)benchmarks
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SRC_FILES := $(benchmark_src_files)
-LOCAL_SHARED_LIBRARIES := libbase liblogcat
-include $(BUILD_NATIVE_BENCHMARK)
-
-# -----------------------------------------------------------------------------
-# Unit tests.
-# -----------------------------------------------------------------------------
-
-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
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)unit-tests
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog libbase liblogcat
-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 c8a00da..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..b32b437 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -31,18 +31,14 @@
 #include <string>
 
 #include <android-base/file.h>
+#include <android-base/macros.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <gtest/gtest.h>
 #include <log/event_tag_map.h>
 #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 +74,6 @@
 
 TEST(logcat, buckets) {
     FILE* fp;
-    logcat_define(ctx);
 
 #undef LOG_TAG
 #define LOG_TAG "inject.buckets"
@@ -90,10 +85,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 +105,7 @@
         }
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     EXPECT_EQ(ids, 15);
 
@@ -120,7 +114,6 @@
 
 TEST(logcat, event_tag_filter) {
     FILE* fp;
-    logcat_define(ctx);
 
 #undef LOG_TAG
 #define LOG_TAG "inject.filter"
@@ -135,7 +128,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 +138,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 +184,6 @@
 
     do {
         FILE* fp;
-        logcat_define(ctx);
 
         char needle[32];
         time_t now;
@@ -205,9 +197,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 +209,7 @@
                 ++count;
             }
         }
-        logcat_pclose(ctx, fp);
+        pclose(fp);
 
     } while ((count < 3) && --tries && inject(3 - count));
 
@@ -268,12 +259,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 +276,7 @@
             }
         }
 
-        logcat_pclose(ctx, fp);
+        pclose(fp);
 
     } while ((count < 3) && --tries && inject(3 - count));
 
@@ -296,11 +285,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 +301,7 @@
         }
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     ASSERT_EQ(0, count);
 }
@@ -330,8 +319,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 +327,7 @@
             ++count;
         }
 
-        logcat_pclose(ctx, fp);
+        pclose(fp);
 
     } while ((count < num) && --tries && inject(num - count));
 
@@ -377,8 +365,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 +378,7 @@
             free(last_timestamp);
             last_timestamp = strdup(input);
         }
-        logcat_pclose(ctx, fp);
+        pclose(fp);
 
     } while ((count < 10) && --tries && inject(10 - count));
 
@@ -401,8 +388,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 +428,7 @@
             last_timestamp_count = second_count;
         }
     }
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     EXPECT_TRUE(found);
     if (!found) {
@@ -483,10 +469,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 +491,7 @@
         }
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     ASSERT_EQ(1, count);
 }
@@ -521,12 +505,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 +537,7 @@
             }
         }
 
-        logcat_pclose(ctx[idx], fp[idx]);
+        pclose(fp[idx]);
     }
 
     ASSERT_EQ(num, count);
@@ -564,10 +545,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;
@@ -579,47 +559,48 @@
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         int size, consumed, max, payload;
-        char size_mult[3], consumed_mult[3];
+        char size_mult[4], consumed_mult[4];
         long full_size, full_consumed;
 
         size = consumed = max = payload = 0;
         // NB: crash log can be very small, not hit a Kb of consumed space
         //     doubly lucky we are not including it.
-        if (6 != sscanf(buffer,
-                        "%*s ring buffer is %d%2s (%d%2s consumed),"
-                        " max entry is %db, max payload is %db",
-                        &size, size_mult, &consumed, consumed_mult, &max,
-                        &payload)) {
-            fprintf(stderr, "WARNING: Parse error: %s", buffer);
-            continue;
-        }
+        EXPECT_EQ(6, sscanf(buffer,
+                            "%*s ring buffer is %d %3s (%d %3s consumed),"
+                            " max entry is %d B, max payload is %d B",
+                            &size, size_mult, &consumed, consumed_mult, &max, &payload))
+                << "Parse error on: " << buffer;
         full_size = size;
         switch (size_mult[0]) {
             case 'G':
                 full_size *= 1024;
-            /* FALLTHRU */
+                FALLTHROUGH_INTENDED;
             case 'M':
                 full_size *= 1024;
-            /* FALLTHRU */
+                FALLTHROUGH_INTENDED;
             case 'K':
                 full_size *= 1024;
-            /* FALLTHRU */
-            case 'b':
+                FALLTHROUGH_INTENDED;
+            case 'B':
                 break;
+            default:
+                ADD_FAILURE() << "Parse error on multiplier: " << size_mult;
         }
         full_consumed = consumed;
         switch (consumed_mult[0]) {
             case 'G':
                 full_consumed *= 1024;
-            /* FALLTHRU */
+                FALLTHROUGH_INTENDED;
             case 'M':
                 full_consumed *= 1024;
-            /* FALLTHRU */
+                FALLTHROUGH_INTENDED;
             case 'K':
                 full_consumed *= 1024;
-            /* FALLTHRU */
-            case 'b':
+                FALLTHROUGH_INTENDED;
+            case 'B':
                 break;
+            default:
+                ADD_FAILURE() << "Parse error on multiplier: " << consumed_mult;
         }
         EXPECT_GT((full_size * 9) / 4, full_consumed);
         EXPECT_GT(full_size, max);
@@ -631,7 +612,7 @@
         }
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     return count;
 }
@@ -815,7 +796,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 +842,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 +901,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 +950,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 +1033,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 +1063,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 +1101,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 +1175,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
@@ -1251,39 +1232,38 @@
         }
 
         int size, consumed, max, payload;
-        char size_mult[3], consumed_mult[3];
+        char size_mult[4], consumed_mult[4];
         size = consumed = max = payload = 0;
         if (6 == sscanf(buffer,
-                        "events: ring buffer is %d%2s (%d%2s consumed),"
-                        " max entry is %db, max payload is %db",
-                        &size, size_mult, &consumed, consumed_mult, &max,
-                        &payload)) {
+                        "events: ring buffer is %d %3s (%d %3s consumed),"
+                        " max entry is %d B, max payload is %d B",
+                        &size, size_mult, &consumed, consumed_mult, &max, &payload)) {
             long full_size = size, full_consumed = consumed;
 
             switch (size_mult[0]) {
                 case 'G':
                     full_size *= 1024;
-                /* FALLTHRU */
+                    FALLTHROUGH_INTENDED;
                 case 'M':
                     full_size *= 1024;
-                /* FALLTHRU */
+                    FALLTHROUGH_INTENDED;
                 case 'K':
                     full_size *= 1024;
-                /* FALLTHRU */
-                case 'b':
+                    FALLTHROUGH_INTENDED;
+                case 'B':
                     break;
             }
             switch (consumed_mult[0]) {
                 case 'G':
                     full_consumed *= 1024;
-                /* FALLTHRU */
+                    FALLTHROUGH_INTENDED;
                 case 'M':
                     full_consumed *= 1024;
-                /* FALLTHRU */
+                    FALLTHROUGH_INTENDED;
                 case 'K':
                     full_consumed *= 1024;
-                /* FALLTHRU */
-                case 'b':
+                    FALLTHROUGH_INTENDED;
+                case 'B':
                     break;
             }
             EXPECT_GT(full_size, full_consumed);
@@ -1329,10 +1309,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 +1337,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 +1364,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,11 +1402,10 @@
 
 TEST(logcat, regex) {
     FILE* fp;
-    logcat_define(ctx);
     int count = 0;
 
     char buffer[BIG_BUFFER];
-#define logcat_regex_prefix ___STRING(logcat) "_test"
+#define logcat_regex_prefix logcat_executable "_test"
 
     snprintf(buffer, sizeof(buffer),
              logcat_executable " --pid %d -d -e " logcat_regex_prefix "_a+b",
@@ -1450,7 +1422,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 +1434,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 +1459,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 +1469,7 @@
         count++;
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     ASSERT_EQ(3, count);
 }
@@ -1510,13 +1481,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 +1516,7 @@
         }
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     if ((count == 0) && (lastMatch.length() > 0)) {
         // Help us pinpoint where things went wrong ...
@@ -1586,7 +1551,7 @@
 
     {
         static const struct tag sync = { 2720, "sync" };
-        static const char id[] = ___STRING(logcat) ".descriptive-sync";
+        static const char id[] = logcat_executable ".descriptive-sync";
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << id << (int32_t)42 << (int32_t)-1 << (int32_t)0;
@@ -1701,7 +1666,7 @@
         // Invent new entries because existing can not serve
         EventTagMap* map = android_openEventTagMap(nullptr);
         ASSERT_TRUE(nullptr != map);
-        static const char name[] = ___STRING(logcat) ".descriptive-monotonic";
+        static const char name[] = logcat_executable ".descriptive-monotonic";
         int myTag = android_lookupEventTagNum(map, name, "(new|1|s)",
                                               ANDROID_LOG_UNKNOWN);
         android_closeEventTagMap(map);
@@ -1741,13 +1706,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 +1726,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();
 }
@@ -1785,3 +1748,13 @@
     EXPECT_EQ(logcatHelpTextSize * 2, logcatLastHelpTextSize);
 #endif
 }
+
+TEST(logcat, invalid_buffer) {
+  FILE* fp = popen("logcat -b foo 2>&1", "r");
+  ASSERT_NE(nullptr, fp);
+  std::string output;
+  ASSERT_TRUE(android::base::ReadFdToString(fileno(fp), &output));
+  pclose(fp);
+
+  ASSERT_TRUE(android::base::StartsWith(output, "unknown buffer foo\n"));
+}
diff --git a/logd/.clang-format b/logd/.clang-format
deleted file mode 100644
index 393c309..0000000
--- a/logd/.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/logd/.clang-format b/logd/.clang-format
new file mode 120000
index 0000000..1af4f51
--- /dev/null
+++ b/logd/.clang-format
@@ -0,0 +1 @@
+../.clang-format-4
\ No newline at end of file
diff --git a/logd/Android.bp b/logd/Android.bp
index 5c79976..b337b7c 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -39,7 +39,6 @@
         "FlushCommand.cpp",
         "LogBuffer.cpp",
         "LogBufferElement.cpp",
-        "LogBufferInterface.cpp",
         "LogTimes.cpp",
         "LogStatistics.cpp",
         "LogWhiteBlackList.cpp",
@@ -63,16 +62,44 @@
 
     srcs: ["main.cpp"],
 
-    static_libs: ["liblogd"],
+    static_libs: [
+        "liblog",
+        "liblogd",
+    ],
 
     shared_libs: [
         "libsysutils",
-        "liblog",
         "libcutils",
         "libbase",
         "libpackagelistparser",
+        "libprocessgroup",
         "libcap",
     ],
 
     cflags: ["-Werror"],
 }
+
+cc_binary {
+    name: "auditctl",
+
+    srcs: ["auditctl.cpp"],
+
+    static_libs: [
+        "liblogd",
+    ],
+
+    shared_libs: ["libbase"],
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wconversion"
+    ],
+}
+
+prebuilt_etc {
+    name: "logtagd.rc",
+    src: "logtagd.rc",
+    sub_dir: "init",
+}
diff --git a/logd/Android.mk b/logd/Android.mk
deleted file mode 100644
index 1bca891..0000000
--- a/logd/Android.mk
+++ /dev/null
@@ -1,13 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := logtagd.rc
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_TAGS := debug
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
-
-include $(BUILD_PREBUILT)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
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
old mode 100755
new mode 100644
index 70ecbe0..bd17555
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -36,13 +36,13 @@
 // 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();
     LastLogTimes::iterator it = times.begin();
     while (it != times.end()) {
-        entry = (*it);
+        entry = it->get();
         if (entry->mClient == client) {
             if (!entry->isWatchingMultiple(mLogMask)) {
                 LogTimeEntry::unlock();
@@ -63,31 +63,12 @@
                 }
             }
             entry->triggerReader_Locked();
-            if (entry->runningReader_Locked()) {
-                LogTimeEntry::unlock();
-                return;
-            }
-            entry->incRef_Locked();
-            break;
+            LogTimeEntry::unlock();
+            return;
         }
         it++;
     }
 
-    if (it == times.end()) {
-        // Create LogTimeEntry in notifyNewLog() ?
-        if (mTail == (unsigned long)-1) {
-            LogTimeEntry::unlock();
-            return;
-        }
-        entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask,
-                                 mPid, mStart, mTimeout);
-        times.push_front(entry);
-    }
-
-    client->incRef();
-
-    // release client and entry reference counts once done
-    entry->startReader_Locked();
     LogTimeEntry::unlock();
 }
 
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
old mode 100755
new mode 100644
index 543dfc3..ceaf393
--- a/logd/FlushCommand.h
+++ b/logd/FlushCommand.h
@@ -27,36 +27,11 @@
 
 class FlushCommand : public SocketClientCommand {
     LogReader& mReader;
-    bool mNonBlock;
-    unsigned long mTail;
     log_mask_t mLogMask;
-    pid_t mPid;
-    log_time mStart;
-    uint64_t mTimeout;
 
    public:
-    // 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) {
+        : mReader(reader), mLogMask(logMask) {
     }
 
     virtual void runSocketCommand(SocketClient* client);
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
old mode 100755
new mode 100644
index 269db2f..5a375ec
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -19,6 +19,7 @@
 #include <errno.h>
 #include <limits.h>
 #include <stdarg.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/prctl.h>
@@ -110,9 +111,9 @@
 }
 
 std::map<std::string, std::string> LogAudit::populateDenialMap() {
-    std::ifstream bug_file("/system/etc/selinux/selinux_denial_metadata");
+    std::ifstream bug_file("/vendor/etc/selinux/selinux_denial_metadata");
     std::string line;
-    // allocate a map for the static map pointer in logParse to keep track of,
+    // 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()) {
@@ -140,7 +141,8 @@
     return "";
 }
 
-void LogAudit::logParse(const std::string& string, std::string* bug_num) {
+void LogAudit::auditParse(const std::string& string, uid_t uid,
+                          std::string* bug_num) {
     if (!__android_log_is_debuggable()) {
         bug_num->assign("");
         return;
@@ -162,16 +164,26 @@
     } else {
         bug_num->assign("");
     }
+
+    // Ensure the uid name is not null before passing it to the bug string.
+    if (uid >= AID_APP_START && uid <= AID_APP_END) {
+        char* uidname = android::uidToName(uid);
+        if (uidname) {
+            bug_num->append(" app=");
+            bug_num->append(uidname);
+            free(uidname);
+        }
+    }
 }
 
 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);
@@ -190,78 +202,44 @@
     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 bug_metadata;
+    static std::string denial_metadata;
     if ((fdDmesg >= 0) && initialized) {
         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";
 
-        // Dedupe messages, checking for identical messages starting with avc:
-        static unsigned count;
-        static char* last_str;
-        static bool last_info;
+        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*>(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);
 
-        if (last_str != NULL) {
-            static const char avc[] = "): avc: ";
-            char* avcl = strstr(last_str, avc);
-            bool skip = false;
-
-            if (avcl) {
-                char* avcr = strstr(str, avc);
-
-                skip = avcr &&
-                       !fastcmp<strcmp>(avcl + strlen(avc), avcr + strlen(avc));
-                if (skip) {
-                    ++count;
-                    free(last_str);
-                    last_str = strdup(str);
-                    last_info = info;
-                }
-            }
-            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*>(bug_metadata.c_str());
-                iov[2].iov_len = bug_metadata.length();
-                if (count > 1) {
-                    iov[3].iov_base = const_cast<char*>(resume);
-                    iov[3].iov_len = strlen(resume);
-                } else {
-                    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;
-            }
-        }
-        if (last_str == NULL) {
-            count = 0;
-            last_str = strdup(str);
-            last_info = info;
-        }
-        if (count == 0) {
-            logParse(str, &bug_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*>(bug_metadata.c_str());
-            iov[2].iov_len = bug_metadata.length();
-            iov[3].iov_base = const_cast<char*>(newline);
-            iov[3].iov_len = strlen(newline);
-
-            writev(fdDmesg, iov, arraysize(iov));
-        }
+        writev(fdDmesg, iov, arraysize(iov));
     }
 
     if (!main && !events) {
@@ -269,10 +247,7 @@
         return 0;
     }
 
-    pid_t pid = getpid();
-    pid_t tid = gettid();
-    uid_t uid = AID_LOGD;
-    log_time now;
+    log_time now(log_time::EPOCH);
 
     static const char audit_str[] = " audit(";
     char* timeptr = strstr(str, audit_str);
@@ -296,29 +271,13 @@
         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 str_len = strnlen(str, LOGGER_ENTRY_MAX_PAYLOAD);
     if (((fdDmesg < 0) || !initialized) && !hasMetadata(str, str_len))
-        logParse(str, &bug_metadata);
-    str_len = (str_len + bug_metadata.length() <= LOGGER_ENTRY_MAX_PAYLOAD)
-                  ? str_len + bug_metadata.length()
+        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);
 
@@ -332,14 +291,13 @@
         event->header.tag = htole32(AUDITD_LOG_TAG);
         event->type = EVENT_TYPE_STRING;
         event->length = htole32(str_len);
-        memcpy(event->data, str, str_len - bug_metadata.length());
-        memcpy(event->data + str_len - bug_metadata.length(),
-               bug_metadata.c_str(), bug_metadata.length());
+        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),
-            (message_len <= USHRT_MAX) ? (unsigned short)message_len
-                                       : USHRT_MAX);
+            (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
         if (rc >= 0) {
             notify |= 1 << LOG_ID_EVENTS;
         }
@@ -351,7 +309,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;
@@ -380,7 +338,8 @@
         prefix_len = LOGGER_ENTRY_MAX_PAYLOAD;
     }
     size_t suffix_len = strnlen(ecomm, LOGGER_ENTRY_MAX_PAYLOAD - prefix_len);
-    message_len = str_len + prefix_len + suffix_len + bug_metadata.length() + 2;
+    message_len =
+        str_len + prefix_len + suffix_len + denial_metadata.length() + 2;
 
     if (main) {  // begin scope for main buffer
         char newstr[message_len];
@@ -390,11 +349,11 @@
         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,
-                bug_metadata.c_str(), bug_metadata.length());
+                denial_metadata.c_str(), denial_metadata.length());
 
-        rc = logbuf->log(LOG_ID_MAIN, now, uid, pid, tid, newstr,
-                         (message_len <= USHRT_MAX) ? (unsigned short)message_len
-                                                    : USHRT_MAX);
+        rc = logbuf->log(
+            LOG_ID_MAIN, now, uid, pid, tid, newstr,
+            (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
 
         if (rc >= 0) {
             notify |= 1 << LOG_ID_MAIN;
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index 5904966..c3d7a3e 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -48,7 +48,7 @@
     std::map<std::string, std::string> populateDenialMap();
     std::string denialParse(const std::string& denial, char terminator,
                             const std::string& search_term);
-    void logParse(const std::string& string, std::string* bug_num);
+    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 9b04363..9cbc7c4 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -105,10 +105,8 @@
 
     LastLogTimes::iterator times = mTimes.begin();
     while (times != mTimes.end()) {
-        LogTimeEntry* entry = (*times);
-        if (entry->owned_Locked()) {
-            entry->triggerReader_Locked();
-        }
+        LogTimeEntry* entry = times->get();
+        entry->triggerReader_Locked();
         times++;
     }
 
@@ -199,7 +197,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) {
+                   pid_t tid, const char* msg, uint16_t len) {
     if (log_id >= LOG_ID_MAX) {
         return -EINVAL;
     }
@@ -240,7 +238,7 @@
     LogBufferElement* currentLast = lastLoggedElements[log_id];
     if (currentLast) {
         LogBufferElement* dropped = droppedElements[log_id];
-        unsigned short count = dropped ? dropped->getDropped() : 0;
+        uint16_t count = dropped ? dropped->getDropped() : 0;
         //
         // State Init
         //     incoming:
@@ -401,7 +399,7 @@
                         ((*it)->getLogId() != LOG_ID_KERNEL))) {
         mLogElements.push_back(elem);
     } else {
-        log_time end = log_time::EPOCH;
+        log_time end(log_time::EPOCH);
         bool end_set = false;
         bool end_always = false;
 
@@ -409,17 +407,15 @@
 
         LastLogTimes::iterator times = mTimes.begin();
         while (times != mTimes.end()) {
-            LogTimeEntry* entry = (*times);
-            if (entry->owned_Locked()) {
-                if (!entry->mNonBlock) {
-                    end_always = true;
-                    break;
-                }
-                // it passing mEnd is blocked by the following checks.
-                if (!end_set || (end <= entry->mEnd)) {
-                    end = entry->mEnd;
-                    end_set = true;
-                }
+            LogTimeEntry* entry = times->get();
+            if (!entry->mNonBlock) {
+                end_always = true;
+                break;
+            }
+            // it passing mEnd is blocked by the following checks.
+            if (!end_set || (end <= entry->mEnd)) {
+                end = entry->mEnd;
+                end_set = true;
             }
             times++;
         }
@@ -584,13 +580,13 @@
     LogBufferElementMap map;
 
    public:
-    bool coalesce(LogBufferElement* element, unsigned short dropped) {
+    bool coalesce(LogBufferElement* element, uint16_t dropped) {
         LogBufferElementKey key(element->getUid(), element->getPid(),
                                 element->getTid());
         LogBufferElementMap::iterator it = map.find(key.getKey());
         if (it != map.end()) {
             LogBufferElement* found = it->second;
-            unsigned short moreDropped = found->getDropped();
+            uint16_t moreDropped = found->getDropped();
             if ((dropped + moreDropped) > USHRT_MAX) {
                 map.erase(it);
             } else {
@@ -710,8 +706,8 @@
     // Region locked?
     LastLogTimes::iterator times = mTimes.begin();
     while (times != mTimes.end()) {
-        LogTimeEntry* entry = (*times);
-        if (entry->owned_Locked() && entry->isWatching(id) &&
+        LogTimeEntry* entry = times->get();
+        if (entry->isWatching(id) &&
             (!oldest || (oldest->mStart > entry->mStart) ||
              ((oldest->mStart == entry->mStart) &&
               (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec)))) {
@@ -847,7 +843,7 @@
                 mLastSet[id] = true;
             }
 
-            unsigned short dropped = element->getDropped();
+            uint16_t dropped = element->getDropped();
 
             // remove any leading drops
             if (leading && dropped) {
@@ -927,7 +923,7 @@
 
             kick = true;
 
-            unsigned short len = element->getMsgLen();
+            uint16_t len = element->getMsgLen();
 
             // do not create any leading drops
             if (leading) {
@@ -1052,9 +1048,9 @@
                 LogTimeEntry::wrlock();
                 LastLogTimes::iterator times = mTimes.begin();
                 while (times != mTimes.end()) {
-                    LogTimeEntry* entry = (*times);
+                    LogTimeEntry* entry = times->get();
                     // Killer punch
-                    if (entry->owned_Locked() && entry->isWatching(id)) {
+                    if (entry->isWatching(id)) {
                         entry->release_Locked();
                     }
                     times++;
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 0942987..c2d5b97 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -27,7 +27,6 @@
 #include <sysutils/SocketClient.h>
 
 #include "LogBufferElement.h"
-#include "LogBufferInterface.h"
 #include "LogStatistics.h"
 #include "LogTags.h"
 #include "LogTimes.h"
@@ -75,7 +74,7 @@
 
 typedef std::list<LogBufferElement*> LogBufferElementCollection;
 
-class LogBuffer : public LogBufferInterface {
+class LogBuffer {
     LogBufferElementCollection mLogElements;
     pthread_rwlock_t mLogElementsLock;
 
@@ -108,14 +107,14 @@
     LastLogTimes& mTimes;
 
     explicit LogBuffer(LastLogTimes* times);
-    ~LogBuffer() override;
+    ~LogBuffer();
     void init();
     bool isMonotonic() {
         return monotonic;
     }
 
-    int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
-            const char* msg, unsigned short len) override;
+    int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, const char* msg,
+            uint16_t len);
     // lastTid is an optional context to help detect if the last previous
     // valid message was from the same source so we can differentiate chatty
     // filter types (identical or expired)
@@ -159,12 +158,7 @@
     const char* pidToName(pid_t pid) {
         return stats.pidToName(pid);
     }
-    virtual uid_t pidToUid(pid_t pid) override {
-        return stats.pidToUid(pid);
-    }
-    virtual pid_t tidToPid(pid_t tid) override {
-        return stats.tidToPid(tid);
-    }
+    uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
     const char* uidToName(uid_t uid) {
         return stats.uidToName(uid);
     }
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index f20ac45..2fd9f95 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -35,7 +35,7 @@
 
 LogBufferElement::LogBufferElement(log_id_t log_id, log_time realtime,
                                    uid_t uid, pid_t pid, pid_t tid,
-                                   const char* msg, unsigned short len)
+                                   const char* msg, uint16_t len)
     : mUid(uid),
       mPid(pid),
       mTid(tid),
@@ -55,8 +55,19 @@
       mMsgLen(elem.mMsgLen),
       mLogId(elem.mLogId),
       mDropped(elem.mDropped) {
-    mMsg = new char[mMsgLen];
-    memcpy(mMsg, elem.mMsg, mMsgLen);
+    if (mDropped) {
+        if (elem.isBinary() && elem.mMsg != nullptr) {
+            // for the following "len" value, refer to : setDropped(uint16_t value), getTag()
+            const int len = sizeof(android_event_header_t);
+            mMsg = new char[len];
+            memcpy(mMsg, elem.mMsg, len);
+        } else {
+            mMsg = nullptr;
+        }
+    } else {
+        mMsg = new char[mMsgLen];
+        memcpy(mMsg, elem.mMsg, mMsgLen);
+    }
 }
 
 LogBufferElement::~LogBufferElement() {
@@ -71,7 +82,7 @@
                : 0;
 }
 
-unsigned short LogBufferElement::setDropped(unsigned short value) {
+uint16_t LogBufferElement::setDropped(uint16_t 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) {
@@ -91,7 +102,7 @@
 
 // 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);
@@ -114,7 +125,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
@@ -162,15 +173,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));
@@ -178,7 +189,7 @@
         }
     }
     if (commName) {
-        char* buf = NULL;
+        char* buf = nullptr;
         asprintf(&buf, " %s", commName);
         if (buf) {
             free(const_cast<char*>(commName));
@@ -187,7 +198,7 @@
     }
     // 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 : "",
+    size_t len = snprintf(nullptr, 0, format_uid, mUid, name ? name : "",
                           commName ? commName : "", type, getDropped(),
                           (getDropped() > 1) ? "s" : "");
 
@@ -247,7 +258,7 @@
     iovec[0].iov_base = &entry;
     iovec[0].iov_len = entry.hdr_size;
 
-    char* buffer = NULL;
+    char* buffer = nullptr;
 
     if (mDropped) {
         entry.len = populateDroppedMessage(buffer, parent, lastSame);
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index b168645..57b0a95 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -18,6 +18,7 @@
 #define _LOGD_LOG_BUFFER_ELEMENT_H__
 
 #include <stdatomic.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <sys/types.h>
 
@@ -56,7 +57,7 @@
 
    public:
     LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
-                     pid_t tid, const char* msg, unsigned short len);
+                     pid_t tid, const char* msg, uint16_t len);
     LogBufferElement(const LogBufferElement& elem);
     ~LogBufferElement();
 
@@ -77,11 +78,11 @@
         return mTid;
     }
     uint32_t getTag() const;
-    unsigned short getDropped(void) const {
+    uint16_t getDropped(void) const {
         return mDropped ? mDroppedCount : 0;
     }
-    unsigned short setDropped(unsigned short value);
-    unsigned short getMsgLen() const {
+    uint16_t setDropped(uint16_t value);
+    uint16_t getMsgLen() const {
         return mDropped ? 0 : mMsgLen;
     }
     const char* getMsg() const {
diff --git a/logd/LogBufferInterface.cpp b/logd/LogBufferInterface.cpp
deleted file mode 100644
index 4b6d363..0000000
--- a/logd/LogBufferInterface.cpp
+++ /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.
- */
-
-#include "LogBufferInterface.h"
-#include "LogUtils.h"
-
-LogBufferInterface::LogBufferInterface() {
-}
-LogBufferInterface::~LogBufferInterface() {
-}
-uid_t LogBufferInterface::pidToUid(pid_t pid) {
-    return android::pidToUid(pid);
-}
-pid_t LogBufferInterface::tidToPid(pid_t tid) {
-    return android::tidToPid(tid);
-}
diff --git a/logd/LogBufferInterface.h b/logd/LogBufferInterface.h
deleted file mode 100644
index ff73a22..0000000
--- a/logd/LogBufferInterface.h
+++ /dev/null
@@ -1,43 +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.
- */
-
-#ifndef _LOGD_LOG_BUFFER_INTERFACE_H__
-#define _LOGD_LOG_BUFFER_INTERFACE_H__
-
-#include <sys/types.h>
-
-#include <android-base/macros.h>
-#include <log/log_id.h>
-#include <log/log_time.h>
-
-// Abstract interface that handles log when log available.
-class LogBufferInterface {
-   public:
-    LogBufferInterface();
-    virtual ~LogBufferInterface();
-    // Handles a log entry when available in LogListener.
-    // Returns the size of the handled log message.
-    virtual int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
-                    pid_t tid, const char* msg, unsigned short len) = 0;
-
-    virtual uid_t pidToUid(pid_t pid);
-    virtual pid_t tidToPid(pid_t tid);
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(LogBufferInterface);
-};
-
-#endif  // _LOGD_LOG_BUFFER_INTERFACE_H__
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
old mode 100755
new mode 100644
index ab980ac..edd326a
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -197,10 +197,9 @@
     // NOTREACHED
 }
 
-log_time LogKlog::correction =
-    (log_time(CLOCK_REALTIME) < log_time(CLOCK_MONOTONIC))
-        ? log_time::EPOCH
-        : (log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC));
+log_time LogKlog::correction = (log_time(CLOCK_REALTIME) < log_time(CLOCK_MONOTONIC))
+                                       ? log_time(log_time::EPOCH)
+                                       : (log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC));
 
 LogKlog::LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead,
                  bool auditd)
@@ -268,7 +267,7 @@
     static const char real_format[] = "%Y-%m-%d %H:%M:%S.%09q UTC";
     if (len < (ssize_t)(strlen(real_format) + 5)) return;
 
-    log_time real;
+    log_time real(log_time::EPOCH);
     const char* ep = real.strptime(real_string, real_format);
     if (!ep || (ep > &real_string[len]) || (real > log_time(CLOCK_REALTIME))) {
         return;
@@ -282,20 +281,20 @@
     tm.tm_isdst = -1;
     localtime_r(&now, &tm);
     if ((tm.tm_gmtoff < 0) && ((-tm.tm_gmtoff) > (long)real.tv_sec)) {
-        real = log_time::EPOCH;
+        real = log_time(log_time::EPOCH);
     } else {
         real.tv_sec += tm.tm_gmtoff;
     }
     if (monotonic > real) {
-        correction = log_time::EPOCH;
+        correction = log_time(log_time::EPOCH);
     } else {
         correction = real - monotonic;
     }
 }
 
-void LogKlog::sniffTime(log_time& now, const char*& buf, ssize_t len,
-                        bool reverse) {
-    if (len <= 0) return;
+log_time LogKlog::sniffTime(const char*& buf, ssize_t len, bool reverse) {
+    log_time now(log_time::EPOCH);
+    if (len <= 0) return now;
 
     const char* cp = nullptr;
     if ((len > 10) && (*buf == '[')) {
@@ -310,7 +309,7 @@
         }
         buf = cp;
 
-        if (isMonotonic()) return;
+        if (isMonotonic()) return now;
 
         const char* b;
         if (((b = android::strnstr(cp, len, suspendStr))) &&
@@ -329,11 +328,11 @@
             //     trigger a check for ntp-induced or hardware clock drift.
             log_time real(CLOCK_REALTIME);
             log_time mono(CLOCK_MONOTONIC);
-            correction = (real < mono) ? log_time::EPOCH : (real - mono);
+            correction = (real < mono) ? log_time(log_time::EPOCH) : (real - mono);
         } else if (((b = android::strnstr(cp, len, suspendedStr))) &&
                    (((b += strlen(suspendStr)) - cp) < len)) {
             len -= b - cp;
-            log_time real;
+            log_time real(log_time::EPOCH);
             char* endp;
             real.tv_sec = strtol(b, &endp, 10);
             if ((*endp == '.') && ((endp - b) < len)) {
@@ -345,7 +344,7 @@
                 }
                 if (reverse) {
                     if (real > correction) {
-                        correction = log_time::EPOCH;
+                        correction = log_time(log_time::EPOCH);
                     } else {
                         correction -= real;
                     }
@@ -363,6 +362,7 @@
             now = log_time(CLOCK_REALTIME);
         }
     }
+    return now;
 }
 
 pid_t LogKlog::sniffPid(const char*& buf, ssize_t len) {
@@ -451,8 +451,7 @@
     }
     parseKernelPrio(cp, len - (cp - buf));
 
-    log_time now;
-    sniffTime(now, cp, len - (cp - buf), true);
+    log_time now = sniffTime(cp, len - (cp - buf), true);
 
     const char* suspended = android::strnstr(buf, len, suspendedStr);
     if (!suspended || (suspended > cp)) {
@@ -468,16 +467,14 @@
     }
     parseKernelPrio(cp, len - (cp - buf));
 
-    sniffTime(now, cp, len - (cp - buf), true);
+    sniffTime(cp, len - (cp - buf), true);
 }
 
 // Convert kernel log priority number into an Android Logger priority number
 static int convertKernelPrioToAndroidPrio(int pri) {
     switch (pri & LOG_PRIMASK) {
         case LOG_EMERG:
-        // FALLTHRU
         case LOG_ALERT:
-        // FALLTHRU
         case LOG_CRIT:
             return ANDROID_LOG_FATAL;
 
@@ -488,9 +485,7 @@
             return ANDROID_LOG_WARN;
 
         default:
-        // FALLTHRU
         case LOG_NOTICE:
-        // FALLTHRU
         case LOG_INFO:
             break;
 
@@ -552,8 +547,7 @@
     const char* p = buf;
     int pri = parseKernelPrio(p, len);
 
-    log_time now;
-    sniffTime(now, p, len - (p - buf), false);
+    log_time now = sniffTime(p, len - (p - buf), false);
 
     // sniff for start marker
     const char* start = android::strnstr(p, len - (p - buf), klogdStr);
@@ -821,8 +815,7 @@
     }
 
     // Log message
-    int rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr,
-                         (unsigned short)n);
+    int rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr, (uint16_t)n);
 
     // notify readers
     if (rc > 0) {
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index bb92dd2..6bfd6a8 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -55,11 +55,10 @@
     }
 
    protected:
-    void sniffTime(log_time& now, const char*& buf, ssize_t len, bool reverse);
-    pid_t sniffPid(const char*& buf, ssize_t len);
-    void calculateCorrection(const log_time& monotonic, const char* real_string,
-                             ssize_t len);
-    virtual bool onDataAvailable(SocketClient* cli);
+     log_time sniffTime(const char*& buf, ssize_t len, bool reverse);
+     pid_t sniffPid(const char*& buf, ssize_t len);
+     void calculateCorrection(const log_time& monotonic, const char* real_string, ssize_t len);
+     virtual bool onDataAvailable(SocketClient* cli);
 };
 
 #endif
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
old mode 100755
new mode 100644
index fc51dcf..443570f
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#include <ctype.h>
 #include <limits.h>
-#include <stdio.h>
 #include <sys/cdefs.h>
 #include <sys/prctl.h>
 #include <sys/socket.h>
@@ -32,9 +30,8 @@
 #include "LogListener.h"
 #include "LogUtils.h"
 
-LogListener::LogListener(LogBufferInterface* buf, LogReader* reader)
-    : SocketListener(getLogSocket(), false), logbuf(buf), reader(reader) {
-}
+LogListener::LogListener(LogBuffer* buf, LogReader* reader)
+    : SocketListener(getLogSocket(), false), logbuf(buf), reader(reader) {}
 
 bool LogListener::onDataAvailable(SocketClient* cli) {
     static bool name_set;
@@ -50,7 +47,7 @@
 
     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();
@@ -66,10 +63,10 @@
 
     buffer[n] = 0;
 
-    struct ucred* cred = NULL;
+    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);
@@ -78,11 +75,8 @@
         cmsg = CMSG_NXTHDR(&hdr, cmsg);
     }
 
-    struct ucred fake_cred;
-    if (cred == NULL) {
-        cred = &fake_cred;
-        cred->pid = 0;
-        cred->uid = DEFAULT_OVERFLOWUID;
+    if (cred == nullptr) {
+        return false;
     }
 
     if (cred->uid == AID_LOGD) {
@@ -106,40 +100,16 @@
         return false;
     }
 
-    // Check credential validity, acquire corrected details if not supplied.
-    if (cred->pid == 0) {
-        cred->pid = logbuf ? logbuf->tidToPid(header->tid)
-                           : android::tidToPid(header->tid);
-        if (cred->pid == getpid()) {
-            // We expect that /proc/<tid>/ is accessible to self even without
-            // readproc group, so that we will always drop messages that come
-            // from any of our logd threads and their library calls.
-            return false;  // ignore self
-        }
-    }
-    if (cred->uid == DEFAULT_OVERFLOWUID) {
-        uid_t uid =
-            logbuf ? logbuf->pidToUid(cred->pid) : android::pidToUid(cred->pid);
-        if (uid == AID_LOGD) {
-            uid = logbuf ? logbuf->pidToUid(header->tid)
-                         : android::pidToUid(cred->pid);
-        }
-        if (uid != AID_LOGD) cred->uid = uid;
-    }
-
     char* msg = ((char*)buffer) + sizeof(android_log_header_t);
     n -= sizeof(android_log_header_t);
 
     // NB: hdr.msg_flags & MSG_TRUNC is not tested, silently passing a
     // truncated message to the logs.
 
-    if (logbuf != nullptr) {
-        int res = logbuf->log(
-            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(static_cast<log_mask_t>(1 << logId));
-        }
+    int res = logbuf->log(logId, header->realtime, cred->uid, cred->pid, header->tid, msg,
+                          ((size_t)n <= UINT16_MAX) ? (uint16_t)n : UINT16_MAX);
+    if (res > 0) {
+        reader->notifyNewLog(static_cast<log_mask_t>(1 << logId));
     }
 
     return true;
diff --git a/logd/LogListener.h b/logd/LogListener.h
index a562a54..8fe3da4 100644
--- a/logd/LogListener.h
+++ b/logd/LogListener.h
@@ -20,22 +20,12 @@
 #include <sysutils/SocketListener.h>
 #include "LogReader.h"
 
-// DEFAULT_OVERFLOWUID is defined in linux/highuid.h, which is not part of
-// the uapi headers for userspace to use.  This value is filled in on the
-// out-of-band socket credentials if the OS fails to find one available.
-// One of the causes of this is if SO_PASSCRED is set, all the packets before
-// that point will have this value.  We also use it in a fake credential if
-// no socket credentials are supplied.
-#ifndef DEFAULT_OVERFLOWUID
-#define DEFAULT_OVERFLOWUID 65534
-#endif
-
 class LogListener : public SocketListener {
-    LogBufferInterface* logbuf;
+    LogBuffer* logbuf;
     LogReader* reader;
 
    public:
-    LogListener(LogBufferInterface* buf, LogReader* reader /* nullable */);
+     LogListener(LogBuffer* buf, LogReader* reader);
 
    protected:
     virtual bool onDataAvailable(SocketClient* cli);
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
old mode 100755
new mode 100644
index 2b6556d..9db8c00
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -41,6 +41,7 @@
     runOnEachSocket(&command);
 }
 
+// Note returning false will release the SocketClient instance.
 bool LogReader::onDataAvailable(SocketClient* cli) {
     static bool name_set;
     if (!name_set) {
@@ -57,6 +58,18 @@
     }
     buffer[len] = '\0';
 
+    // Clients are only allowed to send one command, disconnect them if they
+    // send another.
+    LogTimeEntry::wrlock();
+    for (const auto& entry : mLogbuf.mTimes) {
+        if (entry->mClient == cli) {
+            entry->release_Locked();
+            LogTimeEntry::unlock();
+            return false;
+        }
+    }
+    LogTimeEntry::unlock();
+
     unsigned long tail = 0;
     static const char _tail[] = " tail=";
     char* cp = strstr(buffer, _tail);
@@ -199,14 +212,29 @@
         cli->getUid(), cli->getGid(), cli->getPid(), nonBlock ? 'n' : 'b', tail,
         logMask, (int)pid, sequence.nsec(), timeout);
 
-    FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence, timeout);
+    if (sequence == log_time::EPOCH) {
+        timeout = 0;
+    }
+
+    LogTimeEntry::wrlock();
+    auto entry = std::make_unique<LogTimeEntry>(
+        *this, cli, nonBlock, tail, logMask, pid, sequence, timeout);
+    if (!entry->startReader_Locked()) {
+        LogTimeEntry::unlock();
+        return false;
+    }
+
+    // release client and entry reference counts once done
+    cli->incRef();
+    mLogbuf.mTimes.emplace_front(std::move(entry));
 
     // Set acceptable upper limit to wait for slow reader processing b/27242723
     struct timeval t = { LOGD_SNDTIMEO, 0 };
     setsockopt(cli->getSocket(), SOL_SOCKET, SO_SNDTIMEO, (const char*)&t,
                sizeof(t));
 
-    command.runSocketCommand(cli);
+    LogTimeEntry::unlock();
+
     return true;
 }
 
@@ -215,9 +243,8 @@
     LogTimeEntry::wrlock();
     LastLogTimes::iterator it = times.begin();
     while (it != times.end()) {
-        LogTimeEntry* entry = (*it);
+        LogTimeEntry* entry = it->get();
         if (entry->mClient == cli) {
-            times.erase(it);
             entry->release_Locked();
             break;
         }
diff --git a/logd/LogReader.h b/logd/LogReader.h
old mode 100755
new mode 100644
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index af59ddc..431b778 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 {
@@ -83,7 +83,7 @@
     if (element->getDropped()) return;
 
     log_id_t log_id = element->getLogId();
-    unsigned short size = element->getMsgLen();
+    uint16_t size = element->getMsgLen();
     mSizesTotal[log_id] += size;
     SizesTotal += size;
     ++mElementsTotal[log_id];
@@ -91,7 +91,7 @@
 
 void LogStatistics::add(LogBufferElement* element) {
     log_id_t log_id = element->getLogId();
-    unsigned short size = element->getMsgLen();
+    uint16_t size = element->getMsgLen();
     mSizes[log_id] += size;
     ++mElements[log_id];
 
@@ -161,7 +161,7 @@
 
 void LogStatistics::subtract(LogBufferElement* element) {
     log_id_t log_id = element->getLogId();
-    unsigned short size = element->getMsgLen();
+    uint16_t size = element->getMsgLen();
     mSizes[log_id] -= size;
     --mElements[log_id];
     if (element->getDropped()) {
@@ -206,7 +206,7 @@
 // entry->setDropped(1) must follow this call, caller should do this explicitly.
 void LogStatistics::drop(LogBufferElement* element) {
     log_id_t log_id = element->getLogId();
-    unsigned short size = element->getMsgLen();
+    uint16_t size = element->getMsgLen();
     mSizes[log_id] -= size;
     ++mDroppedElements[log_id];
 
@@ -286,7 +286,7 @@
                     name = strdup(nameTmp);
                 } else if (fastcmp<strcmp>(name, nameTmp)) {
                     free(const_cast<char*>(name));
-                    name = NULL;
+                    name = nullptr;
                     break;
                 }
             }
@@ -613,13 +613,13 @@
 
 std::string LogStatistics::format(uid_t uid, pid_t pid,
                                   unsigned int logMask) const {
-    static const unsigned short spaces_total = 19;
+    static const uint16_t spaces_total = 19;
 
     // Report on total logging, current and for all time
 
     std::string output = "size/num";
     size_t oldLength;
-    short spaces = 1;
+    int16_t spaces = 1;
 
     log_id_for_each(id) {
         if (!(logMask & (1 << id))) continue;
@@ -837,42 +837,19 @@
     }
     return AID_LOGD;  // associate this with the logger
 }
-
-pid_t tidToPid(pid_t tid) {
-    char buffer[512];
-    snprintf(buffer, sizeof(buffer), "/proc/%u/status", tid);
-    FILE* fp = fopen(buffer, "r");
-    if (fp) {
-        while (fgets(buffer, sizeof(buffer), fp)) {
-            int pid = tid;
-            char space = 0;
-            if ((sscanf(buffer, "Tgid: %d%c", &pid, &space) == 2) &&
-                isspace(space)) {
-                fclose(fp);
-                return pid;
-            }
-        }
-        fclose(fp);
-    }
-    return tid;
-}
 }
 
 uid_t LogStatistics::pidToUid(pid_t pid) {
     return pidTable.add(pid)->second.getUid();
 }
 
-pid_t LogStatistics::tidToPid(pid_t tid) {
-    return tidTable.add(tid)->second.getPid();
-}
-
 // caller must free character string
 const char* LogStatistics::pidToName(pid_t pid) const {
     // An inconvenient truth ... getName() can alter the object
     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 ac3cf9a..0782de3 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -306,6 +306,10 @@
     std::string format(const LogStatistics& stat, log_id_t id) const;
 };
 
+namespace android {
+uid_t pidToUid(pid_t pid);
+}
+
 struct PidEntry : public EntryBaseDropped {
     const pid_t pid;
     uid_t uid;
@@ -385,13 +389,6 @@
           uid(android::pidToUid(tid)),
           name(android::tidToName(tid)) {
     }
-    TidEntry(pid_t tid)
-        : EntryBaseDropped(),
-          tid(tid),
-          pid(android::tidToPid(tid)),
-          uid(android::pidToUid(tid)),
-          name(android::tidToName(tid)) {
-    }
     explicit TidEntry(const LogBufferElement* element)
         : EntryBaseDropped(element),
           tid(element->getTid()),
@@ -520,7 +517,7 @@
             return;
         }
         ++msg;
-        unsigned short len = element->getMsgLen();
+        uint16_t len = element->getMsgLen();
         len = (len <= 1) ? 0 : strnlen(msg, len - 1);
         if (!len) {
             name = std::string_view("<NULL>", strlen("<NULL>"));
@@ -531,7 +528,7 @@
         name = std::string_view(alloc->c_str(), alloc->size());
     }
 
-    explicit TagNameKey(TagNameKey&& rval)
+    explicit TagNameKey(TagNameKey&& rval) noexcept
         : alloc(rval.alloc), name(rval.name.data(), rval.name.length()) {
         rval.alloc = nullptr;
     }
@@ -787,7 +784,6 @@
     // helper (must be locked directly or implicitly by mLogElementsLock)
     const char* pidToName(pid_t pid) const;
     uid_t pidToUid(pid_t pid);
-    pid_t tidToPid(pid_t tid);
     const char* uidToName(uid_t uid) const;
 };
 
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index ff7e762..f19e7b0 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -30,6 +30,7 @@
 
 #include <android-base/file.h>
 #include <android-base/macros.h>
+#include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 #include <log/log_event_list.h>
 #include <log/log_properties.h>
@@ -38,6 +39,8 @@
 #include "LogTags.h"
 #include "LogUtils.h"
 
+using android::base::make_scope_guard;
+
 static LogTags* logtags;
 
 const char LogTags::system_event_log_tags[] = "/system/etc/event-log-tags";
@@ -91,7 +94,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];
@@ -208,7 +211,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 +238,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 +248,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 +266,7 @@
                             android::prdebug("tag name invalid %.*s",
                                              (int)(cp - name + 1), name);
                         }
-                        lineStart = NULL;
+                        lineStart = nullptr;
                     }
                 } else if (!isspace(*cp)) {
                     break;
@@ -316,27 +319,29 @@
         std::string Format;
         android_log_list_element elem;
         {
-            android_log_event_list ctx(log_msg);
-            elem = ctx.read();
+            auto ctx = create_android_log_parser(log_msg.msg() + sizeof(uint32_t),
+                                                 log_msg.entry.len - sizeof(uint32_t));
+            auto guard = make_scope_guard([&ctx]() { android_log_destroy(&ctx); });
+            elem = android_log_read_next(ctx);
             if (elem.type != EVENT_TYPE_LIST) {
                 continue;
             }
-            elem = ctx.read();
+            elem = android_log_read_next(ctx);
             if (elem.type != EVENT_TYPE_INT) {
                 continue;
             }
             Tag = elem.data.int32;
-            elem = ctx.read();
+            elem = android_log_read_next(ctx);
             if (elem.type != EVENT_TYPE_STRING) {
                 continue;
             }
             Name = std::string(elem.data.string, elem.len);
-            elem = ctx.read();
+            elem = android_log_read_next(ctx);
             if (elem.type != EVENT_TYPE_STRING) {
                 continue;
             }
             Format = std::string(elem.data.string, elem.len);
-            elem = ctx.read();
+            elem = android_log_read_next(ctx);
         }
         if ((elem.type != EVENT_TYPE_LIST_STOP) || !elem.complete) continue;
 
@@ -364,7 +369,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 +388,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 +417,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 +446,7 @@
                                    bool& unique) {
     key2tag_const_iterator ik;
 
-    bool write = format != NULL;
+    bool write = format != nullptr;
     unique = write;
 
     if (!write) {
@@ -524,10 +529,22 @@
     tag2format_const_iterator iform = tag2format.find(tag);
     std::string Format = (iform != tag2format.end()) ? iform->second : "";
 
-    __android_log_event_list ctx(TAG_DEF_LOG_TAG);
-    ctx << tag << Name << Format;
-    std::string buffer(ctx);
-    if (buffer.length() <= 0) return;  // unlikely
+    auto ctx = create_android_logger(TAG_DEF_LOG_TAG);
+    auto guard = make_scope_guard([&ctx]() { android_log_destroy(&ctx); });
+    if (android_log_write_int32(ctx, static_cast<int32_t>(tag) < 0) ||
+        android_log_write_string8_len(ctx, Name.c_str(), Name.size()) < 0 ||
+        android_log_write_string8_len(ctx, Format.c_str(), Format.size()) < 0) {
+        return;
+    }
+
+    const char* cp = nullptr;
+    ssize_t len = android_log_write_list_buffer(ctx, &cp);
+
+    if (len <= 0 || cp == nullptr) {
+        return;
+    }
+
+    std::string buffer(cp, len);
 
     /*
      *  struct {
@@ -679,7 +696,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 +865,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
old mode 100755
new mode 100644
index 7a6f84b..208d67f
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -30,11 +30,7 @@
 LogTimeEntry::LogTimeEntry(LogReader& reader, SocketClient* client,
                            bool nonBlock, unsigned long tail, log_mask_t logMask,
                            pid_t pid, log_time start, uint64_t timeout)
-    : mRefCount(1),
-      mRelease(false),
-      mError(false),
-      threadRunning(false),
-      leadingDropped(false),
+    : leadingDropped(false),
       mReader(reader),
       mLogMask(logMask),
       mPid(pid),
@@ -52,65 +48,21 @@
     cleanSkip_Locked();
 }
 
-void LogTimeEntry::startReader_Locked(void) {
+bool LogTimeEntry::startReader_Locked() {
     pthread_attr_t attr;
 
-    threadRunning = true;
-
     if (!pthread_attr_init(&attr)) {
         if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
             if (!pthread_create(&mThread, &attr, LogTimeEntry::threadStart,
                                 this)) {
                 pthread_attr_destroy(&attr);
-                return;
+                return true;
             }
         }
         pthread_attr_destroy(&attr);
     }
-    threadRunning = false;
-    if (mClient) {
-        mClient->decRef();
-    }
-    decRef_Locked();
-}
 
-void LogTimeEntry::threadStop(void* obj) {
-    LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
-
-    wrlock();
-
-    if (me->mNonBlock) {
-        me->error_Locked();
-    }
-
-    SocketClient* client = me->mClient;
-
-    if (me->isError_Locked()) {
-        LogReader& reader = me->mReader;
-        LastLogTimes& times = reader.logbuf().mTimes;
-
-        LastLogTimes::iterator it = times.begin();
-        while (it != times.end()) {
-            if (*it == me) {
-                times.erase(it);
-                me->release_nodelete_Locked();
-                break;
-            }
-            it++;
-        }
-
-        me->mClient = nullptr;
-        reader.release(client);
-    }
-
-    if (client) {
-        client->decRef();
-    }
-
-    me->threadRunning = false;
-    me->decRef_Locked();
-
-    unlock();
+    return false;
 }
 
 void* LogTimeEntry::threadStart(void* obj) {
@@ -118,13 +70,7 @@
 
     LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
 
-    pthread_cleanup_push(threadStop, obj);
-
     SocketClient* client = me->mClient;
-    if (!client) {
-        me->error();
-        return nullptr;
-    }
 
     LogBuffer& logbuf = me->mReader.logbuf();
 
@@ -137,14 +83,14 @@
 
     log_time start = me->mStart;
 
-    while (me->threadRunning && !me->isError_Locked()) {
+    while (!me->mRelease) {
         if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
             if (pthread_cond_timedwait(&me->threadTriggeredCondition,
                                        &timesLock, &me->mTimeout) == ETIMEDOUT) {
                 me->mTimeout.tv_sec = 0;
                 me->mTimeout.tv_nsec = 0;
             }
-            if (!me->threadRunning || me->isError_Locked()) {
+            if (me->mRelease) {
                 break;
             }
         }
@@ -162,13 +108,12 @@
         wrlock();
 
         if (start == LogBufferElement::FLUSH_ERROR) {
-            me->error_Locked();
             break;
         }
 
         me->mStart = start + log_time(0, 1);
 
-        if (me->mNonBlock || !me->threadRunning || me->isError_Locked()) {
+        if (me->mNonBlock || me->mRelease) {
             break;
         }
 
@@ -179,9 +124,21 @@
         }
     }
 
-    unlock();
+    LogReader& reader = me->mReader;
+    reader.release(client);
 
-    pthread_cleanup_pop(true);
+    client->decRef();
+
+    LastLogTimes& times = reader.logbuf().mTimes;
+    auto it =
+        std::find_if(times.begin(), times.end(),
+                     [&me](const auto& other) { return other.get() == me; });
+
+    if (it != times.end()) {
+        times.erase(it);
+    }
+
+    unlock();
 
     return nullptr;
 }
@@ -247,7 +204,7 @@
         goto skip;
     }
 
-    if (me->isError_Locked()) {
+    if (me->mRelease) {
         goto stop;
     }
 
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
old mode 100755
new mode 100644
index 76d016c..9f6f203
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -18,10 +18,12 @@
 #define _LOGD_LOG_TIMES_H__
 
 #include <pthread.h>
+#include <sys/socket.h>
 #include <sys/types.h>
 #include <time.h>
 
 #include <list>
+#include <memory>
 
 #include <log/log.h>
 #include <sysutils/SocketClient.h>
@@ -33,16 +35,12 @@
 
 class LogTimeEntry {
     static pthread_mutex_t timesLock;
-    unsigned int mRefCount;
-    bool mRelease;
-    bool mError;
-    bool threadRunning;
+    bool mRelease = false;
     bool leadingDropped;
     pthread_cond_t threadTriggeredCondition;
     pthread_t mThread;
     LogReader& mReader;
     static void* threadStart(void* me);
-    static void threadStop(void* me);
     const log_mask_t mLogMask;
     const pid_t mPid;
     unsigned int skipAhead[LOG_ID_MAX];
@@ -73,11 +71,8 @@
         pthread_mutex_unlock(&timesLock);
     }
 
-    void startReader_Locked(void);
+    bool startReader_Locked();
 
-    bool runningReader_Locked(void) const {
-        return threadRunning || mRelease || mError || mNonBlock;
-    }
     void triggerReader_Locked(void) {
         pthread_cond_signal(&threadTriggeredCondition);
     }
@@ -87,54 +82,13 @@
     }
     void cleanSkip_Locked(void);
 
-    // These called after LogTimeEntry removed from list, lock implicitly held
-    void release_nodelete_Locked(void) {
-        mRelease = true;
-        pthread_cond_signal(&threadTriggeredCondition);
-        // assumes caller code path will call decRef_Locked()
-    }
-
     void release_Locked(void) {
+        // gracefully shut down the socket.
+        shutdown(mClient->getSocket(), SHUT_RDWR);
         mRelease = true;
         pthread_cond_signal(&threadTriggeredCondition);
-        if (mRefCount || threadRunning) {
-            return;
-        }
-        // No one else is holding a reference to this
-        delete this;
     }
 
-    // Called to mark socket in jeopardy
-    void error_Locked(void) {
-        mError = true;
-    }
-    void error(void) {
-        wrlock();
-        error_Locked();
-        unlock();
-    }
-
-    bool isError_Locked(void) const {
-        return mRelease || mError;
-    }
-
-    // Mark Used
-    //  Locking implied, grabbed when protection around loop iteration
-    void incRef_Locked(void) {
-        ++mRefCount;
-    }
-
-    bool owned_Locked(void) const {
-        return mRefCount != 0;
-    }
-
-    void decRef_Locked(void) {
-        if ((mRefCount && --mRefCount) || !mRelease || threadRunning) {
-            return;
-        }
-        // No one else is holding a reference to this
-        delete this;
-    }
     bool isWatching(log_id_t id) const {
         return mLogMask & (1 << id);
     }
@@ -146,6 +100,6 @@
     static int FilterSecondPass(const LogBufferElement* element, void* me);
 };
 
-typedef std::list<LogTimeEntry*> LastLogTimes;
+typedef std::list<std::unique_ptr<LogTimeEntry>> LastLogTimes;
 
 #endif  // _LOGD_LOG_TIMES_H__
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index 4dcd3e7..fa9f398 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -38,8 +38,6 @@
 // Caller must own and free returned value
 char* pidToName(pid_t pid);
 char* tidToName(pid_t tid);
-uid_t pidToUid(pid_t pid);
-pid_t tidToPid(pid_t tid);
 
 // Furnished in LogTags.cpp. Thread safe.
 const char* tagToName(uint32_t tag);
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/README.property b/logd/README.property
index da5f96f..d2a2cbb 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -17,10 +17,13 @@
 					 Responds to logcatd, clear and stop.
 logd.logpersistd.buffer          persist logpersistd buffers to collect
 logd.logpersistd.size            persist logpersistd size in MB
+logd.logpersistd.rotate_kbytes   	 persist logpersistd outout file size in KB.
 persist.logd.logpersistd   string        Enable logpersist daemon, "logcatd"
                                          turns on logcat -f in logd context.
 persist.logd.logpersistd.buffer    all   logpersistd buffers to collect
 persist.logd.logpersistd.size      256   logpersistd size in MB
+persist.logd.logpersistd.count     256   sets max number of rotated logs to <count>.
+persist.logd.logpersistd.rotate_kbytes   1024  logpersistd output file size in KB
 persist.logd.size          number  ro    Global default size of the buffer for
                                          all log ids at initial startup, at
                                          runtime use: logcat -b all -G <value>
diff --git a/logd/auditctl.cpp b/logd/auditctl.cpp
new file mode 100644
index 0000000..98bb02d
--- /dev/null
+++ b/logd/auditctl.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/parseint.h>
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "libaudit.h"
+
+static void usage(const char* cmdline) {
+    fprintf(stderr, "Usage: %s [-r rate]\n", cmdline);
+}
+
+static void do_update_rate(uint32_t rate) {
+    int fd = audit_open();
+    if (fd == -1) {
+        error(EXIT_FAILURE, errno, "Unable to open audit socket");
+    }
+    int result = audit_rate_limit(fd, rate);
+    close(fd);
+    if (result < 0) {
+        fprintf(stderr, "Can't update audit rate limit: %d\n", result);
+        exit(EXIT_FAILURE);
+    }
+}
+
+int main(int argc, char* argv[]) {
+    uint32_t rate = 0;
+    bool update_rate = false;
+    int opt;
+
+    while ((opt = getopt(argc, argv, "r:")) != -1) {
+        switch (opt) {
+            case 'r':
+                if (!android::base::ParseUint<uint32_t>(optarg, &rate)) {
+                    error(EXIT_FAILURE, errno, "Invalid Rate");
+                }
+                update_rate = true;
+                break;
+            default: /* '?' */
+                usage(argv[0]);
+                exit(EXIT_FAILURE);
+        }
+    }
+
+    // In the future, we may add other options to auditctl
+    // so this if statement will expand.
+    // if (!update_rate && !update_backlog && !update_whatever) ...
+    if (!update_rate) {
+        fprintf(stderr, "Nothing to do\n");
+        usage(argv[0]);
+        exit(EXIT_FAILURE);
+    }
+
+    if (update_rate) {
+        do_update_rate(rate);
+    }
+
+    return 0;
+}
diff --git a/logd/libaudit.c b/logd/libaudit.c
index 9d9a857..f452c71 100644
--- a/logd/libaudit.c
+++ b/logd/libaudit.c
@@ -160,8 +160,7 @@
      * and the the mask set to AUDIT_STATUS_PID
      */
     status.pid = pid;
-    status.mask = AUDIT_STATUS_PID | AUDIT_STATUS_RATE_LIMIT;
-    status.rate_limit = AUDIT_RATE_LIMIT; /* audit entries per second */
+    status.mask = AUDIT_STATUS_PID;
 
     /* Let the kernel know this pid will be registering for audit events */
     rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
@@ -188,6 +187,14 @@
     return socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_AUDIT);
 }
 
+int audit_rate_limit(int fd, uint32_t limit) {
+    struct audit_status status;
+    memset(&status, 0, sizeof(status));
+    status.mask = AUDIT_STATUS_RATE_LIMIT;
+    status.rate_limit = limit; /* audit entries per second */
+    return audit_send(fd, AUDIT_SET, &status, sizeof(status));
+}
+
 int audit_get_reply(int fd, struct audit_message* rep, reply_t block, int peek) {
     ssize_t len;
     int flags;
diff --git a/logd/libaudit.h b/logd/libaudit.h
index 2a93ea3..b4a92a8 100644
--- a/logd/libaudit.h
+++ b/logd/libaudit.h
@@ -89,8 +89,17 @@
  */
 extern int audit_setup(int fd, pid_t pid);
 
-/* Max audit messages per second  */
-#define AUDIT_RATE_LIMIT 5
+/**
+ * Throttle kernel messages at the provided rate
+ * @param fd
+ *  The fd returned by a call to audit_open()
+ * @param rate
+ *  The rate, in messages per second, above which the kernel
+ *  should drop audit messages.
+ * @return
+ *  This function returns 0 on success, -errno on error.
+ */
+extern int audit_rate_limit(int fd, uint32_t limit);
 
 __END_DECLS
 
diff --git a/logd/logd.rc b/logd/logd.rc
index bd303b7..530f342 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -6,6 +6,8 @@
     file /dev/kmsg w
     user logd
     group logd system package_info readproc
+    capabilities SYSLOG AUDIT_CONTROL
+    priority 10
     writepid /dev/cpuset/system-background/tasks
 
 service logd-reinit /system/bin/logd --reinit
@@ -15,8 +17,19 @@
     group logd
     writepid /dev/cpuset/system-background/tasks
 
+# Limit SELinux denial generation to 5/second
+service logd-auditctl /system/bin/auditctl -r 5
+    oneshot
+    disabled
+    user logd
+    group logd
+    capabilities AUDIT_CONTROL
+
 on fs
     write /dev/event-log-tags "# content owned by logd
 "
     chown logd logd /dev/event-log-tags
     chmod 0644 /dev/event-log-tags
+
+on property:sys.boot_completed=1
+    start logd-auditctl
diff --git a/logd/main.cpp b/logd/main.cpp
index 4af0d21..23bbf86 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -17,6 +17,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/capability.h>
 #include <poll.h>
 #include <sched.h>
 #include <semaphore.h>
@@ -33,18 +34,17 @@
 #include <syslog.h>
 #include <unistd.h>
 
-#include <cstdbool>
 #include <memory>
 
 #include <android-base/macros.h>
 #include <cutils/android_get_control_file.h>
 #include <cutils/properties.h>
-#include <cutils/sched_policy.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
 #include <packagelistparser/packagelistparser.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
+#include <processgroup/sched_policy.h>
 #include <utils/threads.h>
 
 #include "CommandListener.h"
@@ -58,117 +58,52 @@
     '<', '0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) / 10, \
         '0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) % 10, '>'
 
-//
-// The service is designed to be run by init, it does not respond well
-// to starting up manually. When starting up manually the sockets will
-// fail to open typically for one of the following reasons:
-//     EADDRINUSE if logger is running.
-//     EACCESS if started without precautions (below)
-//
-// Here is a cookbook procedure for starting up logd manually assuming
-// init is out of the way, pedantically all permissions and SELinux
-// security is put back in place:
-//
-//    setenforce 0
-//    rm /dev/socket/logd*
-//    chmod 777 /dev/socket
-//        # here is where you would attach the debugger or valgrind for example
-//    runcon u:r:logd:s0 /system/bin/logd </dev/null >/dev/null 2>&1 &
-//    sleep 1
-//    chmod 755 /dev/socket
-//    chown logd.logd /dev/socket/logd*
-//    restorecon /dev/socket/logd*
-//    setenforce 1
-//
-// If minimalism prevails, typical for debugging and security is not a concern:
-//
-//    setenforce 0
-//    chmod 777 /dev/socket
-//    logd
-//
-
+// The service is designed to be run by init, it does not respond well to starting up manually. Init
+// has a 'sigstop' feature that sends SIGSTOP to a service immediately before calling exec().  This
+// allows debuggers, etc to be attached to logd at the very beginning, while still having init
+// handle the user, groups, capabilities, files, etc setup.
 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)) {
+    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;
     }
 
-    if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
-        android::prdebug("failed to set PR_SET_KEEPCAPS");
-        if (!eng) return -1;
-    }
-
-    std::unique_ptr<struct _cap_struct, int (*)(void*)> caps(cap_init(),
-                                                             cap_free);
-    if (cap_clear(caps.get()) < 0) return -1;
-    cap_value_t cap_value[] = { CAP_SETGID,  // must be first for below
-                                klogd ? CAP_SYSLOG : CAP_SETGID,
-                                auditd ? CAP_AUDIT_CONTROL : CAP_SETGID };
-    if (cap_set_flag(caps.get(), CAP_PERMITTED, arraysize(cap_value), cap_value,
-                     CAP_SET) < 0) {
+    std::unique_ptr<struct _cap_struct, int (*)(void*)> caps(cap_init(), cap_free);
+    if (cap_clear(caps.get()) < 0) {
         return -1;
     }
-    if (cap_set_flag(caps.get(), CAP_EFFECTIVE, arraysize(cap_value), cap_value,
-                     CAP_SET) < 0) {
+    std::vector<cap_value_t> cap_value;
+    if (klogd) {
+        cap_value.emplace_back(CAP_SYSLOG);
+    }
+    if (auditd) {
+        cap_value.emplace_back(CAP_AUDIT_CONTROL);
+    }
+
+    if (cap_set_flag(caps.get(), CAP_PERMITTED, cap_value.size(), cap_value.data(), CAP_SET) < 0) {
+        return -1;
+    }
+    if (cap_set_flag(caps.get(), CAP_EFFECTIVE, cap_value.size(), cap_value.data(), CAP_SET) < 0) {
         return -1;
     }
     if (cap_set_proc(caps.get()) < 0) {
-        android::prdebug(
-            "failed to set CAP_SETGID, CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)",
-            errno);
-        if (!eng) 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;
-    }
-
-    if (setgid(AID_LOGD) != 0) {
-        android::prdebug("failed to set AID_LOGD gid");
-        if (!eng) return -1;
-    }
-
-    if (setuid(AID_LOGD) != 0) {
-        android::prdebug("failed to set AID_LOGD uid");
-        if (!eng) return -1;
-    }
-
-    if (cap_set_flag(caps.get(), CAP_PERMITTED, 1, cap_value, CAP_CLEAR) < 0) {
+        android::prdebug("failed to set CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)", errno);
         return -1;
     }
-    if (cap_set_flag(caps.get(), CAP_EFFECTIVE, 1, cap_value, CAP_CLEAR) < 0) {
-        return -1;
-    }
-    if (cap_set_proc(caps.get()) < 0) {
-        android::prdebug("failed to clear CAP_SETGID (%d)", errno);
-        if (!eng) return -1;
-    }
 
     return 0;
 }
@@ -215,67 +150,14 @@
     }
 }
 
-static sem_t uidName;
-static uid_t uid;
-static char* name;
-
 static sem_t reinit;
 static bool reinit_running = false;
 static LogBuffer* logBuf = nullptr;
 
-static bool package_list_parser_cb(pkg_info* info, void* /* userdata */) {
-    bool rc = true;
-    if (info->uid == uid) {
-        name = strdup(info->name);
-        // false to stop processing
-        rc = false;
-    }
-
-    packagelist_free(info);
-    return rc;
-}
-
 static void* reinit_thread_start(void* /*obj*/) {
     prctl(PR_SET_NAME, "logd.daemon");
-    set_sched_policy(0, SP_BACKGROUND);
-    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND);
-
-    // We should drop to AID_LOGD, if we are anything else, we have
-    // even lesser privileges and accept our fate.
-    gid_t groups[] = {
-        AID_SYSTEM,        // search access to /data/system path
-        AID_PACKAGE_INFO,  // readonly access to /data/system/packages.list
-    };
-    if (setgroups(arraysize(groups), groups) == -1) {
-        android::prdebug(
-            "logd.daemon: failed to set AID_SYSTEM AID_PACKAGE_INFO groups");
-    }
-    if (setgid(AID_LOGD) != 0) {
-        android::prdebug("logd.daemon: failed to set AID_LOGD gid");
-    }
-    if (setuid(AID_LOGD) != 0) {
-        android::prdebug("logd.daemon: failed to set AID_LOGD uid");
-    }
-
-    cap_t caps = cap_init();
-    (void)cap_clear(caps);
-    (void)cap_set_proc(caps);
-    (void)cap_free(caps);
 
     while (reinit_running && !sem_wait(&reinit) && reinit_running) {
-        // uidToName Privileged Worker
-        if (uid) {
-            name = nullptr;
-
-            // if we got the perms wrong above, this would spam if we reported
-            // problems with acquisition of an uid name from the packages.
-            (void)packagelist_parse(package_list_parser_cb, nullptr);
-
-            uid = 0;
-            sem_post(&uidName);
-            continue;
-        }
-
         if (fdDmesg >= 0) {
             static const char reinit_message[] = { KMSG_PRIORITY(LOG_INFO),
                                                    'l',
@@ -312,26 +194,30 @@
     return nullptr;
 }
 
-static sem_t sem_name;
-
 char* android::uidToName(uid_t u) {
-    if (!u || !reinit_running) {
-        return nullptr;
-    }
+    struct Userdata {
+        uid_t uid;
+        char* name;
+    } userdata = {
+            .uid = u,
+            .name = nullptr,
+    };
 
-    sem_wait(&sem_name);
+    packagelist_parse(
+            [](pkg_info* info, void* callback_parameter) {
+                auto userdata = reinterpret_cast<Userdata*>(callback_parameter);
+                bool result = true;
+                if (info->uid == userdata->uid) {
+                    userdata->name = strdup(info->name);
+                    // false to stop processing
+                    result = false;
+                }
+                packagelist_free(info);
+                return result;
+            },
+            &userdata);
 
-    // Not multi-thread safe, we use sem_name to protect
-    uid = u;
-
-    name = nullptr;
-    sem_post(&reinit);
-    sem_wait(&uidName);
-    char* ret = name;
-
-    sem_post(&sem_name);
-
-    return ret;
+    return userdata.name;
 }
 
 // Serves as a global method to trigger reinitialization
@@ -383,11 +269,6 @@
 }
 
 static int issueReinit() {
-    cap_t caps = cap_init();
-    (void)cap_clear(caps);
-    (void)cap_set_proc(caps);
-    (void)cap_free(caps);
-
     int sock = TEMP_FAILURE_RETRY(socket_local_client(
         "logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM));
     if (sock < 0) return -errno;
@@ -450,10 +331,13 @@
         if (fdPmesg < 0) android::prdebug("Failed to open %s\n", proc_kmsg);
     }
 
+    bool auditd = __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
+    if (drop_privs(klogd, auditd) != 0) {
+        return EXIT_FAILURE;
+    }
+
     // Reinit Thread
     sem_init(&reinit, 0, 0);
-    sem_init(&uidName, 0, 0);
-    sem_init(&sem_name, 0, 1);
     pthread_attr_t attr;
     if (!pthread_attr_init(&attr)) {
         struct sched_param param;
@@ -471,12 +355,6 @@
         pthread_attr_destroy(&attr);
     }
 
-    bool auditd =
-        __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
-    if (drop_privs(klogd, auditd) != 0) {
-        return -1;
-    }
-
     // Serves the purpose of managing the last logs times read on a
     // socket connection, and as a reader lock on a range of log
     // entries.
@@ -502,7 +380,7 @@
 
     LogReader* reader = new LogReader(logBuf);
     if (reader->startListener()) {
-        exit(1);
+        return EXIT_FAILURE;
     }
 
     // LogListener listens on /dev/socket/logdw for client
@@ -512,7 +390,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 +398,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 +433,5 @@
 
     TEMP_FAILURE_RETRY(pause());
 
-    exit(0);
+    return EXIT_SUCCESS;
 }
diff --git a/logd/tests/Android.bp b/logd/tests/Android.bp
new file mode 100644
index 0000000..d39da8a
--- /dev/null
+++ b/logd/tests/Android.bp
@@ -0,0 +1,68 @@
+//
+// 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.
+//
+
+// -----------------------------------------------------------------------------
+// Unit tests.
+// -----------------------------------------------------------------------------
+
+cc_defaults {
+    name: "logd-unit-test-defaults",
+
+    cflags: [
+        "-fstack-protector-all",
+        "-g",
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-fno-builtin",
+
+        "-DAUDITD_LOG_TAG=1003",
+        "-DCHATTY_LOG_TAG=1004",
+    ],
+
+    srcs: ["logd_test.cpp"],
+
+    static_libs: [
+        "libbase",
+        "libcutils",
+        "libselinux",
+        "liblog",
+    ],
+}
+
+// Build tests for the logger. Run with:
+//   adb shell /data/nativetest/logd-unit-tests/logd-unit-tests
+cc_test {
+    name: "logd-unit-tests",
+    defaults: ["logd-unit-test-defaults"],
+}
+
+cc_test {
+    name: "CtsLogdTestCases",
+    defaults: ["logd-unit-test-defaults"],
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+    test_suites: [
+        "cts",
+        "vts",
+    ],
+}
diff --git a/logd/tests/Android.mk b/logd/tests/Android.mk
deleted file mode 100644
index a0875ea..0000000
--- a/logd/tests/Android.mk
+++ /dev/null
@@ -1,86 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-# -----------------------------------------------------------------------------
-# Benchmarks. (see ../../liblog/tests)
-# -----------------------------------------------------------------------------
-
-test_module_prefix := logd-
-test_tags := tests
-
-# -----------------------------------------------------------------------------
-# Unit tests.
-# -----------------------------------------------------------------------------
-
-event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004
-
-test_c_flags := \
-    -fstack-protector-all \
-    -g \
-    -Wall -Wextra \
-    -Werror \
-    -fno-builtin \
-    $(event_flag)
-
-test_src_files := \
-    logd_test.cpp
-
-# Build tests for the logger. Run with:
-#   adb shell /data/nativetest/logd-unit-tests/logd-unit-tests
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)unit-tests
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libselinux
-LOCAL_SRC_FILES := $(test_src_files)
-include $(BUILD_NATIVE_TEST)
-
-cts_executable := CtsLogdTestCases
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(cts_executable)
-LOCAL_MODULE_TAGS := tests
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SRC_FILES := $(test_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 := libbase libcutils liblog libselinux
-LOCAL_STATIC_LIBRARIES := libgtest libgtest_main
-LOCAL_COMPATIBILITY_SUITE := cts vts
-LOCAL_CTS_TEST_PACKAGE := android.core.logd
-include $(BUILD_CTS_EXECUTABLE)
-
-ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(cts_executable)_list
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := $(test_c_flags) -DHOST
-LOCAL_C_INCLUDES := external/gtest/include
-LOCAL_SRC_FILES := $(test_src_files)
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_CXX_STL := libc++
-LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-include $(BUILD_HOST_NATIVE_TEST)
-
-endif  # ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
diff --git a/logd/tests/AndroidTest.xml b/logd/tests/AndroidTest.xml
index 84f0764..9a18edb 100644
--- a/logd/tests/AndroidTest.xml
+++ b/logd/tests/AndroidTest.xml
@@ -16,6 +16,8 @@
 <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" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <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 7d7a22f..b6c33d7 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -39,7 +39,6 @@
 #endif
 
 #include "../LogReader.h"  // pickup LOGD_SNDTIMEO
-#include "../libaudit.h"   // pickup AUDIT_RATE_LIMIT_*
 
 #ifdef __ANDROID__
 static void send_to_control(char* buf, size_t len) {
@@ -953,7 +952,7 @@
 void __android_log_btwrite_multiple__helper(int count) {
 #ifdef __ANDROID__
     log_time ts(CLOCK_MONOTONIC);
-
+    usleep(100);
     log_time ts1(CLOCK_MONOTONIC);
 
     // We fork to create a unique pid for the submitted log messages
@@ -1065,145 +1064,3 @@
 TEST(logd, multiple_test_10) {
     __android_log_btwrite_multiple__helper(10);
 }
-
-#ifdef __ANDROID__
-// returns violating pid
-static pid_t sepolicy_rate(unsigned rate, unsigned num) {
-    pid_t pid = fork();
-
-    if (pid) {
-        siginfo_t info = {};
-        if (TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED))) return -1;
-        if (info.si_status) return -1;
-        return pid;
-    }
-
-    // We may have DAC, but let's not have MAC
-    if ((setcon("u:object_r:shell:s0") < 0) && (setcon("u:r:shell:s0") < 0)) {
-        int save_errno = errno;
-        security_context_t context;
-        getcon(&context);
-        if (strcmp(context, "u:r:shell:s0")) {
-            fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n",
-                    context, strerror(save_errno));
-            freecon(context);
-            _exit(-1);
-            // NOTREACHED
-            return -1;
-        }
-    }
-
-    // The key here is we are root, but we are in u:r:shell:s0,
-    // and the directory does not provide us DAC access
-    // (eg: 0700 system system) so we trigger the pair dac_override
-    // 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/drm/cannot_access_directory_%u";
-    static const unsigned avc_requests_per_access = 2;
-
-    rate /= avc_requests_per_access;
-    useconds_t usec;
-    if (rate == 0) {
-        rate = 1;
-        usec = 2000000;
-    } else {
-        usec = (1000000 + (rate / 2)) / rate;
-    }
-    num = (num + (avc_requests_per_access / 2)) / avc_requests_per_access;
-
-    if (usec < 2) usec = 2;
-
-    while (num > 0) {
-        if (access(android::base::StringPrintf(file, num).c_str(), F_OK) == 0) {
-            _exit(-1);
-            // NOTREACHED
-            return -1;
-        }
-        usleep(usec);
-        --num;
-    }
-    _exit(0);
-    // NOTREACHED
-    return -1;
-}
-
-static constexpr int background_period = 10;
-
-static int count_avc(pid_t pid) {
-    int count = 0;
-
-    // pid=-1 skip as pid is in error
-    if (pid == (pid_t)-1) return count;
-
-    // pid=0 means we want to report the background count of avc: activities
-    struct logger_list* logger_list =
-        pid ? android_logger_list_alloc(
-                  ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, pid)
-            : android_logger_list_alloc_time(
-                  ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
-                  log_time(android_log_clockid()) -
-                      log_time(background_period, 0),
-                  0);
-    if (!logger_list) return count;
-    struct logger* logger = android_logger_open(logger_list, LOG_ID_EVENTS);
-    if (!logger) {
-        android_logger_list_close(logger_list);
-        return count;
-    }
-    for (;;) {
-        log_msg log_msg;
-
-        if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
-
-        if ((log_msg.entry.pid != pid) || (log_msg.entry.len < (4 + 1 + 8)) ||
-            (log_msg.id() != LOG_ID_EVENTS))
-            continue;
-
-        char* eventData = log_msg.msg();
-        if (!eventData) continue;
-
-        uint32_t tag = get4LE(eventData);
-        if (tag != AUDITD_LOG_TAG) continue;
-
-        if (eventData[4] != EVENT_TYPE_STRING) continue;
-
-        // int len = get4LE(eventData + 4 + 1);
-        log_msg.buf[LOGGER_ENTRY_MAX_LEN] = '\0';
-        const char* cp = strstr(eventData + 4 + 1 + 4, "): avc: denied");
-        if (!cp) continue;
-
-        ++count;
-    }
-
-    android_logger_list_close(logger_list);
-
-    return count;
-}
-#endif
-
-TEST(logd, sepolicy_rate_limiter) {
-#ifdef __ANDROID__
-    int background_selinux_activity_too_high = count_avc(0);
-    if (background_selinux_activity_too_high > 2) {
-        GTEST_LOG_(ERROR) << "Too much background selinux activity "
-                          << background_selinux_activity_too_high * 60 /
-                                 background_period
-                          << "/minute on the device, this test\n"
-                          << "can not measure the functionality of the "
-                          << "sepolicy rate limiter.  Expect test to\n"
-                          << "fail as this device is in a bad state, "
-                          << "but is not strictly a unit test failure.";
-    }
-
-    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 54506dc..c378646 100644
--- a/logwrapper/Android.bp
+++ b/logwrapper/Android.bp
@@ -12,6 +12,7 @@
 cc_library {
     name: "liblogwrap",
     defaults: ["logwrapper_defaults"],
+    recovery_available: true,
     srcs: ["logwrap.c"],
     shared_libs: [
         "libcutils",
@@ -41,14 +42,11 @@
     defaults: ["logwrapper_common"],
 }
 
-// Build vendor logwrapper.
-// TODO: Add vendor_available to "logwrapper" module and remove "logwrapper_vendor" module
-//       when vendor_available is fully supported.
 cc_binary {
     name: "logwrapper_vendor",
+    defaults: ["logwrapper_common"],
     stem: "logwrapper",
     vendor: true,
-    defaults: ["logwrapper_common"],
 }
 
 // ========================================================
diff --git a/mkbootimg/Android.bp b/mkbootimg/Android.bp
deleted file mode 100644
index b494346..0000000
--- a/mkbootimg/Android.bp
+++ /dev/null
@@ -1,32 +0,0 @@
-// 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,
-    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
deleted file mode 100644
index 92e1e27..0000000
--- a/mkbootimg/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := mkbootimg
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_IS_HOST_MODULE := true
-
-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/include/abi_check/mkbootimg_abi_check.h b/mkbootimg/include/abi_check/mkbootimg_abi_check.h
deleted file mode 100644
index d478aba..0000000
--- a/mkbootimg/include/abi_check/mkbootimg_abi_check.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 0188d0e..0000000
--- a/mkbootimg/include/bootimg/bootimg.h
+++ /dev/null
@@ -1,167 +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_
-
-#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
-
-#define BOOT_HEADER_VERSION_ZERO 0
-/*
- *  Bootloader expects the structure of boot_img_hdr with header version
- *  BOOT_HEADER_VERSION_ZERO to be as follows:
- */
-struct boot_img_hdr_v0 {
-    uint8_t magic[BOOT_MAGIC_SIZE];
-
-    uint32_t kernel_size; /* size in bytes */
-    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 for the boot image header.
-     */
-    uint32_t header_version;
-
-    /* operating system version and security patch level; for
-     * version "A.B.C" and patch level "Y-M-D":
-     * 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));
-
-/*
- * It is expected that callers would explicitly specify which version of the
- * boot image header they need to use.
- */
-typedef struct boot_img_hdr_v0 boot_img_hdr;
-
-/* When a boot header is of version BOOT_HEADER_VERSION_ZERO, the structure of boot image is as
- * follows:
- *
- * +-----------------+
- * | boot header     | 1 page
- * +-----------------+
- * | kernel          | n pages
- * +-----------------+
- * | ramdisk         | m pages
- * +-----------------+
- * | second stage    | o pages
- * +-----------------+
- *
- * n = (kernel_size + page_size - 1) / page_size
- * m = (ramdisk_size + page_size - 1) / page_size
- * o = (second_size + page_size - 1) / page_size
- *
- * 0. all entities are page_size aligned in flash
- * 1. kernel and ramdisk are required (size != 0)
- * 2. second is optional (second_size == 0 -> no second)
- * 3. load each element (kernel, ramdisk, second) at
- *    the specified physical address (kernel_addr, etc)
- * 4. prepare tags at tag_addr.  kernel_args[] is
- *    appended to the kernel commandline in the tags.
- * 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
- * 6. if second_size != 0: jump to second_addr
- *    else: jump to kernel_addr
- */
-
-#define BOOT_HEADER_VERSION_ONE 1
-
-struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
-    uint32_t recovery_dtbo_size;   /* size in bytes for recovery DTBO image */
-    uint64_t recovery_dtbo_offset; /* offset to recovery dtbo in boot image */
-    uint32_t header_size;
-} __attribute__((packed));
-
-/* When the boot image header has a version of BOOT_HEADER_VERSION_ONE, the structure of the boot
- * image is as follows:
- *
- * +-----------------+
- * | boot header     | 1 page
- * +-----------------+
- * | kernel          | n pages
- * +-----------------+
- * | ramdisk         | m pages
- * +-----------------+
- * | second stage    | o pages
- * +-----------------+
- * | recovery dtbo   | p pages
- * +-----------------+
- * n = (kernel_size + page_size - 1) / page_size
- * m = (ramdisk_size + page_size - 1) / page_size
- * o = (second_size + page_size - 1) / page_size
- * p = (recovery_dtbo_size + page_size - 1) / page_size
- *
- * 0. all entities are page_size aligned in flash
- * 1. kernel and ramdisk are required (size != 0)
- * 2. recovery_dtbo is required for recovery.img in non-A/B devices(recovery_dtbo_size != 0)
- * 3. second is optional (second_size == 0 -> no second)
- * 4. load each element (kernel, ramdisk, second) 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
- */
-
-#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/mkbootimg b/mkbootimg/mkbootimg
deleted file mode 100755
index fda9af0..0000000
--- a/mkbootimg/mkbootimg
+++ /dev/null
@@ -1,208 +0,0 @@
-#!/usr/bin/env python
-# 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.
-
-from __future__ import print_function
-from sys import argv, exit, stderr
-from argparse import ArgumentParser, FileType, Action
-from os import fstat
-from struct import pack
-from hashlib import sha1
-import sys
-import re
-
-def filesize(f):
-    if f is None:
-        return 0
-    try:
-        return fstat(f.fileno()).st_size
-    except OSError:
-        return 0
-
-
-def update_sha(sha, f):
-    if f:
-        sha.update(f.read())
-        f.seek(0)
-        sha.update(pack('I', filesize(f)))
-    else:
-        sha.update(pack('I', 0))
-
-
-def pad_file(f, padding):
-    pad = (padding - (f.tell() & (padding - 1))) & (padding - 1)
-    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))
-    args.output.write(pack('10I',
-        filesize(args.kernel),                          # size in bytes
-        args.base + args.kernel_offset,                 # physical load addr
-        filesize(args.ramdisk),                         # size in bytes
-        args.base + args.ramdisk_offset,                # physical load addr
-        filesize(args.second),                          # size in bytes
-        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
-        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()))
-
-    sha = sha1()
-    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
-
-
-class ValidateStrLenAction(Action):
-    def __init__(self, option_strings, dest, nargs=None, **kwargs):
-        if 'maxlen' not in kwargs:
-            raise ValueError('maxlen must be set')
-        self.maxlen = int(kwargs['maxlen'])
-        del kwargs['maxlen']
-        super(ValidateStrLenAction, self).__init__(option_strings, dest, **kwargs)
-
-    def __call__(self, parser, namespace, values, option_string=None):
-        if len(values) > self.maxlen:
-            raise ValueError('String argument too long: max {0:d}, got {1:d}'.
-                format(self.maxlen, len(values)))
-        setattr(namespace, self.dest, values)
-
-
-def write_padded_file(f_out, f_in, padding):
-    if f_in is None:
-        return
-    f_out.write(f_in.read())
-    pad_file(f_out, padding)
-
-
-def parse_int(x):
-    return int(x, 0)
-
-def parse_os_version(x):
-    match = re.search(r'^(\d{1,3})(?:\.(\d{1,3})(?:\.(\d{1,3}))?)?', x)
-    if match:
-        a = int(match.group(1))
-        b = c = 0
-        if match.lastindex >= 2:
-            b = int(match.group(2))
-        if match.lastindex == 3:
-            c = int(match.group(3))
-        # 7 bits allocated for each field
-        assert a < 128
-        assert b < 128
-        assert c < 128
-        return (a << 14) | (b << 7) | c
-    return 0
-
-def parse_os_patch_level(x):
-    match = re.search(r'^(\d{4})-(\d{2})-(\d{2})', x)
-    if match:
-        y = int(match.group(1)) - 2000
-        m = int(match.group(2))
-        # 7 bits allocated for the year, 4 bits for the month
-        assert y >= 0 and y < 128
-        assert m > 0 and m <= 12
-        return (y << 4) | m
-    return 0
-
-def parse_cmdline():
-    parser = ArgumentParser()
-    parser.add_argument('--kernel', help='path to the kernel', type=FileType('rb'),
-                        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)
-    parser.add_argument('--kernel_offset', help='kernel offset', type=parse_int, default=0x00008000)
-    parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000)
-    parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
-                        default=0x00f00000)
-    parser.add_argument('--os_version', help='operating system version', type=parse_os_version,
-                        default=0)
-    parser.add_argument('--os_patch_level', help='operating system patch level',
-                        type=parse_os_patch_level, default=0)
-    parser.add_argument('--tags_offset', help='tags offset', type=parse_int, default=0x00000100)
-    parser.add_argument('--board', help='board name', default='', action=ValidateStrLenAction,
-                        maxlen=16)
-    parser.add_argument('--pagesize', help='page size', type=parse_int,
-                        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()
-
-
-def write_data(args):
-    write_padded_file(args.output, args.kernel, args.pagesize)
-    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()
-    img_id = write_header(args)
-    write_data(args)
-    if args.id:
-        if isinstance(img_id, str):
-            # Python 2's struct.pack returns a string, but py3 returns bytes.
-            img_id = [ord(x) for x in img_id]
-        print('0x' + ''.join('{:02x}'.format(c) for c in img_id))
-
-if __name__ == '__main__':
-    main()
diff --git a/mkbootimg/mkbootimg_dummy.cpp b/mkbootimg/mkbootimg_dummy.cpp
deleted file mode 100644
index 410d379..0000000
--- a/mkbootimg/mkbootimg_dummy.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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
deleted file mode 100755
index c37acd5..0000000
--- a/mkbootimg/unpack_bootimg
+++ /dev/null
@@ -1,134 +0,0 @@
-#!/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/platform_tools_tool_version.mk b/platform_tools_tool_version.mk
deleted file mode 100644
index eed2ab5..0000000
--- a/platform_tools_tool_version.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright (C) 2017 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.
-
-# We rewrite ${PLATFORM_SDK_VERSION} with 0 rather than $(PLATFORM_SDK_VERSION)
-# because on the actual platform tools release branches the file contains a
-# literal instead. Using 0 lets us easily distinguish non-canonical builds.
-platform_tools_version := $(shell sed \
-    's/$${PLATFORM_SDK_VERSION}/0/ ; s/^Pkg.Revision=\(.*\)/\1/p ; d' \
-    development/sdk/plat_tools_source.prop_template \
-  )
-tool_version := $(platform_tools_version)-$(BUILD_NUMBER_FROM_FILE)
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index ea9b968..ac802b5 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -2,6 +2,8 @@
     name: "libpropertyinfoparser",
     host_supported: true,
     vendor_available: true,
+    recovery_available: true,
+    native_bridge_supported: true,
     srcs: ["property_info_parser.cpp"],
 
     cpp_std: "experimental",
@@ -11,5 +13,7 @@
         "-Werror",
     ],
     stl: "none",
+    system_shared_libs: [],
+    header_libs: ["libc_headers"],
     export_include_dirs: ["include"],
 }
diff --git a/property_service/libpropertyinfoserializer/Android.bp b/property_service/libpropertyinfoserializer/Android.bp
index 72ae19a..51c1226 100644
--- a/property_service/libpropertyinfoserializer/Android.bp
+++ b/property_service/libpropertyinfoserializer/Android.bp
@@ -17,6 +17,7 @@
 cc_library_static {
     name: "libpropertyinfoserializer",
     defaults: ["propertyinfoserializer_defaults"],
+    recovery_available: true,
     srcs: [
         "property_info_file.cpp",
         "property_info_serializer.cpp",
@@ -35,4 +36,5 @@
         "property_info_serializer_test.cpp",
     ],
     static_libs: ["libpropertyinfoserializer"],
+    test_suites: ["device-tests"],
 }
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
index f484550..33da1f1 100644
--- a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -585,6 +585,7 @@
       {"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.first_stage", "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"},
diff --git a/qemu_pipe/Android.bp b/qemu_pipe/Android.bp
index 93c347b..c6bda4a 100644
--- a/qemu_pipe/Android.bp
+++ b/qemu_pipe/Android.bp
@@ -3,6 +3,7 @@
 cc_library_static {
     name: "libqemu_pipe",
     vendor_available: true,
+    recovery_available: true,
     sanitize: {
         misc_undefined: ["integer"],
     },
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 5f5c363..246f9ac 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -8,6 +8,11 @@
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+LOCAL_REQUIRED_MODULES := fsverity_init
+
+# The init symlink must be a post install command of a file that is to TARGET_ROOT_OUT.
+# Since init.rc is required for init and satisfies that requirement, we hijack it to create the symlink.
+LOCAL_POST_INSTALL_CMD := ln -sf /system/bin/init $(TARGET_ROOT_OUT)/init
 
 include $(BUILD_PREBUILT)
 
@@ -18,7 +23,6 @@
 LOCAL_MODULE := init-debug.rc
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_TAGS := debug
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
 
 include $(BUILD_PREBUILT)
@@ -54,6 +58,15 @@
 endif
 
 #######################################
+# fsverity_init
+
+include $(CLEAR_VARS)
+LOCAL_MODULE:= fsverity_init
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_SRC_FILES := fsverity_init.sh
+include $(BUILD_PREBUILT)
+
+#######################################
 # init.environ.rc
 
 include $(CLEAR_VARS)
@@ -67,9 +80,16 @@
   LOCAL_REQUIRED_MODULES := asan.options $(ASAN_OPTIONS_FILES) $(ASAN_EXTRACT_FILES)
 endif
 
+EXPORT_GLOBAL_HWASAN_OPTIONS :=
+ifneq ($(filter hwaddress,$(SANITIZE_TARGET)),)
+  ifneq ($(HWADDRESS_SANITIZER_GLOBAL_OPTIONS),)
+    EXPORT_GLOBAL_HWASAN_OPTIONS := export HWASAN_OPTIONS $(HWADDRESS_SANITIZER_GLOBAL_OPTIONS)
+  endif
+endif
+
 EXPORT_GLOBAL_GCOV_OPTIONS :=
 ifeq ($(NATIVE_COVERAGE),true)
-  EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/gcov
+  EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/trace
 endif
 
 # Put it here instead of in init.rc module definition,
@@ -77,7 +97,7 @@
 #
 # 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 odm oem acct config storage mnt $(BOARD_ROOT_EXTRA_FOLDERS)); \
+    dev proc sys system data odm oem acct config storage mnt apex debug_ramdisk $(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; \
@@ -93,6 +113,11 @@
 else
   LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product $(TARGET_ROOT_OUT)/product
 endif
+ifdef BOARD_USES_SYSTEM_EXTIMAGE
+  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_ext
+else
+  LOCAL_POST_INSTALL_CMD += ; ln -sf /system/system_ext $(TARGET_ROOT_OUT)/system_ext
+endif
 ifdef BOARD_USES_METADATA_PARTITION
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/metadata
 endif
@@ -112,6 +137,7 @@
 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
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/usr $(TARGET_ROOT_OUT)/odm/usr
 
 ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
@@ -133,22 +159,15 @@
 
 include $(BUILD_SYSTEM)/base_rules.mk
 
-# Regenerate init.environ.rc if PRODUCT_BOOTCLASSPATH has changed.
-bcp_md5 := $(word 1, $(shell echo $(PRODUCT_BOOTCLASSPATH) $(PRODUCT_SYSTEM_SERVER_CLASSPATH) | $(MD5SUM)))
-bcp_dep := $(intermediates)/$(bcp_md5).bcp.dep
-$(bcp_dep) :
-	$(hide) mkdir -p $(dir $@) && rm -rf $(dir $@)*.bcp.dep && touch $@
-
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in $(bcp_dep)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in
 	@echo "Generate: $< -> $@"
 	@mkdir -p $(dir $@)
 	$(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@
+	$(hide) sed -i -e 's?%DEX2OATBOOTCLASSPATH%?$(PRODUCT_DEX2OAT_BOOTCLASSPATH)?g' $@
 	$(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
-
-bcp_md5 :=
-bcp_dep :=
+	$(hide) sed -i -e 's?%EXPORT_GLOBAL_HWASAN_OPTIONS%?$(EXPORT_GLOBAL_HWASAN_OPTIONS)?g' $@
 
 # Append PLATFORM_VNDK_VERSION to base name.
 define append_vndk_version
@@ -157,65 +176,6 @@
 )
 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' $$@
-
-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
@@ -254,26 +214,64 @@
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
 
+# Start of runtime APEX compatibility.
+#
+# Meta-comment:
+# The placing of this section is somewhat arbitrary. The LOCAL_POST_INSTALL_CMD
+# entries need to be associated with something that goes into /system.
+# ld.config.txt qualifies but it could be anything else in /system until soong
+# supports creation of symlinks. http://b/123333111
+#
+# Keeping the appearance of files/dirs having old locations for apps that have
+# come to rely on them.
+
+# http://b/121248172 - create a link from /system/usr/icu to
+# /apex/com.android.runtime/etc/icu so that apps can find the ICU .dat file.
+# A symlink can't overwrite a directory and the /system/usr/icu directory once
+# existed so the required structure must be created whatever we find.
+LOCAL_POST_INSTALL_CMD = mkdir -p $(TARGET_OUT)/usr && rm -rf $(TARGET_OUT)/usr/icu
+LOCAL_POST_INSTALL_CMD += && ln -sf /apex/com.android.runtime/etc/icu $(TARGET_OUT)/usr/icu
+
+# TODO(b/124106384): Clean up compat symlinks for ART binaries.
+ART_BINARIES := \
+  dalvikvm \
+  dalvikvm32 \
+  dalvikvm64 \
+  dex2oat \
+  dexdiag \
+  dexdump \
+  dexlist \
+  dexoptanalyzer \
+  oatdump \
+  profman \
+
+LOCAL_POST_INSTALL_CMD += && mkdir -p $(TARGET_OUT)/bin
+$(foreach b,$(ART_BINARIES), \
+  $(eval LOCAL_POST_INSTALL_CMD += \
+    && ln -sf /apex/com.android.runtime/bin/$(b) $(TARGET_OUT)/bin/$(b)) \
+)
+
+# End of runtime APEX compatibilty.
+
 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)))
+ld_config_template := $(LOCAL_PATH)/etc/ld.config.txt
+check_backward_compatibility := true
+vndk_version := $(PLATFORM_VNDK_VERSION)
+include $(LOCAL_PATH)/update_and_install_ld_config.mk
 
 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))
+ld_config_template := $(LOCAL_PATH)/etc/ld.config.vndk_lite.txt
+vndk_version := $(PLATFORM_VNDK_VERSION)
+libz_is_llndk := true
+include $(LOCAL_PATH)/update_and_install_ld_config.mk
 
 else
 
@@ -284,6 +282,35 @@
 
 endif  # ifeq ($(_enforce_vndk_at_runtime),true)
 
+# ld.config.txt for VNDK versions older than PLATFORM_VNDK_VERSION
+# are built with the VNDK libraries lists under /prebuilts/vndk.
+#
+# ld.config.$(VER).txt is built and installed for all VNDK versions
+# listed in PRODUCT_EXTRA_VNDK_VERSIONS.
+#
+# $(1): VNDK version
+define build_versioned_ld_config
+include $(CLEAR_VARS)
+LOCAL_MODULE := ld.config.$(1).txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+LOCAL_MODULE_STEM := $$(LOCAL_MODULE)
+include $(BUILD_SYSTEM)/base_rules.mk
+ld_config_template := $(LOCAL_PATH)/etc/ld.config.txt
+check_backward_compatibility := true
+vndk_version := $(1)
+lib_list_from_prebuilts := true
+include $(LOCAL_PATH)/update_and_install_ld_config.mk
+endef
+
+vndk_snapshots := $(wildcard prebuilts/vndk/*)
+supported_vndk_snapshot_versions := \
+  $(strip $(patsubst prebuilts/vndk/v%,%,$(vndk_snapshots)))
+$(foreach ver,$(supported_vndk_snapshot_versions),\
+  $(eval $(call build_versioned_ld_config,$(ver))))
+
+vndk_snapshots :=
+supported_vndk_snapshot_versions :=
 
 #######################################
 # ld.config.vndk_lite.txt
@@ -298,11 +325,10 @@
 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))
+ld_config_template := $(LOCAL_PATH)/etc/ld.config.vndk_lite.txt
+vndk_version := $(PLATFORM_VNDK_VERSION)
+libz_is_llndk := true
+include $(LOCAL_PATH)/update_and_install_ld_config.mk
 
 endif  # ifeq ($(_enforce_vndk_lite_at_runtime),false)
 
@@ -310,6 +336,16 @@
 _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)/system/etc
+LOCAL_MODULE_STEM := ld.config.txt
+include $(BUILD_PREBUILT)
+
+#######################################
 # llndk.libraries.txt
 include $(CLEAR_VARS)
 LOCAL_MODULE := llndk.libraries.txt
@@ -340,3 +376,14 @@
 	$(hide) echo -n > $@
 	$(hide) $(foreach lib,$(PRIVATE_VNDK_SAMEPROCESS_LIBRARIES), \
 		echo $(lib).so >> $@;)
+
+#######################################
+# adb_debug.prop in debug ramdisk
+include $(CLEAR_VARS)
+LOCAL_MODULE := adb_debug.prop
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_DEBUG_RAMDISK_OUT)
+include $(BUILD_PREBUILT)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/rootdir/OWNERS b/rootdir/OWNERS
index 6029ae7..ca22eb8 100644
--- a/rootdir/OWNERS
+++ b/rootdir/OWNERS
@@ -1,3 +1,4 @@
+ccross@google.com
 jeffv@google.com
 jiyong@google.com
 smoreland@google.com
diff --git a/rootdir/adb_debug.prop b/rootdir/adb_debug.prop
new file mode 100644
index 0000000..37e2f2d
--- /dev/null
+++ b/rootdir/adb_debug.prop
@@ -0,0 +1,12 @@
+# Note: This file will be loaded with highest priority to override
+# other system properties, if a special ramdisk with "/force_debuggable"
+# is used and the device is unlocked.
+
+# Disable adb authentication to allow test automation on user build GSI
+ro.adb.secure=0
+
+# Allow 'adb root' on user build GSI
+ro.debuggable=1
+
+# Introduce this property to indicate that init has loaded adb_debug.prop
+ro.force.debuggable=1
diff --git a/rootdir/avb/Android.mk b/rootdir/avb/Android.mk
new file mode 100644
index 0000000..5dc019c
--- /dev/null
+++ b/rootdir/avb/Android.mk
@@ -0,0 +1,46 @@
+LOCAL_PATH:= $(call my-dir)
+
+#######################################
+# q-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := q-gsi.avbpubkey
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
+
+include $(BUILD_PREBUILT)
+
+#######################################
+# r-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := r-gsi.avbpubkey
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
+
+include $(BUILD_PREBUILT)
+
+#######################################
+# s-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := s-gsi.avbpubkey
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
+
+include $(BUILD_PREBUILT)
diff --git a/rootdir/avb/q-gsi.avbpubkey b/rootdir/avb/q-gsi.avbpubkey
new file mode 100644
index 0000000..5ed7543
--- /dev/null
+++ b/rootdir/avb/q-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/avb/r-gsi.avbpubkey b/rootdir/avb/r-gsi.avbpubkey
new file mode 100644
index 0000000..2609b30
--- /dev/null
+++ b/rootdir/avb/r-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/avb/s-gsi.avbpubkey b/rootdir/avb/s-gsi.avbpubkey
new file mode 100644
index 0000000..9065fb8
--- /dev/null
+++ b/rootdir/avb/s-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/etc/OWNERS b/rootdir/etc/OWNERS
new file mode 100644
index 0000000..8a65b23
--- /dev/null
+++ b/rootdir/etc/OWNERS
@@ -0,0 +1,4 @@
+danalbert@google.com
+enh@google.com
+jiyong@google.com
+rprichard@google.com
diff --git a/rootdir/etc/TEST_MAPPING b/rootdir/etc/TEST_MAPPING
new file mode 100644
index 0000000..e4d3d5e
--- /dev/null
+++ b/rootdir/etc/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsBionicTestCases"
+    }
+  ]
+}
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index ca6aafe..ad14493 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -6,35 +6,167 @@
 
 # All binaries gets the same configuration 'legacy'
 dir.legacy = /system
+dir.legacy = /product
 dir.legacy = /vendor
 dir.legacy = /odm
 dir.legacy = /sbin
 
-# Except for /postinstall, where only /system is searched
+# Except for /postinstall, where only /system and /product are searched
 dir.postinstall = /postinstall
 
+# Fallback entry to provide APEX namespace lookups for binaries anywhere else.
+# This must be last.
+dir.legacy = /data
+
 [legacy]
 namespace.default.isolated = false
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.default.visible = true
 
 namespace.default.search.paths  = /system/${LIB}
+namespace.default.search.paths += /product/${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/product/${LIB}
+namespace.default.asan.search.paths +=           /product/${LIB}
 namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
 namespace.default.asan.search.paths +=           /vendor/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}
+namespace.default.asan.search.paths +=           /odm/${LIB}
+
+###############################################################################
+# APEX related namespaces.
+###############################################################################
+
+additional.namespaces = runtime,conscrypt,media,resolv
+
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
+# If a shared library or an executable requests a shared library that
+# cannot be loaded into the default namespace, the dynamic linker tries
+# to load the shared library from the runtime namespace. And then, if the
+# shared library cannot be loaded from the runtime namespace either, the
+# dynamic linker tries to load the shared library from the resolv namespace.
+# Finally, if all attempts fail, the dynamic linker returns an error.
+namespace.default.links = runtime,resolv
+namespace.default.asan.links = runtime,resolv
+namespace.default.link.runtime.shared_libs  = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
+namespace.default.link.runtime.shared_libs += libicui18n.so
+namespace.default.link.runtime.shared_libs += libicuuc.so
+namespace.default.link.runtime.shared_libs += libnativebridge.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
+
+# When libnetd_resolv.so can't be found in the default namespace, search for it
+# in the resolv namespace. Don't allow any other libraries from the resolv namespace
+# to be loaded in the default namespace.
+namespace.default.link.resolv.shared_libs = libnetd_resolv.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.runtime.visible = true
+
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# Need allow_all_shared_libs because libart.so can dlopen oat files in
+# /system/framework and /data.
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
+###############################################################################
+# "media" APEX namespace
+#
+# This namespace is for libraries within the media APEX.
+###############################################################################
+namespace.media.isolated = true
+namespace.media.visible = true
+
+namespace.media.search.paths = /apex/com.android.media/${LIB}
+namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+
+namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+
+namespace.media.links = default
+namespace.media.link.default.shared_libs  = libbinder_ndk.so
+namespace.media.link.default.shared_libs += libc.so
+namespace.media.link.default.shared_libs += libcgrouprc.so
+namespace.media.link.default.shared_libs += libdl.so
+namespace.media.link.default.shared_libs += liblog.so
+namespace.media.link.default.shared_libs += libmediametrics.so
+namespace.media.link.default.shared_libs += libmediandk.so
+namespace.media.link.default.shared_libs += libm.so
+namespace.media.link.default.shared_libs += libvndksupport.so
+
+namespace.media.link.default.shared_libs += libclang_rt.asan-aarch64-android.so
+namespace.media.link.default.shared_libs += libclang_rt.asan-arm-android.so
+namespace.media.link.default.shared_libs += libclang_rt.asan-i686-android.so
+namespace.media.link.default.shared_libs += libclang_rt.asan-x86_64-android.so
+namespace.media.link.default.shared_libs += libclang_rt.hwasan-aarch64-android.so
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = runtime,default
+namespace.conscrypt.link.runtime.shared_libs  = libandroidio.so
+namespace.conscrypt.link.default.shared_libs  = libc.so
+namespace.conscrypt.link.default.shared_libs += libm.so
+namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += liblog.so
+
+###############################################################################
+# "resolv" APEX namespace
+#
+# This namespace is for libraries within the resolv APEX.
+###############################################################################
+namespace.resolv.isolated = true
+namespace.resolv.visible = true
+
+namespace.resolv.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.links = default
+namespace.resolv.link.default.shared_libs  = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
+namespace.resolv.link.default.shared_libs += libm.so
+namespace.resolv.link.default.shared_libs += libdl.so
+namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += liblog.so
+namespace.resolv.link.default.shared_libs += libvndksupport.so
 
 ###############################################################################
 # 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.
+# /system/lib and /product/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  = /system/${LIB}
+namespace.default.search.paths += /product/${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 42dc7ab..b1616d3 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -20,15 +20,21 @@
 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.unrestricted = /data/nativetest/unrestricted
+dir.unrestricted = /data/nativetest64/unrestricted
+
+# TODO(b/123864775): Ensure tests are run from /data/nativetest{,64} or (if
+# necessary) the unrestricted subdirs above. Then clean this up.
+dir.unrestricted = /data/local/tmp
 
 dir.postinstall = /postinstall
 
+# Fallback entry to provide APEX namespace lookups for binaries anywhere else.
+# This must be last.
+dir.system = /data
+
 [system]
-additional.namespaces = sphal,vndk,rs
+additional.namespaces = runtime,conscrypt,media,resolv,sphal,vndk,rs
 
 ###############################################################################
 # "default" namespace
@@ -37,9 +43,13 @@
 # can't be loaded in this namespace.
 ###############################################################################
 namespace.default.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.default.visible = true
 
 namespace.default.search.paths  = /system/${LIB}
 namespace.default.search.paths += /%PRODUCT%/${LIB}
+namespace.default.search.paths += /%PRODUCT_SERVICES%/${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
@@ -52,6 +62,7 @@
 namespace.default.permitted.paths += /system/${LIB}/extractors
 namespace.default.permitted.paths += /system/${LIB}/hw
 namespace.default.permitted.paths += /%PRODUCT%/${LIB}
+namespace.default.permitted.paths += /%PRODUCT_SERVICES%/${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
@@ -59,6 +70,9 @@
 namespace.default.permitted.paths += /vendor/framework
 namespace.default.permitted.paths += /vendor/app
 namespace.default.permitted.paths += /vendor/priv-app
+namespace.default.permitted.paths += /system/vendor/framework
+namespace.default.permitted.paths += /system/vendor/app
+namespace.default.permitted.paths += /system/vendor/priv-app
 namespace.default.permitted.paths += /odm/framework
 namespace.default.permitted.paths += /odm/app
 namespace.default.permitted.paths += /odm/priv-app
@@ -66,13 +80,20 @@
 namespace.default.permitted.paths += /%PRODUCT%/framework
 namespace.default.permitted.paths += /%PRODUCT%/app
 namespace.default.permitted.paths += /%PRODUCT%/priv-app
+namespace.default.permitted.paths += /%PRODUCT_SERVICES%/framework
+namespace.default.permitted.paths += /%PRODUCT_SERVICES%/app
+namespace.default.permitted.paths += /%PRODUCT_SERVICES%/priv-app
 namespace.default.permitted.paths += /data
 namespace.default.permitted.paths += /mnt/expand
+namespace.default.permitted.paths += /apex/com.android.runtime/${LIB}/bionic
+namespace.default.permitted.paths += /system/${LIB}/bootstrap
 
 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%/${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
@@ -84,6 +105,9 @@
 namespace.default.asan.permitted.paths += /vendor/framework
 namespace.default.asan.permitted.paths += /vendor/app
 namespace.default.asan.permitted.paths += /vendor/priv-app
+namespace.default.asan.permitted.paths += /system/vendor/framework
+namespace.default.asan.permitted.paths += /system/vendor/app
+namespace.default.asan.permitted.paths += /system/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
@@ -92,7 +116,117 @@
 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 += /%PRODUCT_SERVICES%/${LIB}
+namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/framework
+namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/app
+namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/priv-app
 namespace.default.asan.permitted.paths += /mnt/expand
+namespace.default.asan.permitted.paths += /apex/com.android.runtime/${LIB}/bionic
+namespace.default.asan.permitted.paths += /system/${LIB}/bootstrap
+
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
+# If a shared library or an executable requests a shared library that
+# cannot be loaded into the default namespace, the dynamic linker tries
+# to load the shared library from the runtime namespace. And then, if the
+# shared library cannot be loaded from the runtime namespace either, the
+# dynamic linker tries to load the shared library from the resolv namespace.
+# Finally, if all attempts fail, the dynamic linker returns an error.
+namespace.default.links = runtime,resolv
+namespace.default.link.runtime.shared_libs  = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
+namespace.default.link.runtime.shared_libs += libicui18n.so
+namespace.default.link.runtime.shared_libs += libicuuc.so
+namespace.default.link.runtime.shared_libs += libnativebridge.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
+namespace.default.link.runtime.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+# When libnetd_resolv.so can't be found in the default namespace, search for it
+# in the resolv namespace. Don't allow any other libraries from the resolv namespace
+# to be loaded in the default namespace.
+namespace.default.link.resolv.shared_libs = libnetd_resolv.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.runtime.visible = true
+
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# Need allow_all_shared_libs because libart.so can dlopen oat files in
+# /system/framework and /data.
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
+###############################################################################
+# "media" APEX namespace
+#
+# This namespace is for libraries within the media APEX.
+###############################################################################
+namespace.media.isolated = true
+namespace.media.visible = true
+
+namespace.media.search.paths = /apex/com.android.media/${LIB}
+namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+
+namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+
+namespace.media.links = default
+namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs += libcgrouprc.so
+namespace.media.link.default.shared_libs += libmediametrics.so
+namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = runtime,default
+namespace.conscrypt.link.runtime.shared_libs  = libandroidio.so
+namespace.conscrypt.link.default.shared_libs  = libc.so
+namespace.conscrypt.link.default.shared_libs += libm.so
+namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += liblog.so
+
+###############################################################################
+# "resolv" APEX namespace
+#
+# This namespace is for libraries within the resolv APEX.
+###############################################################################
+namespace.resolv.isolated = true
+namespace.resolv.visible = true
+
+namespace.resolv.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.links = default
+namespace.resolv.link.default.shared_libs  = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
+namespace.resolv.link.default.shared_libs += libm.so
+namespace.resolv.link.default.shared_libs += libdl.so
+namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += liblog.so
+namespace.resolv.link.default.shared_libs += libvndksupport.so
 
 ###############################################################################
 # "sphal" namespace
@@ -108,13 +242,17 @@
 # Note that there is no link from the default namespace to this namespace.
 ###############################################################################
 namespace.sphal.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
 namespace.sphal.visible = true
 
 namespace.sphal.search.paths  = /odm/${LIB}
 namespace.sphal.search.paths += /vendor/${LIB}
+namespace.sphal.search.paths += /vendor/${LIB}/hw
 
 namespace.sphal.permitted.paths  = /odm/${LIB}
 namespace.sphal.permitted.paths += /vendor/${LIB}
+namespace.sphal.permitted.paths += /system/vendor/${LIB}
 
 namespace.sphal.asan.search.paths  = /data/asan/odm/${LIB}
 namespace.sphal.asan.search.paths +=           /odm/${LIB}
@@ -127,17 +265,19 @@
 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
+# libs listed here can be used. Order is important here as the namespaces are
+# tried in this order. rs should be before vndk because both are capable
+# of loading libRS_internal.so
+namespace.sphal.links = rs,default,vndk
+
+# Renderscript gets separate namespace
+namespace.sphal.link.rs.shared_libs = libRS_internal.so
 
 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
 #
@@ -157,6 +297,7 @@
 
 namespace.rs.permitted.paths  = /odm/${LIB}
 namespace.rs.permitted.paths += /vendor/${LIB}
+namespace.rs.permitted.paths += /system/vendor/${LIB}
 namespace.rs.permitted.paths += /data
 
 namespace.rs.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
@@ -178,7 +319,7 @@
 
 namespace.rs.links = default,vndk
 
-namespace.rs.link.default.shared_libs  =  %LLNDK_LIBRARIES%
+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.
@@ -192,6 +333,8 @@
 # This namespace is exclusively for vndk-sp libs.
 ###############################################################################
 namespace.vndk.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
 namespace.vndk.visible = true
 
 namespace.vndk.search.paths  = /odm/${LIB}/vndk-sp
@@ -202,6 +345,8 @@
 namespace.vndk.permitted.paths += /odm/${LIB}/egl
 namespace.vndk.permitted.paths += /vendor/${LIB}/hw
 namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+namespace.vndk.permitted.paths += /system/vendor/${LIB}/hw
+namespace.vndk.permitted.paths += /system/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
 
@@ -227,7 +372,7 @@
 # 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
+namespace.vndk.links = default,sphal,runtime
 
 # 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
@@ -235,9 +380,12 @@
 namespace.vndk.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.vndk.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
+namespace.vndk.link.runtime.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
@@ -245,7 +393,7 @@
 # (LL-NDK only) access.
 ###############################################################################
 [vendor]
-additional.namespaces = system,vndk
+additional.namespaces = runtime,system,vndk%VNDK_IN_SYSTEM_NS%
 
 ###############################################################################
 # "default" namespace
@@ -256,6 +404,9 @@
 # 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.
+#
+# '#VNDK27#' TAG is only for building ld.config.27.txt for backward
+# compatibility. (TODO:b/123390078) Move them to a separate file.
 ###############################################################################
 namespace.default.isolated = true
 namespace.default.visible = true
@@ -265,23 +416,48 @@
 
 namespace.default.permitted.paths  = /odm
 namespace.default.permitted.paths += /vendor
+namespace.default.permitted.paths += /system/vendor
+#VNDK27#namespace.default.search.paths += /vendor/${LIB}/hw
+#VNDK27#namespace.default.search.paths += /vendor/${LIB}/egl
 
 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}
+#VNDK27#namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/hw
+#VNDK27#namespace.default.asan.search.paths +=           /vendor/${LIB}/hw
+#VNDK27#namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/egl
+#VNDK27#namespace.default.asan.search.paths +=           /vendor/${LIB}/egl
 
 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.links = system,vndk%VNDK_IN_SYSTEM_NS%,runtime
+namespace.default.link.runtime.shared_libs = %SANITIZER_RUNTIME_LIBRARIES%
+namespace.default.link.system.shared_libs  = %LLNDK_LIBRARIES%
+namespace.default.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+namespace.default.link.vndk_in_system.shared_libs = %VNDK_USING_CORE_VARIANT_LIBRARIES%
 namespace.default.link.vndk.shared_libs  = %VNDK_SAMEPROCESS_LIBRARIES%
 namespace.default.link.vndk.shared_libs += %VNDK_CORE_LIBRARIES%
 
 ###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.runtime.isolated = true
+
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = system
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
+namespace.runtime.link.system.allow_all_shared_libs = true
+
+###############################################################################
 # "vndk" namespace
 #
 # This namespace is where VNDK and VNDK-SP libraries are loaded for
@@ -311,13 +487,20 @@
 
 # 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
+# Android releases.  The links here should be identical to that of the
+# 'vndk_in_system' namespace, except for the link between 'vndk' and
+# 'vndk_in_system'.
+namespace.vndk.links = system,default%VNDK_IN_SYSTEM_NS%,runtime
+
 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
 
+namespace.vndk.link.runtime.shared_libs = %SANITIZER_RUNTIME_LIBRARIES%
+
+namespace.vndk.link.vndk_in_system.shared_libs = %VNDK_USING_CORE_VARIANT_LIBRARIES%
+
 ###############################################################################
 # "system" namespace
 #
@@ -328,21 +511,201 @@
 
 namespace.system.search.paths  = /system/${LIB}
 namespace.system.search.paths += /%PRODUCT%/${LIB}
+namespace.system.search.paths += /%PRODUCT_SERVICES%/${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.system.asan.search.paths +=           /%PRODUCT%/${LIB}
+namespace.system.asan.search.paths += /data/asan/product_services/${LIB}
+namespace.system.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
+
+namespace.system.links = runtime
+namespace.system.link.runtime.shared_libs  = libdexfile_external.so
+namespace.system.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
+namespace.system.link.runtime.shared_libs += libicui18n.so
+namespace.system.link.runtime.shared_libs += libicuuc.so
+namespace.system.link.runtime.shared_libs += libnativebridge.so
+namespace.system.link.runtime.shared_libs += libnativehelper.so
+namespace.system.link.runtime.shared_libs += libnativeloader.so
+# Workaround for b/124772622
+namespace.system.link.runtime.shared_libs += libandroidicu.so
+namespace.system.link.runtime.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+###############################################################################
+# "vndk_in_system" namespace
+#
+# This namespace is where no-vendor-variant VNDK libraries are loaded for a
+# vendor process.  Note that we do not simply export these libraries from
+# "system" namespace, because in some case both the core variant and the
+# vendor variant of a VNDK library may be loaded.  In such case, we do not
+# want to eliminate double-loading because doing so means the global states
+# of the library would be shared.
+#
+# Only the no-vendor-variant VNDK libraries are whitelisted in this namespace.
+# This is to ensure that we do not load libraries needed by no-vendor-variant
+# VNDK libraries into vndk_in_system namespace.
+###############################################################################
+namespace.vndk_in_system.isolated = true
+namespace.vndk_in_system.visible = true
+
+# The search paths here should be kept the same as that of the 'system'
+# namespace.
+namespace.vndk_in_system.search.paths  = /system/${LIB}
+namespace.vndk_in_system.search.paths += /%PRODUCT%/${LIB}
+namespace.vndk_in_system.search.paths += /%PRODUCT_SERVICES%/${LIB}
+
+namespace.vndk_in_system.asan.search.paths  = /data/asan/system/${LIB}
+namespace.vndk_in_system.asan.search.paths +=           /system/${LIB}
+namespace.vndk_in_system.asan.search.paths += /data/asan/product/${LIB}
+namespace.vndk_in_system.asan.search.paths +=           /%PRODUCT%/${LIB}
+namespace.vndk_in_system.asan.search.paths += /data/asan/product_services/${LIB}
+namespace.vndk_in_system.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
+
+namespace.vndk_in_system.whitelisted = %VNDK_USING_CORE_VARIANT_LIBRARIES%
+
+# The links here should be identical to that of the 'vndk' namespace, with the
+# following exception:
+#   1. 'vndk_in_system' needs to be freely linked back to 'vndk'.
+#   2. 'vndk_in_system' does not need to link to 'default', as any library that
+#      requires anything vendor would not be a vndk_in_system library.
+namespace.vndk_in_system.links = vndk,system,runtime
+namespace.vndk_in_system.link.runtime.shared_libs = %SANITIZER_RUNTIME_LIBRARIES%
+
+namespace.vndk_in_system.link.system.shared_libs  = %LLNDK_LIBRARIES%
+namespace.vndk_in_system.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+namespace.vndk_in_system.link.vndk.allow_all_shared_libs = true
+
+
+###############################################################################
+# Namespace config for native tests that need access to both system and vendor
+# libraries. This replicates the default linker config (done by
+# init_default_namespace_no_config in bionic/linker/linker.cpp), except that it
+# includes the requisite namespace setup for APEXes.
+###############################################################################
+[unrestricted]
+additional.namespaces = runtime,media,conscrypt,resolv
+
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.default.visible = true
+
+namespace.default.search.paths  = /system/${LIB}
+namespace.default.search.paths += /odm/${LIB}
+namespace.default.search.paths += /vendor/${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}
+
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
+namespace.default.links = runtime,resolv
+namespace.default.link.runtime.shared_libs  = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
+namespace.default.link.runtime.shared_libs += libicui18n.so
+namespace.default.link.runtime.shared_libs += libicuuc.so
+namespace.default.link.runtime.shared_libs += libnativebridge.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
+namespace.default.link.runtime.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+namespace.default.link.resolv.shared_libs = libnetd_resolv.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.runtime.visible = true
+
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
+###############################################################################
+# "media" APEX namespace
+#
+# This namespace is for libraries within the media APEX.
+###############################################################################
+namespace.media.isolated = true
+namespace.media.visible = true
+
+namespace.media.search.paths = /apex/com.android.media/${LIB}
+namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+
+namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+
+namespace.media.links = default
+namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs += libmediametrics.so
+namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = runtime,default
+namespace.conscrypt.link.runtime.shared_libs  = libandroidio.so
+namespace.conscrypt.link.default.shared_libs  = libc.so
+namespace.conscrypt.link.default.shared_libs += libm.so
+namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += liblog.so
+
+###############################################################################
+# "resolv" APEX namespace
+#
+# This namespace is for libraries within the resolv APEX.
+###############################################################################
+namespace.resolv.isolated = true
+namespace.resolv.visible = true
+
+namespace.resolv.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.links = default
+namespace.resolv.link.default.shared_libs  = libc.so
+namespace.resolv.link.default.shared_libs += libm.so
+namespace.resolv.link.default.shared_libs += libdl.so
+namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += liblog.so
+
 
 ###############################################################################
 # 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.
+# Only default namespace is defined and default 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 postinstall 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 += /%PRODUCT_SERVICES%/${LIB}
+
+namespace.default.link.runtime.shared_libs = %SANITIZER_RUNTIME_LIBRARIES%
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index db65c14..9212408 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -7,7 +7,7 @@
 # absolute path of an executable is selected.
 dir.system = /system/bin/
 dir.system = /system/xbin/
-dir.system = /product/bin/
+dir.system = /%PRODUCT%/bin/
 
 dir.vendor = /odm/bin/
 dir.vendor = /vendor/bin/
@@ -20,15 +20,21 @@
 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.unrestricted = /data/nativetest/unrestricted
+dir.unrestricted = /data/nativetest64/unrestricted
+
+# TODO(b/123864775): Ensure tests are run from /data/nativetest{,64} or (if
+# necessary) the unrestricted subdirs above. Then clean this up.
+dir.unrestricted = /data/local/tmp
 
 dir.postinstall = /postinstall
 
+# Fallback entry to provide APEX namespace lookups for binaries anywhere else.
+# This must be last.
+dir.system = /data
+
 [system]
-additional.namespaces = sphal,vndk,rs
+additional.namespaces = runtime,conscrypt,media,resolv,sphal,vndk,rs
 
 ###############################################################################
 # "default" namespace
@@ -37,11 +43,15 @@
 # partitions are also allowed temporarily.
 ###############################################################################
 namespace.default.isolated = false
+# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
+# libart.
+namespace.default.visible = true
 
 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.search.paths += /%PRODUCT%/${LIB}
+namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
 
 namespace.default.asan.search.paths  = /data/asan/system/${LIB}
 namespace.default.asan.search.paths +=           /system/${LIB}
@@ -49,8 +59,112 @@
 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}
+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}
+
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
+# If a shared library or an executable requests a shared library that
+# cannot be loaded into the default namespace, the dynamic linker tries
+# to load the shared library from the runtime namespace. And then, if the
+# shared library cannot be loaded from the runtime namespace either, the
+# dynamic linker tries to load the shared library from the resolv namespace.
+# Finally, if all attempts fail, the dynamic linker returns an error.
+namespace.default.links = runtime,resolv
+namespace.default.link.runtime.shared_libs  = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
+namespace.default.link.runtime.shared_libs += libicui18n.so
+namespace.default.link.runtime.shared_libs += libicuuc.so
+namespace.default.link.runtime.shared_libs += libnativebridge.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
+
+# When libnetd_resolv.so can't be found in the default namespace, search for it
+# in the resolv namespace. Don't allow any other libraries from the resolv namespace
+# to be loaded in the default namespace.
+namespace.default.link.resolv.shared_libs = libnetd_resolv.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace pulls in externally accessible libs from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.runtime.visible = true
+
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# Need allow_all_shared_libs because libart.so can dlopen oat files in
+# /system/framework and /data.
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
+###############################################################################
+# "media" APEX namespace
+#
+# This namespace is for libraries within the media APEX.
+###############################################################################
+namespace.media.isolated = true
+namespace.media.visible = true
+
+namespace.media.search.paths = /apex/com.android.media/${LIB}
+namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+
+namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+
+namespace.media.links = default
+namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs += libmediametrics.so
+namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = runtime,default
+namespace.conscrypt.link.runtime.shared_libs  = libandroidio.so
+namespace.conscrypt.link.default.shared_libs  = libc.so
+namespace.conscrypt.link.default.shared_libs += libm.so
+namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += liblog.so
+
+###############################################################################
+# "resolv" APEX namespace
+#
+# This namespace is for libraries within the resolv APEX.
+###############################################################################
+namespace.resolv.isolated = true
+namespace.resolv.visible = true
+
+namespace.resolv.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.links = default
+namespace.resolv.link.default.shared_libs  = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
+namespace.resolv.link.default.shared_libs += libm.so
+namespace.resolv.link.default.shared_libs += libdl.so
+namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += liblog.so
+namespace.resolv.link.default.shared_libs += libvndksupport.so
 
 ###############################################################################
 # "sphal" namespace
@@ -66,13 +180,17 @@
 # Note that there is no link from the default namespace to this namespace.
 ###############################################################################
 namespace.sphal.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
 namespace.sphal.visible = true
 
 namespace.sphal.search.paths  = /odm/${LIB}
 namespace.sphal.search.paths += /vendor/${LIB}
+namespace.sphal.search.paths += /vendor/${LIB}/hw
 
 namespace.sphal.permitted.paths  = /odm/${LIB}
 namespace.sphal.permitted.paths += /vendor/${LIB}
+namespace.sphal.permitted.paths += /system/vendor/${LIB}
 
 namespace.sphal.asan.search.paths  = /data/asan/odm/${LIB}
 namespace.sphal.asan.search.paths +=           /odm/${LIB}
@@ -85,17 +203,19 @@
 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
+# libs listed here can be used. Order is important here as the namespaces are
+# tried in this order. rs should be before vndk because both are capable
+# of loading libRS_internal.so
+namespace.sphal.links = rs,default,vndk
+
+# Renderscript gets separate namespace
+namespace.sphal.link.rs.shared_libs = libRS_internal.so
 
 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
 #
@@ -115,6 +235,7 @@
 
 namespace.rs.permitted.paths  = /odm/${LIB}
 namespace.rs.permitted.paths += /vendor/${LIB}
+namespace.rs.permitted.paths += /system/vendor/${LIB}
 namespace.rs.permitted.paths += /data
 
 namespace.rs.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
@@ -136,7 +257,7 @@
 
 namespace.rs.links = default,vndk
 
-namespace.rs.link.default.shared_libs  =  %LLNDK_LIBRARIES%
+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.
@@ -150,6 +271,8 @@
 # This namespace is exclusively for vndk-sp libs.
 ###############################################################################
 namespace.vndk.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
 namespace.vndk.visible = true
 
 namespace.vndk.search.paths  = /odm/${LIB}/vndk-sp
@@ -160,6 +283,7 @@
 namespace.vndk.permitted.paths += /odm/${LIB}/egl
 namespace.vndk.permitted.paths += /vendor/${LIB}/hw
 namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+namespace.vndk.permitted.paths += /system/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
 
@@ -186,9 +310,11 @@
 # 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
@@ -196,6 +322,8 @@
 # (LL-NDK only) access.
 ###############################################################################
 [vendor]
+additional.namespaces = runtime
+
 namespace.default.isolated = false
 
 namespace.default.search.paths  = /odm/${LIB}
@@ -205,11 +333,12 @@
 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%
+# Access to system libraries is allowed
 namespace.default.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
 namespace.default.search.paths += /system/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
+namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
+namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
 
 namespace.default.asan.search.paths  = /data/asan/odm/${LIB}
 namespace.default.asan.search.paths +=           /odm/${LIB}
@@ -223,24 +352,167 @@
 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.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.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
+namespace.default.asan.search.paths +=           /system/${LIB}/vndk%VNDK_VER%
+
+namespace.default.links = runtime
+namespace.default.link.runtime.shared_libs  = libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
+namespace.default.link.runtime.shared_libs += libicui18n.so
+namespace.default.link.runtime.shared_libs += libicuuc.so
+namespace.default.link.runtime.shared_libs += libnativebridge.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+# Workaround for b/124772622
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.runtime.isolated = true
+
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
+###############################################################################
+# Namespace config for native tests that need access to both system and vendor
+# libraries. This replicates the default linker config (done by
+# init_default_namespace_no_config in bionic/linker/linker.cpp), except that it
+# includes the requisite namespace setup for APEXes.
+###############################################################################
+[unrestricted]
+additional.namespaces = runtime,media,conscrypt,resolv
+
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.default.visible = true
+
+namespace.default.search.paths  = /system/${LIB}
+namespace.default.search.paths += /odm/${LIB}
+namespace.default.search.paths += /vendor/${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}
+
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
+namespace.default.links = runtime,resolv
+namespace.default.link.runtime.shared_libs  = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
+namespace.default.link.runtime.shared_libs += libicui18n.so
+namespace.default.link.runtime.shared_libs += libicuuc.so
+namespace.default.link.runtime.shared_libs += libnativebridge.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
+namespace.default.link.runtime.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+namespace.default.link.resolv.shared_libs = libnetd_resolv.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.runtime.visible = true
+
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
+###############################################################################
+# "media" APEX namespace
+#
+# This namespace is for libraries within the media APEX.
+###############################################################################
+namespace.media.isolated = true
+namespace.media.visible = true
+
+namespace.media.search.paths = /apex/com.android.media/${LIB}
+namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+
+namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+
+namespace.media.links = default
+namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs += libmediametrics.so
+namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = runtime,default
+namespace.conscrypt.link.runtime.shared_libs  = libandroidio.so
+namespace.conscrypt.link.default.shared_libs  = libc.so
+namespace.conscrypt.link.default.shared_libs += libm.so
+namespace.conscrypt.link.default.shared_libs += libdl.so
+
+###############################################################################
+# "resolv" APEX namespace
+#
+# This namespace is for libraries within the resolv APEX.
+###############################################################################
+namespace.resolv.isolated = true
+namespace.resolv.visible = true
+
+namespace.resolv.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.links = default
+namespace.resolv.link.default.shared_libs  = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
+namespace.resolv.link.default.shared_libs += libm.so
+namespace.resolv.link.default.shared_libs += libdl.so
+namespace.resolv.link.default.shared_libs += libbinder_ndk.so
 
 ###############################################################################
 # 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.
+# Only default namespace is defined and default 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 postinstall 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 += /%PRODUCT%/${LIB}
+namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index e20b95d..d8f6095 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -1,6 +1,7 @@
 # See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
 libandroid.so
 libaaudio.so
+libbinder_ndk.so
 libc.so
 libcamera2ndk.so
 libdl.so
diff --git a/rootdir/etc/public.libraries.iot.txt b/rootdir/etc/public.libraries.iot.txt
new file mode 100644
index 0000000..20905bf
--- /dev/null
+++ b/rootdir/etc/public.libraries.iot.txt
@@ -0,0 +1,28 @@
+# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
+libandroid.so
+libandroidthings.so
+libaaudio.so
+libbinder_ndk.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 3c46094..4ece5b5 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -1,6 +1,7 @@
 # See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
 libandroid.so
 libaaudio.so
+libbinder_ndk.so
 libc.so
 libcamera2ndk.so
 libdl.so
diff --git a/rootdir/fsverity_init.sh b/rootdir/fsverity_init.sh
new file mode 100644
index 0000000..4fee15f
--- /dev/null
+++ b/rootdir/fsverity_init.sh
@@ -0,0 +1,32 @@
+#!/system/bin/sh
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Enforce fsverity signature checking
+echo 1 > /proc/sys/fs/verity/require_signatures
+
+# Load all keys
+for cert in /product/etc/security/fsverity/*.der; do
+  /system/bin/mini-keyctl padd asymmetric fsv_product .fs-verity < "$cert" ||
+    log -p e -t fsverity_init "Failed to load $cert"
+done
+
+DEBUGGABLE=$(getprop ro.debuggable)
+if [ $DEBUGGABLE != "1" ]; then
+  # Prevent future key links to .fs-verity keyring
+  /system/bin/mini-keyctl restrict_keyring .fs-verity ||
+    log -p e -t fsverity_init "Failed to restrict .fs-verity keyring"
+fi
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 2e2ab74..455c9a8 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -1,13 +1,17 @@
 # set up the global environment
-on init
+on early-init
     export ANDROID_BOOTLOGO 1
     export ANDROID_ROOT /system
     export ANDROID_ASSETS /system/app
     export ANDROID_DATA /data
     export ANDROID_STORAGE /storage
+    export ANDROID_RUNTIME_ROOT /apex/com.android.runtime
+    export ANDROID_TZDATA_ROOT /apex/com.android.tzdata
     export EXTERNAL_STORAGE /sdcard
     export ASEC_MOUNTPOINT /mnt/asec
     export BOOTCLASSPATH %BOOTCLASSPATH%
+    export DEX2OATBOOTCLASSPATH %DEX2OATBOOTCLASSPATH%
     export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
     %EXPORT_GLOBAL_ASAN_OPTIONS%
     %EXPORT_GLOBAL_GCOV_OPTIONS%
+    %EXPORT_GLOBAL_HWASAN_OPTIONS%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index f4b2082..96ffa69 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -11,10 +11,8 @@
 import /init.usb.configfs.rc
 import /init.${ro.zygote}.rc
 
+# Cgroups are mounted right before early-init using list from /etc/cgroups.json
 on early-init
-    # Set init and its forked children's oom_adj.
-    write /proc/1/oom_score_adj -1000
-
     # Disable sysrq from keyboard
     write /proc/sys/kernel/sysrq 0
 
@@ -24,26 +22,39 @@
     # Set the security context of /postinstall if present.
     restorecon /postinstall
 
-    # Mount cgroup mount point for cpu accounting
-    mount cgroup none /acct nodev noexec nosuid cpuacct
     mkdir /acct/uid
 
-    # root memory control cgroup, used by lmkd
-    mkdir /dev/memcg 0700 root system
-    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
 
+    # set RLIMIT_NICE to allow priorities from 19 to -20
+    setrlimit nice 40 40
+
+    # Allow up to 32K FDs per process
+    setrlimit nofile 32768 32768
+
     start ueventd
 
+    # Run apexd-bootstrap so that APEXes that provide critical libraries
+    # become available. Note that this is executed as exec_start to ensure that
+    # the libraries are available to the processes started after this statement.
+    exec_start apexd-bootstrap
+
 on init
     sysclktz 0
 
     # Mix device-specific information into the entropy pool
     copy /proc/cmdline /dev/urandom
-    copy /default.prop /dev/urandom
+    copy /system/etc/prop.default /dev/urandom
+
+    symlink /proc/self/fd/0 /dev/stdin
+    symlink /proc/self/fd/1 /dev/stdout
+    symlink /proc/self/fd/2 /dev/stderr
 
     symlink /system/bin /bin
     symlink /system/etc /etc
@@ -55,8 +66,6 @@
     symlink /system/vendor /vendor
 
     # Create energy-aware scheduler tuning nodes
-    mkdir /dev/stune
-    mount cgroup none /dev/stune nodev noexec nosuid schedtune
     mkdir /dev/stune/foreground
     mkdir /dev/stune/background
     mkdir /dev/stune/top-app
@@ -77,6 +86,21 @@
     chmod 0664 /dev/stune/top-app/tasks
     chmod 0664 /dev/stune/rt/tasks
 
+    # Create blkio group and apply initial settings.
+    # This feature needs kernel to support it, and the
+    # device's init.rc must actually set the correct values.
+    mkdir /dev/blkio/background
+    chown system system /dev/blkio
+    chown system system /dev/blkio/background
+    chown system system /dev/blkio/tasks
+    chown system system /dev/blkio/background/tasks
+    chmod 0664 /dev/blkio/tasks
+    chmod 0664 /dev/blkio/background/tasks
+    write /dev/blkio/blkio.weight 1000
+    write /dev/blkio/background/blkio.weight 500
+    write /dev/blkio/blkio.group_idle 0
+    write /dev/blkio/background/blkio.group_idle 0
+
     restorecon_recursive /mnt
 
     mount configfs none /config nodev noexec nosuid
@@ -101,6 +125,8 @@
     mkdir /mnt/runtime/read/self 0755 root root
     mkdir /mnt/runtime/write 0755 root root
     mkdir /mnt/runtime/write/self 0755 root root
+    mkdir /mnt/runtime/full 0755 root root
+    mkdir /mnt/runtime/full/self 0755 root root
 
     # Symlink to keep legacy apps working in multi-user world
     symlink /storage/self/primary /sdcard
@@ -148,8 +174,6 @@
     chmod 0400 /proc/net/fib_trie
 
     # Create cgroup mount points for process groups
-    mkdir /dev/cpuctl
-    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
@@ -157,9 +181,6 @@
     write /dev/cpuctl/cpu.rt_runtime_us 950000
 
     # sets up initial cpusets for ActivityManager
-    mkdir /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
@@ -210,6 +231,9 @@
     chmod 0664 /dev/cpuset/restricted/tasks
     chmod 0664 /dev/cpuset/tasks
 
+    # make the PSI monitor accessible to others
+    chown system system /proc/pressure/memory
+    chmod 0664 /proc/pressure/memory
 
     # qtaguid will limit access to specific data based on group memberships.
     #   net_bw_acct grants impersonation of socket owners.
@@ -221,8 +245,6 @@
     # 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
@@ -233,6 +255,8 @@
 
     # pstore/ramoops previous console log
     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
@@ -249,16 +273,29 @@
 
     export DOWNLOAD_CACHE /data/cache
 
-    # set RLIMIT_NICE to allow priorities from 19 to -20
-    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
     write /sys/class/leds/vibrator/trigger "transient"
 
+    # This is used by Bionic to select optimized routines.
+    write /dev/cpu_variant:${ro.bionic.arch} ${ro.bionic.cpu_variant}
+    chmod 0444 /dev/cpu_variant:${ro.bionic.arch}
+    write /dev/cpu_variant:${ro.bionic.2nd_arch} ${ro.bionic.2nd_cpu_variant}
+    chmod 0444 /dev/cpu_variant:${ro.bionic.2nd_arch}
+
+    # Allow system processes to read / write power state.
+    chown system system /sys/power/state
+    chown system system /sys/power/wakeup_count
+    chmod 0660 /sys/power/state
+
+    # Start logd before any other services run to ensure we capture all of their logs.
+    start logd
+
+    # Start essential services.
+    start servicemanager
+    start hwservicemanager
+    start vndservicemanager
+
 # Healthd can trigger a full boot from charger mode by signaling this
 # property when the power button is held.
 on property:sys.boot_from_charger_mode=1
@@ -296,36 +333,28 @@
     # /data, which in turn can only be loaded when system properties are present.
     trigger post-fs-data
 
-    # Now we can start zygote for devices with file based encryption
-    trigger zygote-start
-
     # Load persist properties and override properties (if enabled) from /data.
     trigger load_persist_props_action
 
+    # Now we can start zygote for devices with file based encryption
+    trigger zygote-start
+
     # Remove a file to wake up anything waiting for firmware.
     trigger firmware_mounts_complete
 
     trigger early-boot
     trigger boot
 
+on early-fs
+    # Once metadata has been mounted, we'll need vold to deal with userdata checkpointing
+    start vold
+
 on post-fs
-    # Load properties from
-    #     /system/build.prop,
-    #     /odm/build.prop,
-    #     /vendor/build.prop and
-    #     /factory/factory.prop
-    load_system_props
-    # start essential services
-    start logd
-    start servicemanager
-    start hwservicemanager
-    start vndservicemanager
+    exec - system system -- /system/bin/vdc checkpoint markBootAttempt
 
     # Once everything is setup, no need to modify /.
-    # The bind+ro combination avoids modifying any other mount flags.
-    mount rootfs rootfs / remount bind ro
-    # Mount shared so changes propagate into child namespaces
-    mount rootfs rootfs / shared rec
+    # The bind+remount combination allows this to work in containers.
+    mount rootfs rootfs / remount bind ro nodev
     # Mount default storage into root namespace
     mount none /mnt/runtime/default /storage bind rec
     mount none none /storage slave rec
@@ -372,7 +401,10 @@
     restorecon_recursive /metadata
     mkdir /metadata/vold
     chmod 0700 /metadata/vold
+    mkdir /metadata/password_slots 0771 root system
 
+    mkdir /metadata/apex 0700 root system
+    mkdir /metadata/apex/sessions 0700 root system
 on late-fs
     # Ensure that tracefs has the correct permissions.
     # This does not work correctly if it is called in post-fs.
@@ -386,6 +418,12 @@
     exec_start update_verifier_nonencrypted
 
 on post-fs-data
+    mark_post_data
+
+    # Start checkpoint before we touch data
+    start vold
+    exec - system system -- /system/bin/vdc checkpoint prepareCheckpoint
+
     # We chown/chmod /data again so because mount is run as root + defaults
     chown system system /data
     chmod 0771 /data
@@ -393,7 +431,6 @@
     restorecon /data
 
     # Make sure we have the device encryption key.
-    start vold
     installkey /data
 
     # Start bootcharting as soon as possible after the data partition is
@@ -401,6 +438,22 @@
     mkdir /data/bootchart 0755 shell shell
     bootchart start
 
+    # Load fsverity keys. This needs to happen before apexd, as post-install of
+    # APEXes may rely on keys.
+    exec -- /system/bin/fsverity_init
+
+    # Make sure that apexd is started in the default namespace
+    enter_default_mount_ns
+
+    # /data/apex is now available. Start apexd to scan and activate APEXes.
+    mkdir /data/apex 0750 root system
+    mkdir /data/apex/active 0750 root system
+    mkdir /data/apex/backup 0700 root system
+    mkdir /data/apex/hashtree 0700 root system
+    mkdir /data/apex/sessions 0700 root system
+    mkdir /data/app-staging 0750 system system
+    start apexd
+
     # Avoid predictable entropy pool. Carry over entropy from previous boot.
     copy /data/system/entropy.dat /dev/urandom
 
@@ -467,6 +520,8 @@
     mkdir /data/misc/profman 0770 system shell
     mkdir /data/misc/gcov 0770 root root
 
+    mkdir /data/preloads 0775 system system
+
     mkdir /data/vendor 0771 root root
     mkdir /data/vendor_ce 0771 root root
     mkdir /data/vendor_de 0771 root root
@@ -524,6 +579,7 @@
     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
 
@@ -545,13 +601,18 @@
     mkdir /data/cache/backup_stage 0700 system system
     mkdir /data/cache/backup 0700 system system
 
+    # Wait for apexd to finish activating APEXes before starting more processes.
+    wait_for_prop apexd.status ready
+    parse_apex_configs
+
     init_user0
 
     # Set SELinux security contexts on upgrade or policy update.
     restorecon --recursive --skip-ce /data
 
-    # Check any timezone data in /data is newer than the copy in /system, delete if not.
-    exec - system system -- /system/bin/tzdatacheck /system/usr/share/zoneinfo /data/misc/zoneinfo
+    # Check any timezone data in /data is newer than the copy in the time zone data
+    # module, delete if not.
+    exec - system system -- /system/bin/tzdatacheck /apex/com.android.tzdata/etc/tz /data/misc/zoneinfo
 
     # If there is no post-fs-data action in the init.<device>.rc file, you
     # must uncomment this line, otherwise encrypted filesystems
@@ -559,6 +620,15 @@
     # Set indication (checked by vold) that we have finished this action
     #setprop vold.post_fs_data_done 1
 
+    # sys.memfd_use set to false by default, which keeps it disabled
+    # until it is confirmed that apps and vendor processes don't make
+    # IOCTLs on ashmem fds any more.
+    setprop sys.use_memfd false
+
+    # Set fscklog permission
+    chown root system /dev/fscklogs/log
+    chmod 0770 /dev/fscklogs/log
+
 # It is recommended to put unnecessary data/ initialization from post-fs-data
 # to start-zygote in device's init.rc to unblock zygote start.
 on zygote-start && property:ro.crypto.state=unencrypted
@@ -599,6 +669,12 @@
     write /proc/sys/vm/dirty_expire_centisecs 200
     write /proc/sys/vm/dirty_background_ratio  5
 
+    # F2FS tuning. Set cp_interval larger than dirty_expire_centisecs
+    # to avoid power consumption when system becomes mostly idle. Be careful
+    # to make it too large, since it may bring userdata loss, if they
+    # are not aware of using fsync()/sync() to prepare sudden power-cut.
+    write /sys/fs/f2fs/${dev.mnt.blk.data}/cp_interval 200
+
     # Permissions for System Server and daemons.
     chown radio system /sys/android_power/state
     chown radio system /sys/android_power/request_state
@@ -606,11 +682,8 @@
     chown radio system /sys/android_power/acquire_partial_wake_lock
     chown radio system /sys/android_power/release_wake_lock
     chown system system /sys/power/autosleep
-    chown system system /sys/power/state
-    chown system system /sys/power/wakeup_count
     chown radio wakelock /sys/power/wake_lock
     chown radio wakelock /sys/power/wake_unlock
-    chmod 0660 /sys/power/state
     chmod 0660 /sys/power/wake_lock
     chmod 0660 /sys/power/wake_unlock
 
@@ -685,9 +758,6 @@
 on charger
     class_start charger
 
-on property:vold.decrypt=trigger_reset_main
-    class_reset main
-
 on property:vold.decrypt=trigger_load_persist_props
     load_persist_props
     start logd
@@ -701,14 +771,20 @@
     class_start main
 
 on property:vold.decrypt=trigger_restart_framework
-    stop surfaceflinger
-    start surfaceflinger
+    # A/B update verifier that marks a successful boot.
+    exec_start update_verifier
+    class_start_post_data hal
+    class_start_post_data core
     class_start main
     class_start late_start
+    setprop service.bootanim.exit 0
+    start bootanim
 
 on property:vold.decrypt=trigger_shutdown_framework
     class_reset late_start
     class_reset main
+    class_reset_post_data core
+    class_reset_post_data hal
 
 on property:sys.boot_completed=1
     bootchart stop
@@ -725,6 +801,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
@@ -735,7 +814,7 @@
 
 ## Daemon processes to be run by init.
 ##
-service ueventd /sbin/ueventd
+service ueventd /system/bin/ueventd
     class core
     critical
     seclabel u:r:ueventd:s0
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index ac87979..f8e680d 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -4,6 +4,7 @@
     user root
     group root readproc reserved_disk
     socket zygote stream 660 root system
+    socket blastula_pool stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index a535846..0235370 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -4,6 +4,7 @@
     user root
     group root readproc reserved_disk
     socket zygote stream 660 root system
+    socket blastula_pool stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
@@ -19,5 +20,6 @@
     user root
     group root readproc reserved_disk
     socket zygote_secondary stream 660 root system
+    socket blastula_pool_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 6fc810b..3f3cc15 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -4,6 +4,7 @@
     user root
     group root readproc reserved_disk
     socket zygote stream 660 root system
+    socket blastula_pool stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 7ddd52e..fae38c9 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -4,6 +4,7 @@
     user root
     group root readproc reserved_disk
     socket zygote stream 660 root system
+    socket blastula_pool stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
@@ -19,5 +20,6 @@
     user root
     group root readproc reserved_disk
     socket zygote_secondary stream 660 root system
+    socket blastula_pool_secondary stream 660 root system
     onrestart restart zygote
     writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/ld_config_backward_compatibility_check.py b/rootdir/ld_config_backward_compatibility_check.py
new file mode 100755
index 0000000..1a27578
--- /dev/null
+++ b/rootdir/ld_config_backward_compatibility_check.py
@@ -0,0 +1,177 @@
+#!/usr/bin/env python
+#
+# 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 glob
+import os.path
+import re
+import sys
+
+PREBUILTS_VNDK_DIR = "prebuilts/vndk"
+VENDOR_DIRECTORIES = ('/vendor', '/odm')
+
+def find_latest_vndk_snapshot_version():
+  """Returns latest vndk snapshot version in current source tree.
+  It will skip the test if the snapshot directories are not found.
+
+  Returns:
+    latest_version: string
+  """
+  vndk_dir_list = glob.glob(PREBUILTS_VNDK_DIR + "/v*")
+  if not vndk_dir_list:
+    """Exit without error because we may have source trees that do not include
+    VNDK snapshot directories in it.
+    """
+    sys.exit(0)
+  vndk_ver_list = [re.match(r".*/v(\d+)", vndk_dir).group(1)
+                                          for vndk_dir in vndk_dir_list]
+  latest_version = max(vndk_ver_list)
+  if latest_version == '27':
+    """Exit without error because VNDK v27 is not using ld.config.txt template
+    """
+    sys.exit(0)
+  return latest_version
+
+def get_vendor_configuration(ld_config_file):
+  """Reads the ld.config.txt file to parse the namespace configurations.
+  It finds the configurations that include vendor directories.
+
+  Args:
+    ld_config_file: string, path (relative to build top) of the ld.config.txt
+                    file.
+  Returns:
+    configs: dict{string:[string]}, dictionary of namespace configurations.
+             it has 'section + property' names as keys and the directory list
+             as values.
+  """
+  try:
+    conf_file = open(ld_config_file)
+  except IOError:
+    print("error: could not read %s" % ld_config_file)
+    sys.exit(1)
+
+  configs = dict()
+  current_section = None
+
+  with conf_file:
+    for line in conf_file:
+      # ignore comments
+      found = line.find('#')
+      if found != -1:
+        line = line[:found]
+      line = line.strip()
+      if not line:
+        continue
+
+      if line[0] == '[' and line[-1] == ']':
+        # new section started
+        current_section = line[1:-1]
+        continue
+
+      if current_section == None:
+        continue
+
+      found = line.find('+=')
+      opr_len = 2
+      if found == -1:
+        found = line.find('=')
+        opr_len = 1
+      if found == -1:
+        continue
+
+      namespace = line[:found].strip()
+      if not namespace.endswith(".paths"):
+        # check ".paths" only
+        continue
+      namespace = '[' + current_section + ']' + namespace
+      values = line[found + opr_len:].strip()
+      directories = values.split(':')
+
+      for directory in directories:
+        if any(vendor_dir in directory for vendor_dir in VENDOR_DIRECTORIES):
+          if namespace in configs:
+            configs[namespace].append(directory)
+          else:
+            configs[namespace] = [directory]
+
+  return configs
+
+def get_snapshot_config(version):
+  """Finds the ld.config.{version}.txt file from the VNDK snapshot directory.
+  In the vndk prebuilt directory (prebuilts/vndk/v{version}), it searches
+  {arch}/configs/ld.config.{version}.txt file, where {arch} is one of ('arm64',
+  'arm', 'x86_64', 'x86').
+
+  Args:
+    version: string, the VNDK snapshot version to search.
+  Returns:
+    ld_config_file: string, relative path to ld.config.{version}.txt
+  """
+  arch_list = ('arm64', 'arm', 'x86_64', 'x86')
+  for arch in arch_list:
+    ld_config_file = (PREBUILTS_VNDK_DIR
+                + "/v{0}/{1}/configs/ld.config.{0}.txt".format(version, arch))
+    if os.path.isfile(ld_config_file):
+      return ld_config_file
+  print("error: cannot find ld.config.{0}.txt file in snapshot v{0}"
+                                                        .format(version))
+  sys.exit(1)
+
+def check_backward_compatibility(ld_config, vndk_snapshot_version):
+  """Checks backward compatibility for current ld.config.txt file with the
+  old ld.config.txt file. If any of the vendor directories in the old namespace
+  configurations are missing, the test will fail. It is allowed to have new
+  vendor directories in current ld.config.txt file.
+
+  Args:
+    ld_config: string, relative path to current ld.config.txt file.
+    vndk_snapshot_version: string, the VNDK snapshot version that has an old
+                           ld.config.txt file to compare.
+  Returns:
+    result: bool, True if the current configuration is backward compatible.
+  """
+  current_config = get_vendor_configuration(ld_config)
+  old_config = get_vendor_configuration(
+                                get_snapshot_config(vndk_snapshot_version))
+  for namespace in old_config:
+    if namespace not in current_config:
+      print("error: cannot find %s which was provided in ld.config.%s.txt"
+                                        % (namespace, vndk_snapshot_version))
+      return False
+    for path in old_config[namespace]:
+      if not path in current_config[namespace]:
+        print("error: %s for %s in ld.config.%s.txt are missing in %s"
+                % (path, namespace, vndk_snapshot_version, ld_config))
+        return False
+  return True
+
+def main():
+  if len(sys.argv) != 2:
+    print ("Usage: %s target_ld_config_txt_file_name" % sys.argv[0])
+    sys.exit(1)
+
+  latest_vndk_snapshot_version = find_latest_vndk_snapshot_version()
+  if not check_backward_compatibility(sys.argv[1],
+                                          latest_vndk_snapshot_version):
+    print("error: %s has backward incompatible changes to old "
+          "vendor partition." % sys.argv[1])
+    sys.exit(1)
+
+  # Current ld.config.txt file is backward compatible
+  sys.exit(0)
+
+if __name__ == '__main__':
+  main()
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index b03d83b..451f5ad 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -1,5 +1,5 @@
-subsystem adf
-    devname uevent_devname
+firmware_directories /etc/firmware/ /odm/firmware/ /vendor/firmware/ /firmware/image/
+uevent_socket_rcvbuf_size 16M
 
 subsystem graphics
     devname uevent_devpath
@@ -9,26 +9,10 @@
     devname uevent_devpath
     dirname /dev/dri
 
-subsystem oncrpc
-    devname uevent_devpath
-    dirname /dev/oncrpc
-
-subsystem adsp
-    devname uevent_devpath
-    dirname /dev/adsp
-
-subsystem msm_camera
-    devname uevent_devpath
-    dirname /dev/msm_camera
-
 subsystem input
     devname uevent_devpath
     dirname /dev/input
 
-subsystem mtd
-    devname uevent_devpath
-    dirname /dev/mtd
-
 subsystem sound
     devname uevent_devpath
     dirname /dev/snd
@@ -56,73 +40,24 @@
 
 /dev/pmsg0                0222   root       log
 
-# the msm hw3d client device node is world writable/readable.
-/dev/msm_hw3dc            0666   root       root
-
-# gpu driver for adreno200 is globally accessible
-/dev/kgsl                 0666   root       root
-
 # kms driver for drm based gpu
 /dev/dri/*                0666   root       graphics
 
 # these should not be world writable
-/dev/diag                 0660   radio      radio
-/dev/diag_arm9            0660   radio      radio
-/dev/ttyMSM0              0600   bluetooth  bluetooth
 /dev/uhid                 0660   uhid       uhid
-/dev/uinput               0660   system     bluetooth
-/dev/alarm                0664   system     radio
+/dev/uinput               0660   uhid       uhid
 /dev/rtc0                 0640   system     system
 /dev/tty0                 0660   root       system
 /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
-/dev/pmem_adsp*           0660   system     audio
-/dev/pmem_camera*         0660   system     camera
-/dev/oncrpc/*             0660   root       system
-/dev/adsp/*               0660   system     audio
 /dev/snd/*                0660   system     audio
-/dev/mt9t013              0660   system     system
-/dev/msm_camera/*         0660   system     system
-/dev/akm8976_daemon       0640   compass    system
-/dev/akm8976_aot          0640   compass    system
-/dev/akm8973_daemon       0640   compass    system
-/dev/akm8973_aot          0640   compass    system
-/dev/bma150               0640   compass    system
-/dev/cm3602               0640   compass    system
-/dev/akm8976_pffd         0640   compass    system
-/dev/lightsensor          0640   system     system
-/dev/msm_pcm_out*         0660   system     audio
-/dev/msm_pcm_in*          0660   system     audio
-/dev/msm_pcm_ctl*         0660   system     audio
-/dev/msm_snd*             0660   system     audio
-/dev/msm_mp3*             0660   system     audio
-/dev/audience_a1026*      0660   system     audio
-/dev/tpa2018d1*           0660   system     audio
-/dev/msm_audpre           0660   system     audio
-/dev/msm_audio_ctl        0660   system     audio
-/dev/htc-acoustic         0660   system     audio
-/dev/vdec                 0660   system     audio
-/dev/q6venc               0660   system     audio
-/dev/snd/dsp              0660   system     audio
-/dev/snd/dsp1             0660   system     audio
-/dev/snd/mixer            0660   system     audio
-/dev/smd0                 0640   radio      radio
-/dev/qmi                  0640   radio      radio
-/dev/qmi0                 0640   radio      radio
-/dev/qmi1                 0640   radio      radio
-/dev/qmi2                 0640   radio      radio
 /dev/bus/usb/*            0660   root       usb
 /dev/mtp_usb              0660   root       mtp
 /dev/usb_accessory        0660   root       usb
 /dev/tun                  0660   system     vpn
 
 # CDMA radio interface MUX
-/dev/ts0710mux*           0640   radio      radio
 /dev/ppp                  0660   radio      vpn
 
 # sysfs properties
@@ -132,6 +67,3 @@
 /sys/devices/virtual/usb_composite/*   enable      0664  root   system
 /sys/devices/system/cpu/cpu*   cpufreq/scaling_max_freq   0664  system system
 /sys/devices/system/cpu/cpu*   cpufreq/scaling_min_freq   0664  system system
-
-# DVB API device nodes
-/dev/dvb*                 0660   root       system
diff --git a/rootdir/update_and_install_ld_config.mk b/rootdir/update_and_install_ld_config.mk
new file mode 100644
index 0000000..f62c3df
--- /dev/null
+++ b/rootdir/update_and_install_ld_config.mk
@@ -0,0 +1,184 @@
+#####################################################################
+# Builds linker config file, ld.config.txt, from the specified template
+# under $(LOCAL_PATH)/etc/*.
+#
+# Inputs:
+#   (expected to follow an include of $(BUILD_SYSTEM)/base_rules.mk)
+#   ld_config_template: template linker config file to use,
+#                       e.g. $(LOCAL_PATH)/etc/ld.config.txt
+#   vndk_version: version of the VNDK library lists used to update the
+#                 template linker config file, e.g. 28
+#   lib_list_from_prebuilts: should be set to 'true' if the VNDK library
+#                            lists should be read from /prebuilts/vndk/*
+#   libz_is_llndk: should be set to 'true' if libz must be included in
+#                  llndk and not in vndk-sp
+# Outputs:
+#   Builds and installs ld.config.$VER.txt or ld.config.vndk_lite.txt
+#####################################################################
+
+# Read inputs
+ld_config_template := $(strip $(ld_config_template))
+check_backward_compatibility := $(strip $(check_backward_compatibility))
+vndk_version := $(strip $(vndk_version))
+lib_list_from_prebuilts := $(strip $(lib_list_from_prebuilts))
+libz_is_llndk := $(strip $(libz_is_llndk))
+
+my_vndk_use_core_variant := $(TARGET_VNDK_USE_CORE_VARIANT)
+ifeq ($(lib_list_from_prebuilts),true)
+my_vndk_use_core_variant := false
+endif
+
+compatibility_check_script := \
+  $(LOCAL_PATH)/ld_config_backward_compatibility_check.py
+intermediates_dir := $(call intermediates-dir-for,ETC,$(LOCAL_MODULE))
+library_lists_dir := $(intermediates_dir)
+ifeq ($(lib_list_from_prebuilts),true)
+  library_lists_dir := prebuilts/vndk/v$(vndk_version)/$(TARGET_ARCH)/configs
+endif
+
+llndk_libraries_file := $(library_lists_dir)/llndk.libraries.$(vndk_version).txt
+vndksp_libraries_file := $(library_lists_dir)/vndksp.libraries.$(vndk_version).txt
+vndkcore_libraries_file := $(library_lists_dir)/vndkcore.libraries.txt
+vndkprivate_libraries_file := $(library_lists_dir)/vndkprivate.libraries.txt
+ifeq ($(my_vndk_use_core_variant),true)
+vndk_using_core_variant_libraries_file := $(library_lists_dir)/vndk_using_core_variant.libraries.$(vndk_version).txt
+endif
+
+sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\
+  $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(UBSAN_RUNTIME_LIBRARY) \
+  $(TSAN_RUNTIME_LIBRARY) \
+  $(2ND_ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(2ND_HWADDRESS_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 $(vndk_version),-$(vndk_version))
+
+ifneq ($(lib_list_from_prebuilts),true)
+ifeq ($(libz_is_llndk),true)
+  llndk_libraries_list := $(LLNDK_LIBRARIES) libz
+  vndksp_libraries_list := $(filter-out libz,$(VNDK_SAMEPROCESS_LIBRARIES))
+else
+  llndk_libraries_list := $(LLNDK_LIBRARIES)
+  vndksp_libraries_list := $(VNDK_SAMEPROCESS_LIBRARIES)
+endif
+
+# $(1): list of libraries
+# $(2): output file to write the list of libraries to
+define write-libs-to-file
+$(2): PRIVATE_LIBRARIES := $(1)
+$(2):
+	echo -n > $$@ && $$(foreach lib,$$(PRIVATE_LIBRARIES),echo $$(lib).so >> $$@;)
+endef
+$(eval $(call write-libs-to-file,$(llndk_libraries_list),$(llndk_libraries_file)))
+$(eval $(call write-libs-to-file,$(vndksp_libraries_list),$(vndksp_libraries_file)))
+$(eval $(call write-libs-to-file,$(VNDK_CORE_LIBRARIES),$(vndkcore_libraries_file)))
+$(eval $(call write-libs-to-file,$(VNDK_PRIVATE_LIBRARIES),$(vndkprivate_libraries_file)))
+ifeq ($(my_vndk_use_core_variant),true)
+$(eval $(call write-libs-to-file,$(VNDK_USING_CORE_VARIANT_LIBRARIES),$(vndk_using_core_variant_libraries_file)))
+endif
+endif # ifneq ($(lib_list_from_prebuilts),true)
+
+# Given a file with a list of libs, filter-out the VNDK private libraries
+# and write resulting list to a new file in "a:b:c" format
+#
+# $(1): libs file from which to filter-out VNDK private libraries
+# $(2): output file with the filtered list of lib names
+$(LOCAL_BUILT_MODULE): private-filter-out-private-libs = \
+  paste -sd ":" $(1) > $(2) && \
+  cat $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) | xargs -n 1 -I privatelib bash -c "sed -i.bak 's/privatelib//' $(2)" && \
+  sed -i.bak -e 's/::\+/:/g ; s/^:\+// ; s/:\+$$//' $(2) && \
+  rm -f $(2).bak
+$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES_FILE := $(llndk_libraries_file)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SP_LIBRARIES_FILE := $(vndksp_libraries_file)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_LIBRARIES_FILE := $(vndkcore_libraries_file)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE := $(vndkprivate_libraries_file)
+$(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := $(sanitizer_runtime_libraries)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_VERSION_SUFFIX := $(vndk_version_suffix)
+$(LOCAL_BUILT_MODULE): PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir)
+$(LOCAL_BUILT_MODULE): PRIVATE_COMP_CHECK_SCRIPT := $(compatibility_check_script)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_VERSION_TAG := \#VNDK$(vndk_version)\#
+deps := $(llndk_libraries_file) $(vndksp_libraries_file) $(vndkcore_libraries_file) \
+  $(vndkprivate_libraries_file)
+ifeq ($(check_backward_compatibility),true)
+deps += $(compatibility_check_script)
+endif
+ifeq ($(my_vndk_use_core_variant),true)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_USING_CORE_VARIANT_LIBRARIES_FILE := $(vndk_using_core_variant_libraries_file)
+deps += $(vndk_using_core_variant_libraries_file)
+endif
+
+$(LOCAL_BUILT_MODULE): $(ld_config_template) $(deps)
+	@echo "Generate: $< -> $@"
+ifeq ($(check_backward_compatibility),true)
+	@echo "Checking backward compatibility..."
+	$(hide) $(PRIVATE_COMP_CHECK_SCRIPT) $<
+endif
+	@mkdir -p $(dir $@)
+	$(call private-filter-out-private-libs,$(PRIVATE_LLNDK_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/llndk_filtered)
+	$(hide) sed -e "s?%LLNDK_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/llndk_filtered)?g" $< >$@
+	$(call private-filter-out-private-libs,$(PRIVATE_VNDK_SP_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/vndksp_filtered)
+	$(hide) sed -i.bak -e "s?%VNDK_SAMEPROCESS_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/vndksp_filtered)?g" $@
+	$(call private-filter-out-private-libs,$(PRIVATE_VNDK_CORE_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/vndkcore_filtered)
+	$(hide) sed -i.bak -e "s?%VNDK_CORE_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/vndkcore_filtered)?g" $@
+
+ifeq ($(my_vndk_use_core_variant),true)
+	$(call private-filter-out-private-libs,$(PRIVATE_VNDK_USING_CORE_VARIANT_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/vndk_using_core_variant_filtered)
+	$(hide) sed -i.bak -e "s?%VNDK_IN_SYSTEM_NS%?,vndk_in_system?g" $@
+	$(hide) sed -i.bak -e "s?%VNDK_USING_CORE_VARIANT_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/vndk_using_core_variant_filtered)?g" $@
+else
+	$(hide) sed -i.bak -e "s?%VNDK_IN_SYSTEM_NS%??g" $@
+	# Unlike LLNDK or VNDK-SP, VNDK_USING_CORE_VARIANT_LIBRARIES can be nothing
+	# if TARGET_VNDK_USE_CORE_VARIANT is not set.  In this case, we need to remove
+	# the entire line in the linker config so that we are not left with a line
+	# like:
+	#   namespace.vndk.link.vndk_in_system.shared_libs =
+	$(hide) sed -i.bak -e 's?^.*= %VNDK_USING_CORE_VARIANT_LIBRARIES%$$??' $@
+endif
+
+	$(hide) echo -n > $(PRIVATE_INTERMEDIATES_DIR)/private_llndk && \
+	cat $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) | \
+	xargs -n 1 -I privatelib bash -c "(grep privatelib $(PRIVATE_LLNDK_LIBRARIES_FILE) || true) >> $(PRIVATE_INTERMEDIATES_DIR)/private_llndk" && \
+	paste -sd ":" $(PRIVATE_INTERMEDIATES_DIR)/private_llndk | \
+	sed -i.bak -e "s?%PRIVATE_LLNDK_LIBRARIES%?$$(cat -)?g" $@
+
+	$(hide) sed -i.bak -e "s?%SANITIZER_RUNTIME_LIBRARIES%?$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g" $@
+	$(hide) sed -i.bak -e "s?%VNDK_VER%?$(PRIVATE_VNDK_VERSION_SUFFIX)?g" $@
+	$(hide) sed -i.bak -e "s?%PRODUCT%?$(TARGET_COPY_OUT_PRODUCT)?g" $@
+ifeq ($(TARGET_COPY_OUT_PRODUCT),$(TARGET_COPY_OUT_PRODUCT_SERVICES))
+	# Remove lines containing %PRODUCT_SERVICES% (identical to the %PRODUCT% ones)
+	$(hide) sed -i.bak -e "\?%PRODUCT_SERVICES%?d" $@
+else
+	$(hide) sed -i.bak -e "s?%PRODUCT_SERVICES%?$(TARGET_COPY_OUT_PRODUCT_SERVICES)?g" $@
+endif
+	$(hide) sed -i.bak -e "s?^$(PRIVATE_VNDK_VERSION_TAG)??g" $@
+	$(hide) sed -i.bak "/^\#VNDK[0-9]\{2\}\#.*$$/d" $@
+	$(hide) rm -f $@.bak
+
+ld_config_template :=
+check_backward_compatibility :=
+vndk_version :=
+lib_list_from_prebuilts :=
+libz_is_llndk :=
+compatibility_check_script :=
+intermediates_dir :=
+library_lists_dir :=
+llndk_libraries_file :=
+vndksp_libraries_file :=
+vndkcore_libraries_file :=
+vndkprivate_libraries_file :=
+deps :=
+sanitizer_runtime_libraries :=
+vndk_version_suffix :=
+llndk_libraries_list :=
+vndksp_libraries_list :=
+write-libs-to-file :=
+
+ifeq ($(my_vndk_use_core_variant),true)
+vndk_using_core_variant_libraries_file :=
+vndk_using_core_variant_libraries_list :=
+endif
+
+my_vndk_use_core_variant :=
diff --git a/run-as/.clang-format b/run-as/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/run-as/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
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 b27cfad..432c434 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -25,9 +25,13 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <string>
+#include <vector>
+
 #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 +44,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
@@ -65,32 +70,40 @@
   return true; // Keep searching.
 }
 
-static bool check_directory(const char* path, uid_t uid) {
+static void check_directory(const char* path, uid_t uid) {
   struct stat st;
-  if (TEMP_FAILURE_RETRY(lstat(path, &st)) == -1) return false;
+  if (TEMP_FAILURE_RETRY(lstat(path, &st)) == -1) {
+    error(1, errno, "couldn't stat %s", path);
+  }
 
   // /data/user/0 is a known safe symlink.
-  if (strcmp("/data/user/0", path) == 0) return true;
+  if (strcmp("/data/user/0", path) == 0) return;
 
   // Must be a real directory, not a symlink.
-  if (!S_ISDIR(st.st_mode)) return false;
+  if (!S_ISDIR(st.st_mode)) {
+    error(1, 0, "%s not a directory: %o", path, st.st_mode);
+  }
 
   // Must be owned by specific uid/gid.
-  if (st.st_uid != uid || st.st_gid != uid) return false;
+  if (st.st_uid != uid || st.st_gid != uid) {
+    error(1, 0, "%s has wrong owner: %d/%d, not %d", path, st.st_uid, st.st_gid, uid);
+  }
 
   // Must not be readable or writable by others.
-  if ((st.st_mode & (S_IROTH|S_IWOTH)) != 0) return false;
-
-  return true;
+  if ((st.st_mode & (S_IROTH | S_IWOTH)) != 0) {
+    error(1, 0, "%s readable or writable by others: %o", path, st.st_mode);
+  }
 }
 
 // This function is used to check the data directory path for safety.
 // We check that every sub-directory is owned by the 'system' user
 // and exists and is not a symlink. We also check that the full directory
 // path is properly owned by the user ID.
-static bool check_data_path(const char* data_path, uid_t uid) {
+static void check_data_path(const char* package_name, const char* data_path, uid_t uid) {
   // The path should be absolute.
-  if (data_path[0] != '/') return false;
+  if (data_path[0] != '/') {
+    error(1, 0, "%s data path not absolute: %s", package_name, data_path);
+  }
 
   // Look for all sub-paths, we do that by finding
   // directory separators in the input path and
@@ -105,26 +118,47 @@
     if (data_path[nn+1] == '\0') break;
 
     /* found a separator, check that data_path is not too long. */
-    if (nn >= (int)(sizeof subpath)) return false;
+    if (nn >= (int)(sizeof subpath)) {
+      error(1, 0, "%s data path too long: %s", package_name, data_path);
+    }
 
     /* reject any '..' subpath */
     if (nn >= 3               &&
         data_path[nn-3] == '/' &&
         data_path[nn-2] == '.' &&
         data_path[nn-1] == '.') {
-      return false;
+      error(1, 0, "%s contains '..': %s", package_name, data_path);
     }
 
     /* copy to 'subpath', then check ownership */
     memcpy(subpath, data_path, nn);
     subpath[nn] = '\0';
 
-    if (!check_directory(subpath, AID_SYSTEM)) return false;
+    check_directory(subpath, AID_SYSTEM);
   }
 
   // All sub-paths were checked, now verify that the full data
   // directory is owned by the application uid.
-  return check_directory(data_path, uid);
+  check_directory(data_path, uid);
+}
+
+std::vector<gid_t> get_supplementary_gids(uid_t userAppId) {
+  std::vector<gid_t> gids;
+  int size = getgroups(0, &gids[0]);
+  if (size < 0) {
+    error(1, errno, "getgroups failed");
+  }
+  gids.resize(size);
+  size = getgroups(size, &gids[0]);
+  if (size != static_cast<int>(gids.size())) {
+    error(1, errno, "getgroups failed");
+  }
+  // Profile guide compiled oat files (like /data/app/xxx/oat/arm64/base.odex) are not readable
+  // worldwide (DEXOPT_PUBLIC flag isn't set). To support reading them (needed by simpleperf for
+  // profiling), add shared app gid to supplementary groups.
+  gid_t shared_app_gid = userAppId % AID_USER_OFFSET - AID_APP_START + AID_SHARED_GID_START;
+  gids.push_back(shared_app_gid);
+  return gids;
 }
 
 int main(int argc, char* argv[]) {
@@ -139,6 +173,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;
 
@@ -159,6 +199,15 @@
   if (!packagelist_parse(packagelist_parse_callback, &info)) {
     error(1, errno, "packagelist_parse failed");
   }
+
+  // Handle a multi-user data path
+  if (userId > 0) {
+    free(info.data_dir);
+    if (asprintf(&info.data_dir, "/data/user/%d/%s", userId, pkgname) == -1) {
+      error(1, errno, "asprintf failed");
+    }
+  }
+
   if (info.uid == 0) {
     error(1, 0, "unknown package: %s", pkgname);
   }
@@ -183,21 +232,21 @@
   }
 
   // Check that the data directory path is valid.
-  if (!check_data_path(info.data_dir, userAppId)) {
-    error(1, 0, "package has corrupt installation: %s", pkgname);
-  }
+  check_data_path(pkgname, info.data_dir, userAppId);
 
   // Ensure that we change all real/effective/saved IDs at the
   // same time to avoid nasty surprises.
   uid_t uid = userAppId;
   uid_t gid = userAppId;
+  std::vector<gid_t> supplementary_gids = get_supplementary_gids(userAppId);
   ScopedMinijail j(minijail_new());
   minijail_change_uid(j.get(), uid);
   minijail_change_gid(j.get(), gid);
-  minijail_keep_supplementary_gids(j.get());
+  minijail_set_supplementary_gids(j.get(), supplementary_gids.size(), supplementary_gids.data());
   minijail_enter(j.get());
 
-  if (selinux_android_setcontext(uid, 0, info.seinfo, pkgname) < 0) {
+  std::string seinfo = std::string(info.seinfo) + ":fromRunAs";
+  if (selinux_android_setcontext(uid, 0, seinfo.c_str(), pkgname) < 0) {
     error(1, errno, "couldn't set SELinux security context");
   }
 
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index dc36596..2b358197 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -27,6 +27,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -99,14 +100,21 @@
 
 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, bool use_esdfs) {
+                           mode_t mask, bool derive_gid, bool default_normal, bool unshared_obb,
+                           bool use_esdfs) {
+    // Add new options at the end of the vector.
+    std::vector<std::string> new_opts_list;
+    if (multi_user) new_opts_list.push_back("multiuser,");
+    if (derive_gid) new_opts_list.push_back("derive_gid,");
+    if (default_normal) new_opts_list.push_back("default_normal,");
+    if (unshared_obb) new_opts_list.push_back("unshared_obb,");
     // 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++) {
+    for (int i = 0; i <= new_opts_list.size(); ++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,";
+        for (int j = 0; j < new_opts_list.size() - i; ++j) {
+            new_opts += new_opts_list[j];
+        }
 
         auto opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
                                                 fsuid, fsgid, new_opts.c_str(), mask, userid, gid);
@@ -142,13 +150,14 @@
     return true;
 }
 
-static bool sdcardfs_setup_secondary(const std::string& default_path, 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, bool use_esdfs) {
+static bool sdcardfs_setup_secondary(const std::string& default_path,
+                                     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,
+                                     bool unshared_obb, bool use_esdfs) {
     if (use_esdfs) {
         return sdcardfs_setup(source_path, dest_path, fsuid, fsgid, multi_user, userid, gid, mask,
-                              derive_gid, default_normal, use_esdfs);
+                              derive_gid, default_normal, unshared_obb, use_esdfs);
     } else {
         return sdcardfs_setup_bind_remount(default_path, dest_path, gid, mask);
     }
@@ -156,23 +165,28 @@
 
 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,
-                         bool derive_gid, bool default_normal, bool use_esdfs) {
+                         bool derive_gid, bool default_normal, bool unshared_obb, bool use_esdfs) {
     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;
+    std::string dest_path_full = "/mnt/runtime/full/" + label;
 
     umask(0);
     if (multi_user) {
         // 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, derive_gid, default_normal, use_esdfs) ||
+                            AID_SDCARD_RW, 0006, derive_gid, default_normal, unshared_obb,
+                            use_esdfs) ||
             !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
                                       multi_user, userid, AID_EVERYBODY, 0027, derive_gid,
-                                      default_normal, use_esdfs) ||
+                                      default_normal, unshared_obb, use_esdfs) ||
             !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
                                       multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0027,
-                                      derive_gid, default_normal, use_esdfs)) {
+                                      derive_gid, default_normal, unshared_obb, use_esdfs) ||
+            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,
+                                      multi_user, userid, AID_EVERYBODY, 0007, derive_gid,
+                                      default_normal, unshared_obb, use_esdfs)) {
             LOG(FATAL) << "failed to sdcardfs_setup";
         }
     } else {
@@ -180,13 +194,17 @@
         // 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, derive_gid, default_normal, use_esdfs) ||
+                            AID_SDCARD_RW, 0006, derive_gid, default_normal, unshared_obb,
+                            use_esdfs) ||
             !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
                                       multi_user, userid, AID_EVERYBODY, full_write ? 0027 : 0022,
-                                      derive_gid, default_normal, use_esdfs) ||
+                                      derive_gid, default_normal, unshared_obb, use_esdfs) ||
             !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
                                       multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0022,
-                                      derive_gid, default_normal, use_esdfs)) {
+                                      derive_gid, default_normal, unshared_obb, use_esdfs) ||
+            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,
+                                      multi_user, userid, AID_EVERYBODY, 0007, derive_gid,
+                                      default_normal, unshared_obb, use_esdfs)) {
             LOG(FATAL) << "failed to sdcardfs_setup";
         }
     }
@@ -209,7 +227,8 @@
                << "    -U: specify user ID that owns device"
                << "    -m: source_path is multi-user"
                << "    -w: runtime write mount has full write access"
-               << "    -P  preserve owners on the lower file system";
+               << "    -P: preserve owners on the lower file system"
+               << "    -o: obb dir doesn't need to be shared between users";
     return 1;
 }
 
@@ -223,6 +242,7 @@
     bool full_write = false;
     bool derive_gid = false;
     bool default_normal = false;
+    bool unshared_obb = false;
     int i;
     struct rlimit rlim;
     int fs_version;
@@ -231,7 +251,7 @@
     android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
 
     int opt;
-    while ((opt = getopt(argc, argv, "u:g:U:mwGi")) != -1) {
+    while ((opt = getopt(argc, argv, "u:g:U:mwGio")) != -1) {
         switch (opt) {
             case 'u':
                 uid = strtoul(optarg, NULL, 10);
@@ -254,8 +274,12 @@
             case 'i':
                 default_normal = true;
                 break;
+            case 'o':
+                unshared_obb = true;
+                break;
             case '?':
             default:
+                LOG(ERROR) << "Unknown option: '" << opt << "'";
                 return usage();
         }
     }
@@ -297,6 +321,6 @@
     }
 
     run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write, derive_gid,
-                 default_normal, !should_use_sdcardfs());
+                 default_normal, unshared_obb, !should_use_sdcardfs());
     return 1;
 }
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 2e42b70..bac3dc3 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -1,21 +1,51 @@
 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: [
+        "auditctl",
         "awk",
-        "awk_vendor",
         "bzip2",
-        "grep",
-        "grep_vendor",
         "logwrapper",
-        "logwrapper_vendor",
+        "mini-keyctl",
         "mkshrc",
-        "mkshrc_vendor",
+        "newfs_msdos",
         "reboot",
         "sh",
-        "sh_vendor",
+        "simpleperf",
+        "simpleperf_app_runner",
+        "tcpdump",
         "toolbox",
-        "toolbox_vendor",
         "toybox",
+        "unzip",
+    ],
+}
+
+phony {
+    name: "shell_and_utilities_recovery",
+    required: [
+        "sh.recovery",
+        "toolbox.recovery",
+        "toybox.recovery",
+        "unzip.recovery",
+    ],
+}
+
+phony {
+    name: "shell_and_utilities_vendor",
+    required: [
+        "awk_vendor",
+        "logwrapper_vendor",
+        "mkshrc_vendor",
+        "sh_vendor",
+        "toolbox_vendor",
         "toybox_vendor",
     ],
 }
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index c423c69..1926a4f 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -1,5 +1,4 @@
-Android's shell and utilities
-=============================
+# Android's shell and utilities
 
 Since IceCreamSandwich Android has used
 [mksh](https://www.mirbsd.org/mksh.htm) as its shell. Before then it used
@@ -18,11 +17,9 @@
 (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. We currently still use
-the BSD dd and grep (because the toybox versions are still unfinished),
-and 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.
+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,
@@ -36,8 +33,7 @@
 full list for a release by running `toybox` directly.
 
 
-Android 2.3 (Gingerbread)
--------------------------
+## Android 2.3 (Gingerbread)
 
 BSD: cat dd newfs\_msdos
 
@@ -48,8 +44,7 @@
 umount uptime vmstat watchprops wipe
 
 
-Android 4.0 (IceCreamSandwich)
-------------------------------
+## Android 4.0 (IceCreamSandwich)
 
 BSD: cat dd newfs\_msdos
 
@@ -60,8 +55,7 @@
 touch umount uptime vmstat watchprops wipe
 
 
-Android 4.1-4.3 (JellyBean)
----------------------------
+## Android 4.1-4.3 (JellyBean)
 
 BSD: cat cp dd du grep newfs\_msdos
 
@@ -73,8 +67,7 @@
 sync top touch umount uptime vmstat watchprops wipe
 
 
-Android 4.4 (KitKat)
---------------------
+## Android 4.4 (KitKat)
 
 BSD: cat cp dd du grep newfs\_msdos
 
@@ -86,8 +79,7 @@
 stop swapoff swapon sync top touch umount uptime vmstat watchprops wipe
 
 
-Android 5.0 (Lollipop)
-----------------------
+## Android 5.0 (Lollipop)
 
 BSD: cat chown cp dd du grep kill ln mv printenv rm rmdir sleep sync
 
@@ -99,8 +91,7 @@
 top touch umount uptime vmstat watchprops wipe
 
 
-Android 6.0 (Marshmallow)
--------------------------
+## Android 6.0 (Marshmallow)
 
 BSD: dd du grep
 
@@ -120,8 +111,7 @@
 vmstat wc which whoami xargs yes
 
 
-Android 7.0 (Nougat)
---------------------
+## Android 7.0 (Nougat)
 
 BSD: dd grep
 
@@ -142,8 +132,7 @@
 uptime usleep vmstat wc which whoami xargs xxd yes
 
 
-Android 8.0 (Oreo)
-------------------
+## Android 8.0 (Oreo)
 
 BSD: dd grep
 
@@ -166,8 +155,8 @@
 tty ulimit umount uname uniq unix2dos uptime usleep uudecode uuencode
 vmstat wc which whoami xargs xxd yes zcat
 
-Android P
----------
+
+## Android 9.0 (Pie)
 
 BSD: dd grep
 
@@ -179,15 +168,75 @@
 
 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
+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 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
+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 bc blkid blockdev cal cat chattr chcon chgrp
+chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
+diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
+false fgrep file find flock fmt free freeramdisk fsfreeze getconf
+getenforce getfattr grep groups gunzip gzip head help hostname hwclock
+i2cdetect i2cdump i2cget i2cset iconv id ifconfig inotifyd insmod
+install ionice iorenice iotop kill killall ln load\_policy log logname
+losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum microcom
+mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount mountpoint
+mv nbd-client nc netcat netstat nice nl nohup nproc nsenter od partprobe
+paste patch pgrep pidof ping ping6 pivot\_root pkill pmap printenv
+printf prlimit ps pwd pwdx readlink realpath renice restorecon rev
+rfkill rm rmdir rmmod runcon sed sendevent seq setenforce setfattr
+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 traceroute traceroute6
+true truncate tty tunctl ulimit umount uname uniq unix2dos unlink
+unshare uptime usleep uudecode uuencode uuidgen vconfig vmstat watch
+wc which whoami xargs xxd yes zcat
+
+## Android R
+
+BSD: grep fsck\_msdos newfs\_msdos
+
+bzip2: bzcat bzip2 bunzip2
+
+one-true-awk: awk
+
+toolbox: getevent getprop setprop start stop
+
+toybox: acpi base64 basename bc blkid blockdev cal cat chattr chcon chgrp
+chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
+diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
+false fgrep file find flock fmt free freeramdisk fsfreeze getconf
+getenforce getfattr grep groups gunzip gzip head help hostname hwclock
+i2cdetect i2cdump i2cget i2cset iconv id ifconfig inotifyd insmod
+install ionice iorenice iotop kill killall ln load\_policy log logname
+losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum microcom
+mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount mountpoint
+mv nbd-client nc netcat netstat nice nl nohup nproc nsenter od partprobe
+paste patch pgrep pidof ping ping6 pivot\_root pkill pmap printenv
+printf prlimit ps pwd pwdx readlink realpath renice restorecon rev
+rfkill rm rmdir rmmod runcon sed sendevent seq setenforce setfattr
+setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep sort split
+stat strings stty swapoff swapon sync sysctl tac tail tar taskset tee
+time timeout top touch tr traceroute traceroute6 true truncate tty tunctl
+ulimit umount uname uniq unix2dos unlink unshare uptime usleep uudecode
+uuencode uuidgen vconfig vmstat watch wc which whoami xargs xxd yes zcat
diff --git a/storaged/Android.bp b/storaged/Android.bp
index b478f4a8..733b60f 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -62,10 +62,11 @@
         "uid_info.cpp",
         "storaged.proto",
         ":storaged_aidl",
-        "binder/android/os/storaged/IStoragedPrivate.aidl",
+        ":storaged_aidl_private",
     ],
 
     static_libs: ["libhealthhalutils"],
+    header_libs: ["libbatteryservice_headers"],
 
     logtags: ["EventLogTags.logtags"],
 
@@ -115,4 +116,13 @@
     srcs: [
         "binder/android/os/IStoraged.aidl",
     ],
+    path: "binder",
+}
+
+filegroup {
+    name: "storaged_aidl_private",
+    srcs: [
+        "binder/android/os/storaged/IStoragedPrivate.aidl",
+    ],
+    path: "binder",
 }
diff --git a/storaged/OWNERS b/storaged/OWNERS
index 7445270..d033f00 100644
--- a/storaged/OWNERS
+++ b/storaged/OWNERS
@@ -1 +1,2 @@
-jinqian@google.com
+salyzyn@google.com
+dvander@google.com
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
index 400e734..6f92048 100644
--- a/storaged/include/storaged.h
+++ b/storaged/include/storaged.h
@@ -26,7 +26,6 @@
 #include <unordered_map>
 #include <vector>
 
-#include <batteryservice/IBatteryPropertiesListener.h>
 #include <utils/Mutex.h>
 
 #include <android/hardware/health/2.0/IHealth.h>
diff --git a/storaged/include/storaged_uid_monitor.h b/storaged/include/storaged_uid_monitor.h
index 3a718fa..fffb3d2 100644
--- a/storaged/include/storaged_uid_monitor.h
+++ b/storaged/include/storaged_uid_monitor.h
@@ -68,31 +68,33 @@
 
 struct uid_record {
     string name;
-    struct uid_io_usage ios;
+    uid_io_usage ios;
 };
 
 struct uid_records {
     uint64_t start_ts;
-    vector<struct uid_record> entries;
+    vector<uid_record> entries;
 };
 
 class uid_monitor {
 private:
     FRIEND_TEST(storaged_test, uid_monitor);
+    FRIEND_TEST(storaged_test, load_uid_io_proto);
+
     // last dump from /proc/uid_io/stats, uid -> uid_info
-    unordered_map<uint32_t, uid_info> last_uid_io_stats;
+    unordered_map<uint32_t, uid_info> last_uid_io_stats_;
     // current io usage for next report, app name -> uid_io_usage
-    unordered_map<string, struct uid_io_usage> curr_io_stats;
+    unordered_map<string, uid_io_usage> curr_io_stats_;
     // io usage records, end timestamp -> {start timestamp, vector of records}
-    map<uint64_t, struct uid_records> io_history;
+    map<uint64_t, uid_records> io_history_;
     // charger ON/OFF
-    charger_stat_t charger_stat;
+    charger_stat_t charger_stat_;
     // protects curr_io_stats, last_uid_io_stats, records and charger_stat
-    Mutex uidm_mutex;
+    Mutex uidm_mutex_;
     // start time for IO records
-    uint64_t start_ts;
+    uint64_t start_ts_;
     // true if UID_IO_STATS_PATH is accessible
-    const bool enable;
+    const bool enabled_;
 
     // reads from /proc/uid_io/stats
     unordered_map<uint32_t, uid_info> get_uid_io_stats_locked();
@@ -103,6 +105,10 @@
     // writes io_history to protobuf
     void update_uid_io_proto(unordered_map<int, StoragedProto>* protos);
 
+    // Ensure that io_history_ can append |n| items without exceeding
+    // MAX_UID_RECORDS_SIZE in size.
+    void maybe_shrink_history_for_items(size_t nitems);
+
 public:
     uid_monitor();
     // called by storaged main thread
@@ -110,16 +116,20 @@
     // called by storaged -u
     unordered_map<uint32_t, uid_info> get_uid_io_stats();
     // called by dumpsys
-    map<uint64_t, struct uid_records> dump(
+    map<uint64_t, 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
-    bool enabled() { return enable; };
+    bool enabled() { return enabled_; };
     void report(unordered_map<int, StoragedProto>* protos);
     // restores io_history from protobuf
-    void load_uid_io_proto(const UidIOUsage& proto);
+    void load_uid_io_proto(userid_t user_id, const UidIOUsage& proto);
     void clear_user_history(userid_t user_id);
+
+    map<uint64_t, uid_records>& io_history() { return io_history_; }
+
+    static constexpr int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours
 };
 
 #endif /* _STORAGED_UID_MONITOR_H_ */
diff --git a/storaged/include/uid_info.h b/storaged/include/uid_info.h
index 4398a0d..c5533ac 100644
--- a/storaged/include/uid_info.h
+++ b/storaged/include/uid_info.h
@@ -19,6 +19,8 @@
 #include <string>
 #include <unordered_map>
 
+#include <binder/Parcelable.h>
+
 namespace android {
 namespace os {
 namespace storaged {
diff --git a/storaged/main.cpp b/storaged/main.cpp
index b3f1281..3817fb5 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -21,9 +21,6 @@
 #include <getopt.h>
 #include <pthread.h>
 #include <stdio.h>
-#include <sys/capability.h>
-#include <sys/prctl.h>
-#include <sys/resource.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <vector>
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index bf8b448..6897663 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -30,6 +30,7 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/unique_fd.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <batteryservice/BatteryServiceConstants.h>
 #include <cutils/properties.h>
@@ -193,7 +194,7 @@
         return;
     }
 
-    mUidm.load_uid_io_proto(proto.uid_io_usage());
+    mUidm.load_uid_io_proto(user_id, proto.uid_io_usage());
 
     if (user_id == USER_SYSTEM) {
         storage_info->load_perf_history_proto(proto.perf_history());
@@ -340,20 +341,14 @@
     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";
-        }
+        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";
-            }
+            PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
             return;
         }
         int64_t cost = (end_ts.tv_sec - start_ts.tv_sec) * SEC_TO_USEC +
diff --git a/storaged/storaged_diskstats.cpp b/storaged/storaged_diskstats.cpp
index 1050033..8b5001d 100644
--- a/storaged/storaged_diskstats.cpp
+++ b/storaged/storaged_diskstats.cpp
@@ -129,6 +129,10 @@
 
     bool success = false;
     auto ret = service->getDiskStats([&success, stats](auto result, const auto& halStats) {
+        if (result == Result::NOT_SUPPORTED) {
+            LOG_TO(SYSTEM, DEBUG) << "getDiskStats is not supported on health HAL.";
+            return;
+        }
         if (result != Result::SUCCESS || halStats.size() == 0) {
             LOG_TO(SYSTEM, ERROR) << "getDiskStats failed with result " << toString(result)
                                   << " and size " << halStats.size();
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index 5605f66..ca2421b 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -87,12 +87,21 @@
     day_start_tp += chrono::seconds(perf_history.day_start_sec());
 
     nr_samples = perf_history.nr_samples();
+    if (nr_samples < recent_perf.size()) {
+        recent_perf.erase(recent_perf.begin() + nr_samples, recent_perf.end());
+    }
+    size_t i = 0;
     for (auto bw : perf_history.recent_perf()) {
-        recent_perf.push_back(bw);
+        if (i < recent_perf.size()) {
+            recent_perf[i] = bw;
+        } else {
+            recent_perf.push_back(bw);
+        }
+        ++i;
     }
 
     nr_days = perf_history.nr_days();
-    int i = 0;
+    i = 0;
     for (auto bw : perf_history.daily_perf()) {
         daily_perf[i++] = bw;
     }
@@ -370,8 +379,12 @@
 
 void health_storage_info_t::report() {
     auto ret = mHealth->getStorageInfo([this](auto result, const auto& halInfos) {
+        if (result == Result::NOT_SUPPORTED) {
+            LOG_TO(SYSTEM, DEBUG) << "getStorageInfo is not supported on health HAL.";
+            return;
+        }
         if (result != Result::SUCCESS || halInfos.size() == 0) {
-            LOG_TO(SYSTEM, DEBUG) << "getStorageInfo failed with result " << toString(result)
+            LOG_TO(SYSTEM, ERROR) << "getStorageInfo failed with result " << toString(result)
                                   << " and size " << halInfos.size();
             return;
         }
@@ -380,7 +393,7 @@
     });
 
     if (!ret.isOk()) {
-        LOG_TO(SYSTEM, DEBUG) << "getStorageInfo failed with " << ret.description();
+        LOG_TO(SYSTEM, ERROR) << "getStorageInfo failed with " << ret.description();
     }
 }
 
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
index 17ea25b..45f1d4d 100644
--- a/storaged/storaged_service.cpp
+++ b/storaged/storaged_service.cpp
@@ -161,7 +161,7 @@
         storaged_sp->update_uid_io_interval(time_window);
     }
 
-    return NO_ERROR;
+    return OK;
 }
 
 binder::Status StoragedService::onUserStarted(int32_t userId) {
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp
index 5745782..55380ba 100644
--- a/storaged/storaged_uid_monitor.cpp
+++ b/storaged/storaged_uid_monitor.cpp
@@ -21,6 +21,7 @@
 
 #include <string>
 #include <unordered_map>
+#include <unordered_set>
 
 #include <android/content/pm/IPackageManagerNative.h>
 #include <android-base/file.h>
@@ -50,7 +51,7 @@
 
 std::unordered_map<uint32_t, uid_info> uid_monitor::get_uid_io_stats()
 {
-    Mutex::Autolock _l(uidm_mutex);
+    Mutex::Autolock _l(uidm_mutex_);
     return get_uid_io_stats_locked();
 };
 
@@ -178,10 +179,10 @@
             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()) {
+            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;
+                uid_io_stats[u.uid].name = last_uid_io_stats_[u.uid].name;
             }
         } else {
             task_info t;
@@ -200,8 +201,6 @@
 
 namespace {
 
-const int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours
-
 inline size_t history_size(
     const std::map<uint64_t, struct uid_records>& history)
 {
@@ -218,12 +217,12 @@
 {
     // remove records more than 5 days old
     if (curr_ts > 5 * DAY_TO_SEC) {
-        auto it = io_history.lower_bound(curr_ts - 5 * DAY_TO_SEC);
-        io_history.erase(io_history.begin(), it);
+        auto it = io_history_.lower_bound(curr_ts - 5 * DAY_TO_SEC);
+        io_history_.erase(io_history_.begin(), it);
     }
 
     struct uid_records new_records;
-    for (const auto& p : curr_io_stats) {
+    for (const auto& p : curr_io_stats_) {
         struct uid_record record = {};
         record.name = p.first;
         if (!p.second.uid_ios.is_zero()) {
@@ -237,23 +236,26 @@
         }
     }
 
-    curr_io_stats.clear();
-    new_records.start_ts = start_ts;
-    start_ts = curr_ts;
+    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
-    ssize_t overflow = history_size(io_history) +
-        new_records.entries.size() - MAX_UID_RECORDS_SIZE;
-    while (overflow > 0 && io_history.size() > 0) {
-        auto del_it = io_history.begin();
-        overflow -= del_it->second.entries.size();
-        io_history.erase(io_history.begin());
-    }
+    maybe_shrink_history_for_items(new_records.entries.size());
 
-    io_history[curr_ts] = new_records;
+    io_history_[curr_ts] = new_records;
+}
+
+void uid_monitor::maybe_shrink_history_for_items(size_t nitems) {
+    ssize_t overflow = history_size(io_history_) + nitems - MAX_UID_RECORDS_SIZE;
+    while (overflow > 0 && io_history_.size() > 0) {
+        auto del_it = io_history_.begin();
+        overflow -= del_it->second.entries.size();
+        io_history_.erase(io_history_.begin());
+    }
 }
 
 std::map<uint64_t, struct uid_records> uid_monitor::dump(
@@ -263,7 +265,7 @@
         report(nullptr);
     }
 
-    Mutex::Autolock _l(uidm_mutex);
+    Mutex::Autolock _l(uidm_mutex_);
 
     std::map<uint64_t, struct uid_records> dump_records;
     uint64_t first_ts = 0;
@@ -272,7 +274,7 @@
         first_ts = time(NULL) - hours * HOUR_TO_SEC;
     }
 
-    for (auto it = io_history.lower_bound(first_ts); it != io_history.end(); ++it) {
+    for (auto it = io_history_.lower_bound(first_ts); it != io_history_.end(); ++it) {
         const std::vector<struct uid_record>& recs = it->second.entries;
         struct uid_records filtered;
 
@@ -311,29 +313,29 @@
 
     for (const auto& it : uid_io_stats) {
         const uid_info& uid = it.second;
-        if (curr_io_stats.find(uid.name) == curr_io_stats.end()) {
-            curr_io_stats[uid.name] = {};
+        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];
+        struct uid_io_usage& usage = curr_io_stats_[uid.name];
         usage.user_id = multiuser_get_user_id(uid.uid);
 
         int64_t fg_rd_delta = uid.io[FOREGROUND].read_bytes -
-            last_uid_io_stats[uid.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;
+            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;
+            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;
+            last_uid_io_stats_[uid.uid].io[BACKGROUND].write_bytes;
 
-        usage.uid_ios.bytes[READ][FOREGROUND][charger_stat] +=
+        usage.uid_ios.bytes[READ][FOREGROUND][charger_stat_] +=
             (fg_rd_delta < 0) ? 0 : fg_rd_delta;
-        usage.uid_ios.bytes[READ][BACKGROUND][charger_stat] +=
+        usage.uid_ios.bytes[READ][BACKGROUND][charger_stat_] +=
             (bg_rd_delta < 0) ? 0 : bg_rd_delta;
-        usage.uid_ios.bytes[WRITE][FOREGROUND][charger_stat] +=
+        usage.uid_ios.bytes[WRITE][FOREGROUND][charger_stat_] +=
             (fg_wr_delta < 0) ? 0 : fg_wr_delta;
-        usage.uid_ios.bytes[WRITE][BACKGROUND][charger_stat] +=
+        usage.uid_ios.bytes[WRITE][BACKGROUND][charger_stat_] +=
             (bg_wr_delta < 0) ? 0 : bg_wr_delta;
 
         for (const auto& task_it : uid.tasks) {
@@ -341,34 +343,34 @@
             const pid_t pid = task_it.first;
             const std::string& comm = task_it.second.comm;
             int64_t task_fg_rd_delta = task.io[FOREGROUND].read_bytes -
-                last_uid_io_stats[uid.uid].tasks[pid].io[FOREGROUND].read_bytes;
+                last_uid_io_stats_[uid.uid].tasks[pid].io[FOREGROUND].read_bytes;
             int64_t task_bg_rd_delta = task.io[BACKGROUND].read_bytes -
-                last_uid_io_stats[uid.uid].tasks[pid].io[BACKGROUND].read_bytes;
+                last_uid_io_stats_[uid.uid].tasks[pid].io[BACKGROUND].read_bytes;
             int64_t task_fg_wr_delta = task.io[FOREGROUND].write_bytes -
-                last_uid_io_stats[uid.uid].tasks[pid].io[FOREGROUND].write_bytes;
+                last_uid_io_stats_[uid.uid].tasks[pid].io[FOREGROUND].write_bytes;
             int64_t task_bg_wr_delta = task.io[BACKGROUND].write_bytes -
-                last_uid_io_stats[uid.uid].tasks[pid].io[BACKGROUND].write_bytes;
+                last_uid_io_stats_[uid.uid].tasks[pid].io[BACKGROUND].write_bytes;
 
             io_usage& task_usage = usage.task_ios[comm];
-            task_usage.bytes[READ][FOREGROUND][charger_stat] +=
+            task_usage.bytes[READ][FOREGROUND][charger_stat_] +=
                 (task_fg_rd_delta < 0) ? 0 : task_fg_rd_delta;
-            task_usage.bytes[READ][BACKGROUND][charger_stat] +=
+            task_usage.bytes[READ][BACKGROUND][charger_stat_] +=
                 (task_bg_rd_delta < 0) ? 0 : task_bg_rd_delta;
-            task_usage.bytes[WRITE][FOREGROUND][charger_stat] +=
+            task_usage.bytes[WRITE][FOREGROUND][charger_stat_] +=
                 (task_fg_wr_delta < 0) ? 0 : task_fg_wr_delta;
-            task_usage.bytes[WRITE][BACKGROUND][charger_stat] +=
+            task_usage.bytes[WRITE][BACKGROUND][charger_stat_] +=
                 (task_bg_wr_delta < 0) ? 0 : task_bg_wr_delta;
         }
     }
 
-    last_uid_io_stats = uid_io_stats;
+    last_uid_io_stats_ = uid_io_stats;
 }
 
 void uid_monitor::report(unordered_map<int, StoragedProto>* protos)
 {
     if (!enabled()) return;
 
-    Mutex::Autolock _l(uidm_mutex);
+    Mutex::Autolock _l(uidm_mutex_);
 
     update_curr_io_stats_locked();
     add_records_locked(time(NULL));
@@ -408,7 +410,7 @@
 
 void uid_monitor::update_uid_io_proto(unordered_map<int, StoragedProto>* protos)
 {
-    for (const auto& item : io_history) {
+    for (const auto& item : io_history_) {
         const uint64_t& end_ts = item.first;
         const struct uid_records& recs = item.second;
         unordered_map<userid_t, UidIOItem*> user_items;
@@ -448,9 +450,9 @@
 
 void uid_monitor::clear_user_history(userid_t user_id)
 {
-    Mutex::Autolock _l(uidm_mutex);
+    Mutex::Autolock _l(uidm_mutex_);
 
-    for (auto& item : io_history) {
+    for (auto& item : io_history_) {
         vector<uid_record>* entries = &item.second.entries;
         entries->erase(
             remove_if(entries->begin(), entries->end(),
@@ -459,27 +461,42 @@
             entries->end());
     }
 
-    for (auto it = io_history.begin(); it != io_history.end(); ) {
+    for (auto it = io_history_.begin(); it != io_history_.end(); ) {
         if (it->second.entries.empty()) {
-            it = io_history.erase(it);
+            it = io_history_.erase(it);
         } else {
             it++;
         }
     }
 }
 
-void uid_monitor::load_uid_io_proto(const UidIOUsage& uid_io_proto)
+void uid_monitor::load_uid_io_proto(userid_t user_id, const UidIOUsage& uid_io_proto)
 {
     if (!enabled()) return;
 
-    Mutex::Autolock _l(uidm_mutex);
+    Mutex::Autolock _l(uidm_mutex_);
 
     for (const auto& item_proto : uid_io_proto.uid_io_items()) {
         const UidIORecords& records_proto = item_proto.records();
-        struct uid_records* recs = &io_history[item_proto.end_ts()];
+        struct uid_records* recs = &io_history_[item_proto.end_ts()];
+
+        // It's possible that the same uid_io_proto file gets loaded more than
+        // once, for example, if system_server crashes. In this case we avoid
+        // adding duplicate entries, so we build a quick way to check for
+        // duplicates.
+        std::unordered_set<std::string> existing_uids;
+        for (const auto& rec : recs->entries) {
+            if (rec.ios.user_id == user_id) {
+                existing_uids.emplace(rec.name);
+            }
+        }
 
         recs->start_ts = records_proto.start_ts();
         for (const auto& rec_proto : records_proto.entries()) {
+            if (existing_uids.find(rec_proto.uid_name()) != existing_uids.end()) {
+                continue;
+            }
+
             struct uid_record record;
             record.name = rec_proto.uid_name();
             record.ios.user_id = rec_proto.user_id();
@@ -492,29 +509,35 @@
             }
             recs->entries.push_back(record);
         }
+
+        // We already added items, so this will just cull down to the maximum
+        // length. We do not remove anything if there is only one entry.
+        if (io_history_.size() > 1) {
+            maybe_shrink_history_for_items(0);
+        }
     }
 }
 
 void uid_monitor::set_charger_state(charger_stat_t stat)
 {
-    Mutex::Autolock _l(uidm_mutex);
+    Mutex::Autolock _l(uidm_mutex_);
 
-    if (charger_stat == stat) {
+    if (charger_stat_ == stat) {
         return;
     }
 
     update_curr_io_stats_locked();
-    charger_stat = stat;
+    charger_stat_ = stat;
 }
 
 void uid_monitor::init(charger_stat_t stat)
 {
-    charger_stat = stat;
+    charger_stat_ = stat;
 
-    start_ts = time(NULL);
-    last_uid_io_stats = get_uid_io_stats();
+    start_ts_ = time(NULL);
+    last_uid_io_stats_ = get_uid_io_stats();
 }
 
 uid_monitor::uid_monitor()
-    : enable(!access(UID_IO_STATS_PATH, R_OK)) {
+    : enabled_(!access(UID_IO_STATS_PATH, R_OK)) {
 }
diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp
index ec47b65..64009c2 100644
--- a/storaged/tests/storaged_test.cpp
+++ b/storaged/tests/storaged_test.cpp
@@ -443,8 +443,9 @@
 
 TEST(storaged_test, uid_monitor) {
     uid_monitor uidm;
+    auto& io_history = uidm.io_history();
 
-    uidm.io_history[200] = {
+    io_history[200] = {
         .start_ts = 100,
         .entries = {
             { "app1", {
@@ -466,7 +467,7 @@
         },
     };
 
-    uidm.io_history[300] = {
+    io_history[300] = {
         .start_ts = 200,
         .entries = {
             { "app1", {
@@ -526,9 +527,9 @@
     EXPECT_EQ(user_1_item_1.records().entries(0).user_id(), 1UL);
     EXPECT_EQ(user_1_item_1.records().entries(0).uid_io().wr_fg_chg_off(), 1000UL);
 
-    uidm.io_history.clear();
+    io_history.clear();
 
-    uidm.io_history[300] = {
+    io_history[300] = {
         .start_ts = 200,
         .entries = {
             { "app1", {
@@ -539,7 +540,7 @@
         },
     };
 
-    uidm.io_history[400] = {
+    io_history[400] = {
         .start_ts = 300,
         .entries = {
             { "app1", {
@@ -550,16 +551,16 @@
         },
     };
 
-    uidm.load_uid_io_proto(protos[0].uid_io_usage());
-    uidm.load_uid_io_proto(protos[1].uid_io_usage());
+    uidm.load_uid_io_proto(0, protos[0].uid_io_usage());
+    uidm.load_uid_io_proto(1, protos[1].uid_io_usage());
 
-    EXPECT_EQ(uidm.io_history.size(), 3UL);
-    EXPECT_EQ(uidm.io_history.count(200), 1UL);
-    EXPECT_EQ(uidm.io_history.count(300), 1UL);
-    EXPECT_EQ(uidm.io_history.count(400), 1UL);
+    EXPECT_EQ(io_history.size(), 3UL);
+    EXPECT_EQ(io_history.count(200), 1UL);
+    EXPECT_EQ(io_history.count(300), 1UL);
+    EXPECT_EQ(io_history.count(400), 1UL);
 
-    EXPECT_EQ(uidm.io_history[200].start_ts, 100UL);
-    const vector<struct uid_record>& entries_0 = uidm.io_history[200].entries;
+    EXPECT_EQ(io_history[200].start_ts, 100UL);
+    const vector<struct uid_record>& entries_0 = io_history[200].entries;
     EXPECT_EQ(entries_0.size(), 3UL);
     EXPECT_EQ(entries_0[0].name, "app1");
     EXPECT_EQ(entries_0[0].ios.user_id, 0UL);
@@ -572,8 +573,8 @@
     EXPECT_EQ(entries_0[2].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
     EXPECT_EQ(entries_0[2].ios.uid_ios.bytes[READ][FOREGROUND][CHARGER_ON], 1000UL);
 
-    EXPECT_EQ(uidm.io_history[300].start_ts, 200UL);
-    const vector<struct uid_record>& entries_1 = uidm.io_history[300].entries;
+    EXPECT_EQ(io_history[300].start_ts, 200UL);
+    const vector<struct uid_record>& entries_1 = io_history[300].entries;
     EXPECT_EQ(entries_1.size(), 3UL);
     EXPECT_EQ(entries_1[0].name, "app1");
     EXPECT_EQ(entries_1[0].ios.user_id, 0UL);
@@ -585,8 +586,8 @@
     EXPECT_EQ(entries_1[2].ios.user_id, 1UL);
     EXPECT_EQ(entries_1[2].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_OFF], 1000UL);
 
-    EXPECT_EQ(uidm.io_history[400].start_ts, 300UL);
-    const vector<struct uid_record>& entries_2 = uidm.io_history[400].entries;
+    EXPECT_EQ(io_history[400].start_ts, 300UL);
+    const vector<struct uid_record>& entries_2 = io_history[400].entries;
     EXPECT_EQ(entries_2.size(), 1UL);
     EXPECT_EQ(entries_2[0].name, "app1");
     EXPECT_EQ(entries_2[0].ios.user_id, 0UL);
@@ -615,14 +616,71 @@
 
     uidm.clear_user_history(0);
 
-    EXPECT_EQ(uidm.io_history.size(), 2UL);
-    EXPECT_EQ(uidm.io_history.count(200), 1UL);
-    EXPECT_EQ(uidm.io_history.count(300), 1UL);
+    EXPECT_EQ(io_history.size(), 2UL);
+    EXPECT_EQ(io_history.count(200), 1UL);
+    EXPECT_EQ(io_history.count(300), 1UL);
 
-    EXPECT_EQ(uidm.io_history[200].entries.size(), 1UL);
-    EXPECT_EQ(uidm.io_history[300].entries.size(), 1UL);
+    EXPECT_EQ(io_history[200].entries.size(), 1UL);
+    EXPECT_EQ(io_history[300].entries.size(), 1UL);
 
     uidm.clear_user_history(1);
 
-    EXPECT_EQ(uidm.io_history.size(), 0UL);
+    EXPECT_EQ(io_history.size(), 0UL);
+}
+
+TEST(storaged_test, load_uid_io_proto) {
+    uid_monitor uidm;
+    auto& io_history = uidm.io_history();
+
+    static const uint64_t kProtoTime = 200;
+    io_history[kProtoTime] = {
+        .start_ts = 100,
+        .entries = {
+            { "app1", {
+                .user_id = 0,
+                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,
+              }
+            },
+            { "app2", {
+                .user_id = 0,
+                .uid_ios.bytes[READ][FOREGROUND][CHARGER_OFF] = 2000,
+              }
+            },
+            { "app3", {
+                .user_id = 0,
+                .uid_ios.bytes[READ][FOREGROUND][CHARGER_OFF] = 3000,
+              }
+            },
+        },
+    };
+
+    unordered_map<int, StoragedProto> protos;
+    uidm.update_uid_io_proto(&protos);
+    ASSERT_EQ(protos.size(), size_t(1));
+
+    // Loading the same proto many times should not add duplicate entries.
+    UidIOUsage user_0 = protos[0].uid_io_usage();
+    for (size_t i = 0; i < 10000; i++) {
+        uidm.load_uid_io_proto(0, user_0);
+    }
+    ASSERT_EQ(io_history.size(), size_t(1));
+    ASSERT_EQ(io_history[kProtoTime].entries.size(), size_t(3));
+
+    // Create duplicate entries until we go over the limit.
+    auto record = io_history[kProtoTime];
+    io_history.clear();
+    for (size_t i = 0; i < uid_monitor::MAX_UID_RECORDS_SIZE * 2; i++) {
+        if (i == kProtoTime) {
+            continue;
+        }
+        io_history[i] = record;
+    }
+    ASSERT_GT(io_history.size(), size_t(uid_monitor::MAX_UID_RECORDS_SIZE));
+
+    // After loading, the history should be truncated.
+    for (auto& item : *user_0.mutable_uid_io_items()) {
+        item.set_end_ts(io_history.size());
+    }
+    uidm.load_uid_io_proto(0, user_0);
+    ASSERT_LE(io_history.size(), size_t(uid_monitor::MAX_UID_RECORDS_SIZE));
 }
diff --git a/storaged/uid_info.cpp b/storaged/uid_info.cpp
index 58e3fd2..0f718de 100644
--- a/storaged/uid_info.cpp
+++ b/storaged/uid_info.cpp
@@ -32,7 +32,7 @@
         parcel->writeCString(task_it.second.comm.c_str());
         parcel->write(&task_it.second.io, sizeof(task_it.second.io));
     }
-    return NO_ERROR;
+    return OK;
 }
 
 status_t UidInfo::readFromParcel(const Parcel* parcel) {
@@ -48,5 +48,5 @@
         parcel->read(&task.io, sizeof(task.io));
         tasks[task.pid] = task;
     }
-    return NO_ERROR;
+    return OK;
 }
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index ddd95b2..0cc603a 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -1,37 +1,11 @@
 cc_defaults {
     name: "toolbox_defaults",
-
     cflags: [
         "-Werror",
         "-Wno-unused-parameter",
         "-Wno-unused-const-variable",
-        "-include bsd-compatibility.h",
         "-D_FILE_OFFSET_BITS=64",
     ],
-    local_include_dirs: ["upstream-netbsd/include/"],
-}
-
-cc_library_static {
-    name: "libtoolbox_dd",
-    defaults: ["toolbox_defaults"],
-    vendor_available: true,
-    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: [
-        "-Dmain=dd_main",
-        "-DNO_CONV",
-    ],
 }
 
 genrule {
@@ -50,29 +24,30 @@
         "toolbox.c",
         "getevent.c",
         "getprop.cpp",
-        "newfs_msdos.c",
+        "setprop.cpp",
+        "start.cpp",
     ],
     generated_headers: [
         "toolbox_input_labels",
     ],
-    whole_static_libs: ["libtoolbox_dd"],
     shared_libs: [
         "libbase",
-        "libcutils",
     ],
     static_libs: ["libpropertyinfoparser"],
 
     symlinks: [
-        "dd",
         "getevent",
         "getprop",
-        "newfs_msdos",
+        "setprop",
+        "start",
+        "stop",
     ],
 }
 
 cc_binary {
     name: "toolbox",
     defaults: ["toolbox_binary_defaults"],
+    recovery_available: true,
 }
 
 cc_binary {
@@ -81,46 +56,3 @@
     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, so it can provide egrep and fgrep too.
-cc_defaults {
-    name: "grep_common",
-    defaults: ["toolbox_defaults"],
-    srcs: [
-        "upstream-netbsd/usr.bin/grep/fastgrep.c",
-        "upstream-netbsd/usr.bin/grep/file.c",
-        "upstream-netbsd/usr.bin/grep/grep.c",
-        "upstream-netbsd/usr.bin/grep/queue.c",
-        "upstream-netbsd/usr.bin/grep/util.c",
-    ],
-    symlinks: [
-        "egrep",
-        "fgrep",
-    ],
-
-    sanitize: {
-        integer_overflow: false,
-    },
-}
-
-cc_binary {
-    name: "grep",
-    defaults: ["grep_common"],
-}
-
-// Build vendor grep.
-// TODO: Add vendor_available to "grep" module and remove "grep_vendor" module
-//       when vendor_available is fully supported.
-cc_binary {
-    name: "grep_vendor",
-    stem: "grep",
-    vendor: true,
-    defaults: ["grep_common"],
-}
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/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/getevent.c b/toolbox/getevent.c
index e6def6b..e2c77c3 100644
--- a/toolbox/getevent.c
+++ b/toolbox/getevent.c
@@ -321,7 +321,7 @@
     char idstr[80];
     struct input_id id;
 
-    fd = open(device, O_RDWR);
+    fd = open(device, O_RDONLY | O_CLOEXEC);
     if(fd < 0) {
         if(print_flags & PRINT_DEVICE_ERRORS)
             fprintf(stderr, "could not open %s, %s\n", device, strerror(errno));
@@ -530,6 +530,9 @@
     const char *device = NULL;
     const char *device_path = "/dev/input";
 
+    /* disable buffering on stdout */
+    setbuf(stdout, NULL);
+
     opterr = 0;
     do {
         c = getopt(argc, argv, "tns:Sv::dpilqc:rh");
diff --git a/toolbox/getprop.cpp b/toolbox/getprop.cpp
index 9e324a0..ca345cb 100644
--- a/toolbox/getprop.cpp
+++ b/toolbox/getprop.cpp
@@ -1,18 +1,18 @@
-//
-// 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.
-//
+/*
+ * 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>
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
deleted file mode 100644
index b96cdb2..0000000
--- a/toolbox/r.c
+++ /dev/null
@@ -1,102 +0,0 @@
-#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/setprop.cpp b/toolbox/setprop.cpp
new file mode 100644
index 0000000..acf8c3e
--- /dev/null
+++ b/toolbox/setprop.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <stdlib.h>
+#include <sys/system_properties.h>
+
+#include <iostream>
+
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+
+using android::base::SetProperty;
+using android::base::StartsWith;
+
+extern "C" int setprop_main(int argc, char** argv) {
+    if (argc != 3) {
+        std::cout << "usage: setprop NAME VALUE\n"
+                     "\n"
+                     "Sets an Android system property."
+                  << std::endl;
+        return EXIT_FAILURE;
+    }
+
+    auto name = std::string{argv[1]};
+    auto value = std::string{argv[2]};
+
+    // SetProperty() doesn't tell us why it failed, and actually can't recognize most failures, so
+    // we duplicate some of init's checks here to help the user.
+
+    if (name.front() == '.' || name.back() == '.') {
+        std::cerr << "Property names must not start or end with a '.'" << std::endl;
+        return EXIT_FAILURE;
+    }
+
+    if (name.find("..") != std::string::npos) {
+        std::cerr << "'..' is not allowed in a property name" << std::endl;
+        return EXIT_FAILURE;
+    }
+
+    for (const auto& c : name) {
+        if (!isalnum(c) && !strchr(":@_.-", c)) {
+            std::cerr << "Invalid character '" << c << "' in name '" << name << "'" << std::endl;
+            return EXIT_FAILURE;
+        }
+    }
+
+    if (value.size() >= PROP_VALUE_MAX && !StartsWith(value, "ro.")) {
+        std::cerr << "Value '" << value << "' is too long, " << value.size()
+                  << " bytes vs a max of " << PROP_VALUE_MAX << std::endl;
+        return EXIT_FAILURE;
+    }
+
+    if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
+        std::cerr << "Value '" << value << "' is not a UTF8 encoded string" << std::endl;
+        return EXIT_FAILURE;
+    }
+
+    if (!SetProperty(name, value)) {
+        std::cerr << "Failed to set property '" << name << "' to '" << value
+                  << "'.\nSee dmesg for error reason." << std::endl;
+        return EXIT_FAILURE;
+    }
+
+    return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/toolbox/start.cpp b/toolbox/start.cpp
new file mode 100644
index 0000000..b87ed15
--- /dev/null
+++ b/toolbox/start.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <iostream>
+#include <string>
+#include <vector>
+
+#include <android-base/properties.h>
+
+using android::base::GetProperty;
+using android::base::SetProperty;
+using namespace std::literals;
+
+static void ControlService(bool start, const std::string& service) {
+    if (!android::base::SetProperty(start ? "ctl.start" : "ctl.stop", service)) {
+        std::cerr << "Unable to " << (start ? "start" : "stop") << " service '" << service
+                  << "'\nSee dmesg for error reason." << std::endl;
+        exit(EXIT_FAILURE);
+    }
+}
+
+static void ControlDefaultServices(bool start) {
+    std::vector<std::string> services = {"netd", "surfaceflinger", "zygote"};
+
+    // Only start zygote_secondary if not single arch.
+    std::string zygote_configuration = GetProperty("ro.zygote", "");
+    if (zygote_configuration != "zygote32" && zygote_configuration != "zygote64") {
+        services.emplace_back("zygote_secondary");
+    }
+
+    if (start) {
+        for (const auto& service : services) {
+            ControlService(true, service);
+        }
+    } else {
+        for (auto it = services.crbegin(); it != services.crend(); ++it) {
+            ControlService(false, *it);
+        }
+    }
+}
+
+static int StartStop(int argc, char** argv, bool start) {
+    if (getuid()) {
+        std::cerr << "Must be root" << std::endl;
+        return EXIT_FAILURE;
+    }
+
+    if (argc == 1) {
+        ControlDefaultServices(start);
+    }
+
+    if (argc == 2 && argv[1] == "--help"s) {
+        std::cout << "usage: " << (start ? "start" : "stop")
+                  << " [SERVICE...]\n"
+                     "\n"
+                  << (start ? "Starts" : "Stops")
+                  << " the given system service, or netd/surfaceflinger/zygotes." << std::endl;
+        return EXIT_SUCCESS;
+    }
+
+    for (int i = 1; i < argc; ++i) {
+        ControlService(start, argv[i]);
+    }
+    return EXIT_SUCCESS;
+}
+
+extern "C" int start_main(int argc, char** argv) {
+    return StartStop(argc, argv, true);
+}
+
+extern "C" int stop_main(int argc, char** argv) {
+    return StartStop(argc, argv, false);
+}
\ No newline at end of file
diff --git a/toolbox/tools.h b/toolbox/tools.h
index 505f528..9a7ebd2 100644
--- a/toolbox/tools.h
+++ b/toolbox/tools.h
@@ -1,5 +1,6 @@
-TOOL(dd)
 TOOL(getevent)
 TOOL(getprop)
-TOOL(newfs_msdos)
+TOOL(setprop)
+TOOL(start)
+TOOL(stop)
 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/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c b/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c
deleted file mode 100644
index 2fcd864..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c
+++ /dev/null
@@ -1,336 +0,0 @@
-/*	$OpenBSD: util.c,v 1.36 2007/10/02 17:59:18 otto Exp $	*/
-/*	$FreeBSD: head/usr.bin/grep/fastgrep.c 211496 2010-08-19 09:28:59Z des $ */
-
-/*-
- * 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.
- */
-
-/*
- * XXX: This file is a speed up for grep to cover the defects of the
- * regex library.  These optimizations should practically be implemented
- * there keeping this code clean.  This is a future TODO, but for the
- * meantime, we need to use this workaround.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: fastgrep.c,v 1.5 2011/04/18 03:27:40 joerg Exp $");
-
-#include <limits.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <wchar.h>
-#include <wctype.h>
-
-#include "grep.h"
-
-static inline int	grep_cmp(const unsigned char *, const unsigned char *, size_t);
-static inline void	grep_revstr(unsigned char *, int);
-
-void
-fgrepcomp(fastgrep_t *fg, const char *pat)
-{
-	unsigned int i;
-
-	/* Initialize. */
-	fg->len = strlen(pat);
-	fg->bol = false;
-	fg->eol = false;
-	fg->reversed = false;
-
-	fg->pattern = (unsigned char *)grep_strdup(pat);
-
-	/* Preprocess pattern. */
-	for (i = 0; i <= UCHAR_MAX; i++)
-		fg->qsBc[i] = fg->len;
-	for (i = 1; i < fg->len; i++)
-		fg->qsBc[fg->pattern[i]] = fg->len - i;
-}
-
-/*
- * Returns: -1 on failure, 0 on success
- */
-int
-fastcomp(fastgrep_t *fg, const char *pat)
-{
-	unsigned int i;
-	int firstHalfDot = -1;
-	int firstLastHalfDot = -1;
-	int hasDot = 0;
-	int lastHalfDot = 0;
-	int shiftPatternLen;
-
-	/* Initialize. */
-	fg->len = strlen(pat);
-	fg->bol = false;
-	fg->eol = false;
-	fg->reversed = false;
-	fg->word = wflag;
-
-	/* Remove end-of-line character ('$'). */
-	if (fg->len > 0 && pat[fg->len - 1] == '$') {
-		fg->eol = true;
-		fg->len--;
-	}
-
-	/* Remove beginning-of-line character ('^'). */
-	if (pat[0] == '^') {
-		fg->bol = true;
-		fg->len--;
-		pat++;
-	}
-
-	if (fg->len >= 14 &&
-	    memcmp(pat, "[[:<:]]", 7) == 0 &&
-	    memcmp(pat + fg->len - 7, "[[:>:]]", 7) == 0) {
-		fg->len -= 14;
-		pat += 7;
-		/* Word boundary is handled separately in util.c */
-		fg->word = true;
-	}
-
-	/*
-	 * pat has been adjusted earlier to not include '^', '$' or
-	 * the word match character classes at the beginning and ending
-	 * of the string respectively.
-	 */
-	fg->pattern = grep_malloc(fg->len + 1);
-	memcpy(fg->pattern, pat, fg->len);
-	fg->pattern[fg->len] = '\0';
-
-	/* Look for ways to cheat...er...avoid the full regex engine. */
-	for (i = 0; i < fg->len; i++) {
-		/* Can still cheat? */
-		if (fg->pattern[i] == '.') {
-			hasDot = i;
-			if (i < fg->len / 2) {
-				if (firstHalfDot < 0)
-					/* Closest dot to the beginning */
-					firstHalfDot = i;
-			} else {
-				/* Closest dot to the end of the pattern. */
-				lastHalfDot = i;
-				if (firstLastHalfDot < 0)
-					firstLastHalfDot = i;
-			}
-		} else {
-			/* Free memory and let others know this is empty. */
-			free(fg->pattern);
-			fg->pattern = NULL;
-			return (-1);
-		}
-	}
-
-	/*
-	 * Determine if a reverse search would be faster based on the placement
-	 * of the dots.
-	 */
-	if ((!(lflag || cflag)) && ((!(fg->bol || fg->eol)) &&
-	    ((lastHalfDot) && ((firstHalfDot < 0) ||
-	    ((fg->len - (lastHalfDot + 1)) < (size_t)firstHalfDot)))) &&
-	    !oflag && !color) {
-		fg->reversed = true;
-		hasDot = fg->len - (firstHalfDot < 0 ?
-		    firstLastHalfDot : firstHalfDot) - 1;
-		grep_revstr(fg->pattern, fg->len);
-	}
-
-	/*
-	 * Normal Quick Search would require a shift based on the position the
-	 * next character after the comparison is within the pattern.  With
-	 * wildcards, the position of the last dot effects the maximum shift
-	 * distance.
-	 * The closer to the end the wild card is the slower the search.  A
-	 * reverse version of this algorithm would be useful for wildcards near
-	 * the end of the string.
-	 *
-	 * Examples:
-	 * Pattern	Max shift
-	 * -------	---------
-	 * this		5
-	 * .his		4
-	 * t.is		3
-	 * th.s		2
-	 * thi.		1
-	 */
-
-	/* Adjust the shift based on location of the last dot ('.'). */
-	shiftPatternLen = fg->len - hasDot;
-
-	/* Preprocess pattern. */
-	for (i = 0; i <= (signed)UCHAR_MAX; i++)
-		fg->qsBc[i] = shiftPatternLen;
-	for (i = hasDot + 1; i < fg->len; i++) {
-		fg->qsBc[fg->pattern[i]] = fg->len - i;
-	}
-
-	/*
-	 * Put pattern back to normal after pre-processing to allow for easy
-	 * comparisons later.
-	 */
-	if (fg->reversed)
-		grep_revstr(fg->pattern, fg->len);
-
-	return (0);
-}
-
-int
-grep_search(fastgrep_t *fg, const unsigned char *data, size_t len, regmatch_t *pmatch)
-{
-	unsigned int j;
-	int ret = REG_NOMATCH;
-
-	if (pmatch->rm_so == (ssize_t)len)
-		return (ret);
-
-	if (fg->bol && pmatch->rm_so != 0) {
-		pmatch->rm_so = len;
-		pmatch->rm_eo = len;
-		return (ret);
-	}
-
-	/* No point in going farther if we do not have enough data. */
-	if (len < fg->len)
-		return (ret);
-
-	/* Only try once at the beginning or ending of the line. */
-	if (fg->bol || fg->eol) {
-		/* Simple text comparison. */
-		/* Verify data is >= pattern length before searching on it. */
-		if (len >= fg->len) {
-			/* Determine where in data to start search at. */
-			j = fg->eol ? len - fg->len : 0;
-			if (!((fg->bol && fg->eol) && (len != fg->len)))
-				if (grep_cmp(fg->pattern, data + j,
-				    fg->len) == -1) {
-					pmatch->rm_so = j;
-					pmatch->rm_eo = j + fg->len;
-						ret = 0;
-				}
-		}
-	} else if (fg->reversed) {
-		/* Quick Search algorithm. */
-		j = len;
-		do {
-			if (grep_cmp(fg->pattern, data + j - fg->len,
-				fg->len) == -1) {
-				pmatch->rm_so = j - fg->len;
-				pmatch->rm_eo = j;
-				ret = 0;
-				break;
-			}
-			/* Shift if within bounds, otherwise, we are done. */
-			if (j == fg->len)
-				break;
-			j -= fg->qsBc[data[j - fg->len - 1]];
-		} while (j >= fg->len);
-	} else {
-		/* Quick Search algorithm. */
-		j = pmatch->rm_so;
-		do {
-			if (grep_cmp(fg->pattern, data + j, fg->len) == -1) {
-				pmatch->rm_so = j;
-				pmatch->rm_eo = j + fg->len;
-				ret = 0;
-				break;
-			}
-
-			/* Shift if within bounds, otherwise, we are done. */
-			if (j + fg->len == len)
-				break;
-			else
-				j += fg->qsBc[data[j + fg->len]];
-		} while (j <= (len - fg->len));
-	}
-
-	return (ret);
-}
-
-/*
- * Returns:	i >= 0 on failure (position that it failed)
- *		-1 on success
- */
-static inline int
-grep_cmp(const unsigned char *pat, const unsigned char *data, size_t len)
-{
-	size_t size;
-	wchar_t *wdata, *wpat;
-	unsigned int i;
-
-	if (iflag) {
-		if ((size = mbstowcs(NULL, (const char *)data, 0)) ==
-		    ((size_t) - 1))
-			return (-1);
-
-		wdata = grep_malloc(size * sizeof(wint_t));
-
-		if (mbstowcs(wdata, (const char *)data, size) ==
-		    ((size_t) - 1))
-			return (-1);
-
-		if ((size = mbstowcs(NULL, (const char *)pat, 0)) ==
-		    ((size_t) - 1))
-			return (-1);
-
-		wpat = grep_malloc(size * sizeof(wint_t));
-
-		if (mbstowcs(wpat, (const char *)pat, size) == ((size_t) - 1))
-			return (-1);
-		for (i = 0; i < len; i++) {
-			if ((towlower(wpat[i]) == towlower(wdata[i])) ||
-			    ((grepbehave != GREP_FIXED) && wpat[i] == L'.'))
-				continue;
-			free(wpat);
-			free(wdata);
-				return (i);
-		}
-	} else {
-		for (i = 0; i < len; i++) {
-			if ((pat[i] == data[i]) || ((grepbehave != GREP_FIXED) &&
-			    pat[i] == '.'))
-				continue;
-			return (i);
-		}
-	}
-	return (-1);
-}
-
-static inline void
-grep_revstr(unsigned char *str, int len)
-{
-	int i;
-	char c;
-
-	for (i = 0; i < len / 2; i++) {
-		c = str[i];
-		str[i] = str[len - i - 1];
-		str[len - i - 1] = c;
-	}
-}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/file.c b/toolbox/upstream-netbsd/usr.bin/grep/file.c
deleted file mode 100644
index cf4a0fa..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/file.c
+++ /dev/null
@@ -1,271 +0,0 @@
-/*	$NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $	*/
-/*	$FreeBSD: head/usr.bin/grep/file.c 211496 2010-08-19 09:28:59Z des $	*/
-/*	$OpenBSD: file.c,v 1.11 2010/07/02 20:48:48 nicm Exp $	*/
-
-/*-
- * 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.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $");
-
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#ifndef __ANDROID__
-#include <bzlib.h>
-#endif
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <wchar.h>
-#include <wctype.h>
-#ifndef __ANDROID__
-#include <zlib.h>
-#endif
-
-#include "grep.h"
-
-#define	MAXBUFSIZ	(32 * 1024)
-#define	LNBUFBUMP	80
-
-#ifndef __ANDROID__
-static gzFile gzbufdesc;
-static BZFILE* bzbufdesc;
-#endif
-
-static unsigned char buffer[MAXBUFSIZ];
-static unsigned char *bufpos;
-static size_t bufrem;
-
-static unsigned char *lnbuf;
-static size_t lnbuflen;
-
-static inline int
-grep_refill(struct file *f)
-{
-	ssize_t nr;
-#ifndef __ANDROID__
-	int bzerr;
-#endif
-
-	bufpos = buffer;
-	bufrem = 0;
-
-#ifndef __ANDROID__
-	if (filebehave == FILE_GZIP)
-		nr = gzread(gzbufdesc, buffer, MAXBUFSIZ);
-	else if (filebehave == FILE_BZIP && bzbufdesc != NULL) {
-		nr = BZ2_bzRead(&bzerr, bzbufdesc, buffer, MAXBUFSIZ);
-		switch (bzerr) {
-		case BZ_OK:
-		case BZ_STREAM_END:
-			/* No problem, nr will be okay */
-			break;
-		case BZ_DATA_ERROR_MAGIC:
-			/*
-			 * As opposed to gzread(), which simply returns the
-			 * plain file data, if it is not in the correct
-			 * compressed format, BZ2_bzRead() instead aborts.
-			 *
-			 * So, just restart at the beginning of the file again,
-			 * and use plain reads from now on.
-			 */
-			BZ2_bzReadClose(&bzerr, bzbufdesc);
-			bzbufdesc = NULL;
-			if (lseek(f->fd, 0, SEEK_SET) == -1)
-				return (-1);
-			nr = read(f->fd, buffer, MAXBUFSIZ);
-			break;
-		default:
-			/* Make sure we exit with an error */
-			nr = -1;
-		}
-	} else
-#endif
-		nr = read(f->fd, buffer, MAXBUFSIZ);
-
-	if (nr < 0)
-		return (-1);
-
-	bufrem = nr;
-	return (0);
-}
-
-static inline int
-grep_lnbufgrow(size_t newlen)
-{
-
-	if (lnbuflen < newlen) {
-		lnbuf = grep_realloc(lnbuf, newlen);
-		lnbuflen = newlen;
-	}
-
-	return (0);
-}
-
-char *
-grep_fgetln(struct file *f, size_t *lenp)
-{
-	unsigned char *p;
-	char *ret;
-	size_t len;
-	size_t off;
-	ptrdiff_t diff;
-
-	/* Fill the buffer, if necessary */
-	if (bufrem == 0 && grep_refill(f) != 0)
-		goto error;
-
-	if (bufrem == 0) {
-		/* Return zero length to indicate EOF */
-		*lenp = 0;
-		return ((char *)bufpos);
-	}
-
-	/* Look for a newline in the remaining part of the buffer */
-	if ((p = memchr(bufpos, line_sep, bufrem)) != NULL) {
-		++p; /* advance over newline */
-		ret = (char *)bufpos;
-		len = p - bufpos;
-		bufrem -= len;
-		bufpos = p;
-		*lenp = len;
-		return (ret);
-	}
-
-	/* We have to copy the current buffered data to the line buffer */
-	for (len = bufrem, off = 0; ; len += bufrem) {
-		/* Make sure there is room for more data */
-		if (grep_lnbufgrow(len + LNBUFBUMP))
-			goto error;
-		memcpy(lnbuf + off, bufpos, len - off);
-		off = len;
-		if (grep_refill(f) != 0)
-			goto error;
-		if (bufrem == 0)
-			/* EOF: return partial line */
-			break;
-		if ((p = memchr(bufpos, line_sep, bufrem)) == NULL)
-			continue;
-		/* got it: finish up the line (like code above) */
-		++p;
-		diff = p - bufpos;
-		len += diff;
-		if (grep_lnbufgrow(len))
-		    goto error;
-		memcpy(lnbuf + off, bufpos, diff);
-		bufrem -= diff;
-		bufpos = p;
-		break;
-	}
-	*lenp = len;
-	return ((char *)lnbuf);
-
-error:
-	*lenp = 0;
-	return (NULL);
-}
-
-static inline struct file *
-grep_file_init(struct file *f)
-{
-
-#ifndef __ANDROID__
-	if (filebehave == FILE_GZIP &&
-	    (gzbufdesc = gzdopen(f->fd, "r")) == NULL)
-		goto error;
-
-	if (filebehave == FILE_BZIP &&
-	    (bzbufdesc = BZ2_bzdopen(f->fd, "r")) == NULL)
-		goto error;
-#endif
-
-	/* Fill read buffer, also catches errors early */
-	if (grep_refill(f) != 0)
-		goto error;
-
-	/* Check for binary stuff, if necessary */
-	if (!nulldataflag && binbehave != BINFILE_TEXT &&
-	    memchr(bufpos, '\0', bufrem) != NULL)
-		f->binary = true;
-
-	return (f);
-error:
-	close(f->fd);
-	free(f);
-	return (NULL);
-}
-
-/*
- * Opens a file for processing.
- */
-struct file *
-grep_open(const char *path)
-{
-	struct file *f;
-
-	f = grep_malloc(sizeof *f);
-	memset(f, 0, sizeof *f);
-	if (path == NULL) {
-		/* Processing stdin implies --line-buffered. */
-		lbflag = true;
-		f->fd = STDIN_FILENO;
-	} else if ((f->fd = open(path, O_RDONLY)) == -1) {
-		free(f);
-		return (NULL);
-	}
-
-	return (grep_file_init(f));
-}
-
-/*
- * Closes a file.
- */
-void
-grep_close(struct file *f)
-{
-
-	close(f->fd);
-
-	/* Reset read buffer and line buffer */
-	bufpos = buffer;
-	bufrem = 0;
-
-	free(lnbuf);
-	lnbuf = NULL;
-	lnbuflen = 0;
-}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/grep.c b/toolbox/upstream-netbsd/usr.bin/grep/grep.c
deleted file mode 100644
index 1ea6ed3..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/grep.c
+++ /dev/null
@@ -1,707 +0,0 @@
-/*	$NetBSD: grep.c,v 1.12 2014/07/11 16:30:45 christos Exp $	*/
-/* 	$FreeBSD: head/usr.bin/grep/grep.c 211519 2010-08-19 22:55:17Z delphij $	*/
-/*	$OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $	*/
-
-/*-
- * 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.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: grep.c,v 1.12 2014/07/11 16:30:45 christos Exp $");
-
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <getopt.h>
-#include <limits.h>
-#include <libgen.h>
-#include <locale.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "grep.h"
-
-#ifndef WITHOUT_NLS
-#include <nl_types.h>
-nl_catd	 catalog;
-#endif
-
-/*
- * Default messags to use when NLS is disabled or no catalogue
- * is found.
- */
-const char	*errstr[] = {
-	"",
-/* 1*/	"(standard input)",
-/* 2*/	"cannot read bzip2 compressed file",
-/* 3*/	"unknown %s option",
-/* 4*/	"usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZz] [-A num] [-B num] [-C[num]]\n",
-/* 5*/	"\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
-/* 6*/	"\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
-/* 7*/	"\t[pattern] [file ...]\n",
-/* 8*/	"Binary file %s matches\n",
-/* 9*/	"%s (BSD grep) %s\n",
-};
-
-/* Flags passed to regcomp() and regexec() */
-int		 cflags = 0;
-int		 eflags = REG_STARTEND;
-
-/* Searching patterns */
-unsigned int	 patterns, pattern_sz;
-char		**pattern;
-regex_t		*r_pattern;
-fastgrep_t	*fg_pattern;
-
-/* Filename exclusion/inclusion patterns */
-unsigned int	 fpatterns, fpattern_sz;
-unsigned int	 dpatterns, dpattern_sz;
-struct epat	*dpattern, *fpattern;
-
-/* For regex errors  */
-char	 re_error[RE_ERROR_BUF + 1];
-
-/* Command-line flags */
-unsigned long long Aflag;	/* -A x: print x lines trailing each match */
-unsigned long long Bflag;	/* -B x: print x lines leading each match */
-bool	 Hflag;		/* -H: always print file name */
-bool	 Lflag;		/* -L: only show names of files with no matches */
-bool	 bflag;		/* -b: show block numbers for each match */
-bool	 cflag;		/* -c: only show a count of matching lines */
-bool	 hflag;		/* -h: don't print filename headers */
-bool	 iflag;		/* -i: ignore case */
-bool	 lflag;		/* -l: only show names of files with matches */
-bool	 mflag;		/* -m x: stop reading the files after x matches */
-unsigned long long mcount;	/* count for -m */
-bool	 nflag;		/* -n: show line numbers in front of matching lines */
-bool	 oflag;		/* -o: print only matching part */
-bool	 qflag;		/* -q: quiet mode (don't output anything) */
-bool	 sflag;		/* -s: silent mode (ignore errors) */
-bool	 vflag;		/* -v: only show non-matching lines */
-bool	 wflag;		/* -w: pattern must start and end on word boundaries */
-bool	 xflag;		/* -x: pattern must match entire line */
-bool	 lbflag;	/* --line-buffered */
-bool	 nullflag;	/* --null */
-bool	 nulldataflag;	/* --null-data */
-unsigned char line_sep = '\n';	/* 0 for --null-data */
-char	*label;		/* --label */
-const char *color;	/* --color */
-int	 grepbehave = GREP_BASIC;	/* -EFGP: type of the regex */
-int	 binbehave = BINFILE_BIN;	/* -aIU: handling of binary files */
-int	 filebehave = FILE_STDIO;	/* -JZ: normal, gzip or bzip2 file */
-int	 devbehave = DEV_READ;		/* -D: handling of devices */
-int	 dirbehave = DIR_READ;		/* -dRr: handling of directories */
-int	 linkbehave = LINK_READ;	/* -OpS: handling of symlinks */
-
-bool	 dexclude, dinclude;	/* --exclude-dir and --include-dir */
-bool	 fexclude, finclude;	/* --exclude and --include */
-
-enum {
-	BIN_OPT = CHAR_MAX + 1,
-	COLOR_OPT,
-	DECOMPRESS_OPT,
-	HELP_OPT,
-	MMAP_OPT,
-	LINEBUF_OPT,
-	LABEL_OPT,
-	R_EXCLUDE_OPT,
-	R_INCLUDE_OPT,
-	R_DEXCLUDE_OPT,
-	R_DINCLUDE_OPT
-};
-
-static inline const char	*init_color(const char *);
-
-/* Housekeeping */
-int	 tail;		/* lines left to print */
-bool	 notfound;	/* file not found */
-
-extern char	*__progname;
-
-/*
- * Prints usage information and returns 2.
- */
-__dead static void
-usage(void)
-{
-	fprintf(stderr, getstr(4), __progname);
-	fprintf(stderr, "%s", getstr(5));
-	fprintf(stderr, "%s", getstr(6));
-	fprintf(stderr, "%s", getstr(7));
-	exit(2);
-}
-
-static const char optstr[] =
-    "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxyz";
-
-struct option long_options[] =
-{
-	{"binary-files",	required_argument,	NULL, BIN_OPT},
-	{"decompress",          no_argument,            NULL, DECOMPRESS_OPT},
-	{"help",		no_argument,		NULL, HELP_OPT},
-	{"mmap",		no_argument,		NULL, MMAP_OPT},
-	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
-	{"label",		required_argument,	NULL, LABEL_OPT},
-	{"color",		optional_argument,	NULL, COLOR_OPT},
-	{"colour",		optional_argument,	NULL, COLOR_OPT},
-	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
-	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
-	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
-	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
-	{"after-context",	required_argument,	NULL, 'A'},
-	{"text",		no_argument,		NULL, 'a'},
-	{"before-context",	required_argument,	NULL, 'B'},
-	{"byte-offset",		no_argument,		NULL, 'b'},
-	{"context",		optional_argument,	NULL, 'C'},
-	{"count",		no_argument,		NULL, 'c'},
-	{"devices",		required_argument,	NULL, 'D'},
-        {"directories",		required_argument,	NULL, 'd'},
-	{"extended-regexp",	no_argument,		NULL, 'E'},
-	{"regexp",		required_argument,	NULL, 'e'},
-	{"fixed-strings",	no_argument,		NULL, 'F'},
-	{"file",		required_argument,	NULL, 'f'},
-	{"basic-regexp",	no_argument,		NULL, 'G'},
-	{"no-filename",		no_argument,		NULL, 'h'},
-	{"with-filename",	no_argument,		NULL, 'H'},
-	{"ignore-case",		no_argument,		NULL, 'i'},
-	{"bz2decompress",	no_argument,		NULL, 'J'},
-	{"files-with-matches",	no_argument,		NULL, 'l'},
-	{"files-without-match", no_argument,            NULL, 'L'},
-	{"max-count",		required_argument,	NULL, 'm'},
-	{"line-number",		no_argument,		NULL, 'n'},
-	{"only-matching",	no_argument,		NULL, 'o'},
-	{"quiet",		no_argument,		NULL, 'q'},
-	{"silent",		no_argument,		NULL, 'q'},
-	{"recursive",		no_argument,		NULL, 'r'},
-	{"no-messages",		no_argument,		NULL, 's'},
-	{"binary",		no_argument,		NULL, 'U'},
-	{"unix-byte-offsets",	no_argument,		NULL, 'u'},
-	{"invert-match",	no_argument,		NULL, 'v'},
-	{"version",		no_argument,		NULL, 'V'},
-	{"word-regexp",		no_argument,		NULL, 'w'},
-	{"line-regexp",		no_argument,		NULL, 'x'},
-	{"null",		no_argument,		NULL, 'Z'},
-	{"null-data",		no_argument,		NULL, 'z'},
-	{NULL,			no_argument,		NULL, 0}
-};
-
-/*
- * Adds a searching pattern to the internal array.
- */
-static void
-add_pattern(char *pat, size_t len)
-{
-
-	/* TODO: Check for empty patterns and shortcut */
-
-	/* Increase size if necessary */
-	if (patterns == pattern_sz) {
-		pattern_sz *= 2;
-		pattern = grep_realloc(pattern, ++pattern_sz *
-		    sizeof(*pattern));
-	}
-	if (len > 0 && pat[len - 1] == '\n')
-		--len;
-	/* pat may not be NUL-terminated */
-	pattern[patterns] = grep_malloc(len + 1);
-	memcpy(pattern[patterns], pat, len);
-	pattern[patterns][len] = '\0';
-	++patterns;
-}
-
-/*
- * Adds a file include/exclude pattern to the internal array.
- */
-static void
-add_fpattern(const char *pat, int mode)
-{
-
-	/* Increase size if necessary */
-	if (fpatterns == fpattern_sz) {
-		fpattern_sz *= 2;
-		fpattern = grep_realloc(fpattern, ++fpattern_sz *
-		    sizeof(struct epat));
-	}
-	fpattern[fpatterns].pat = grep_strdup(pat);
-	fpattern[fpatterns].mode = mode;
-	++fpatterns;
-}
-
-/*
- * Adds a directory include/exclude pattern to the internal array.
- */
-static void
-add_dpattern(const char *pat, int mode)
-{
-
-	/* Increase size if necessary */
-	if (dpatterns == dpattern_sz) {
-		dpattern_sz *= 2;
-		dpattern = grep_realloc(dpattern, ++dpattern_sz *
-		    sizeof(struct epat));
-	}
-	dpattern[dpatterns].pat = grep_strdup(pat);
-	dpattern[dpatterns].mode = mode;
-	++dpatterns;
-}
-
-/*
- * Reads searching patterns from a file and adds them with add_pattern().
- */
-static void
-read_patterns(const char *fn)
-{
-	FILE *f;
-	char *line;
-	size_t len;
-	ssize_t rlen;
-
-	if ((f = fopen(fn, "r")) == NULL)
-		err(2, "%s", fn);
-	line = NULL;
-	len = 0;
-	while ((rlen = getline(&line, &len, f)) != -1)
-		add_pattern(line, *line == '\n' ? 0 : (size_t)rlen);
-	free(line);
-	if (ferror(f))
-		err(2, "%s", fn);
-	fclose(f);
-}
-
-static inline const char *
-init_color(const char *d)
-{
-	char *c;
-
-	c = getenv("GREP_COLOR");
-	return (c != NULL ? c : d);
-}
-
-int
-main(int argc, char *argv[])
-{
-	char **aargv, **eargv, *eopts;
-	char *ep;
-	unsigned long long l;
-	unsigned int aargc, eargc, i, j;
-	int c, lastc, needpattern, newarg, prevoptind;
-
-	setlocale(LC_ALL, "");
-
-#ifndef WITHOUT_NLS
-	catalog = catopen("grep", NL_CAT_LOCALE);
-#endif
-
-	/* Check what is the program name of the binary.  In this
-	   way we can have all the funcionalities in one binary
-	   without the need of scripting and using ugly hacks. */
-	switch (__progname[0]) {
-	case 'e':
-		grepbehave = GREP_EXTENDED;
-		break;
-	case 'f':
-		grepbehave = GREP_FIXED;
-		break;
-	case 'g':
-		grepbehave = GREP_BASIC;
-		break;
-	case 'z':
-		filebehave = FILE_GZIP;
-		switch(__progname[1]) {
-		case 'e':
-			grepbehave = GREP_EXTENDED;
-			break;
-		case 'f':
-			grepbehave = GREP_FIXED;
-			break;
-		case 'g':
-			grepbehave = GREP_BASIC;
-			break;
-		}
-		break;
-	}
-
-	lastc = '\0';
-	newarg = 1;
-	prevoptind = 1;
-	needpattern = 1;
-
-	eopts = getenv("GREP_OPTIONS");
-
-	/* support for extra arguments in GREP_OPTIONS */
-	eargc = 0;
-	if (eopts != NULL) {
-		char *str;
-
-		/* make an estimation of how many extra arguments we have */
-		for (j = 0; j < strlen(eopts); j++)
-			if (eopts[j] == ' ')
-				eargc++;
-
-		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
-
-		eargc = 0;
-		/* parse extra arguments */
-		while ((str = strsep(&eopts, " ")) != NULL)
-			eargv[eargc++] = grep_strdup(str);
-
-		aargv = (char **)grep_calloc(eargc + argc + 1,
-		    sizeof(char *));
-
-		aargv[0] = argv[0];
-		for (i = 0; i < eargc; i++)
-			aargv[i + 1] = eargv[i];
-		for (j = 1; j < (unsigned int)argc; j++, i++)
-			aargv[i + 1] = argv[j];
-
-		aargc = eargc + argc;
-	} else {
-		aargv = argv;
-		aargc = argc;
-	}
-
-	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
-	    -1)) {
-		switch (c) {
-		case '0': case '1': case '2': case '3': case '4':
-		case '5': case '6': case '7': case '8': case '9':
-			if (newarg || !isdigit(lastc))
-				Aflag = 0;
-			else if (Aflag > LLONG_MAX / 10) {
-				errno = ERANGE;
-				err(2, NULL);
-			}
-			Aflag = Bflag = (Aflag * 10) + (c - '0');
-			break;
-		case 'C':
-			if (optarg == NULL) {
-				Aflag = Bflag = 2;
-				break;
-			}
-			/* FALLTHROUGH */
-		case 'A':
-			/* FALLTHROUGH */
-		case 'B':
-			errno = 0;
-			l = strtoull(optarg, &ep, 10);
-			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
-			    ((errno == EINVAL) && (l == 0)))
-				err(2, NULL);
-			else if (ep[0] != '\0') {
-				errno = EINVAL;
-				err(2, NULL);
-			}
-			if (c == 'A')
-				Aflag = l;
-			else if (c == 'B')
-				Bflag = l;
-			else
-				Aflag = Bflag = l;
-			break;
-		case 'a':
-			binbehave = BINFILE_TEXT;
-			break;
-		case 'b':
-			bflag = true;
-			break;
-		case 'c':
-			cflag = true;
-			break;
-		case 'D':
-			if (strcasecmp(optarg, "skip") == 0)
-				devbehave = DEV_SKIP;
-			else if (strcasecmp(optarg, "read") == 0)
-				devbehave = DEV_READ;
-			else
-				errx(2, getstr(3), "--devices");
-			break;
-		case 'd':
-			if (strcasecmp("recurse", optarg) == 0) {
-				Hflag = true;
-				dirbehave = DIR_RECURSE;
-			} else if (strcasecmp("skip", optarg) == 0)
-				dirbehave = DIR_SKIP;
-			else if (strcasecmp("read", optarg) == 0)
-				dirbehave = DIR_READ;
-			else
-				errx(2, getstr(3), "--directories");
-			break;
-		case 'E':
-			grepbehave = GREP_EXTENDED;
-			break;
-		case 'e':
-			add_pattern(optarg, strlen(optarg));
-			needpattern = 0;
-			break;
-		case 'F':
-			grepbehave = GREP_FIXED;
-			break;
-		case 'f':
-			read_patterns(optarg);
-			needpattern = 0;
-			break;
-		case 'G':
-			grepbehave = GREP_BASIC;
-			break;
-		case 'H':
-			Hflag = true;
-			break;
-		case 'h':
-			Hflag = false;
-			hflag = true;
-			break;
-		case 'I':
-			binbehave = BINFILE_SKIP;
-			break;
-		case 'i':
-		case 'y':
-			iflag =  true;
-			cflags |= REG_ICASE;
-			break;
-		case 'J':
-			filebehave = FILE_BZIP;
-			break;
-		case 'L':
-			lflag = false;
-			Lflag = true;
-			break;
-		case 'l':
-			Lflag = false;
-			lflag = true;
-			break;
-		case 'm':
-			mflag = true;
-			errno = 0;
-			mcount = strtoull(optarg, &ep, 10);
-			if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
-			    ((errno == EINVAL) && (mcount == 0)))
-				err(2, NULL);
-			else if (ep[0] != '\0') {
-				errno = EINVAL;
-				err(2, NULL);
-			}
-			break;
-		case 'n':
-			nflag = true;
-			break;
-		case 'O':
-			linkbehave = LINK_EXPLICIT;
-			break;
-		case 'o':
-			oflag = true;
-			break;
-		case 'p':
-			linkbehave = LINK_SKIP;
-			break;
-		case 'q':
-			qflag = true;
-			break;
-		case 'S':
-			linkbehave = LINK_READ;
-			break;
-		case 'R':
-		case 'r':
-			dirbehave = DIR_RECURSE;
-			Hflag = true;
-			break;
-		case 's':
-			sflag = true;
-			break;
-		case 'U':
-			binbehave = BINFILE_BIN;
-			break;
-		case 'u':
-		case MMAP_OPT:
-			/* noop, compatibility */
-			break;
-		case 'V':
-			printf(getstr(9), __progname, VERSION);
-			exit(0);
-		case 'v':
-			vflag = true;
-			break;
-		case 'w':
-			wflag = true;
-			break;
-		case 'x':
-			xflag = true;
-			break;
-		case 'Z':
-			nullflag = true;
-			break;
-		case 'z':
-			nulldataflag = true;
-			line_sep = '\0';
-			break;
-		case BIN_OPT:
-			if (strcasecmp("binary", optarg) == 0)
-				binbehave = BINFILE_BIN;
-			else if (strcasecmp("without-match", optarg) == 0)
-				binbehave = BINFILE_SKIP;
-			else if (strcasecmp("text", optarg) == 0)
-				binbehave = BINFILE_TEXT;
-			else
-				errx(2, getstr(3), "--binary-files");
-			break;
-		case COLOR_OPT:
-			color = NULL;
-			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
-			    strcasecmp("tty", optarg) == 0 ||
-			    strcasecmp("if-tty", optarg) == 0) {
-				char *term;
-
-				term = getenv("TERM");
-				if (isatty(STDOUT_FILENO) && term != NULL &&
-				    strcasecmp(term, "dumb") != 0)
-					color = init_color("01;31");
-			} else if (strcasecmp("always", optarg) == 0 ||
-			    strcasecmp("yes", optarg) == 0 ||
-			    strcasecmp("force", optarg) == 0) {
-				color = init_color("01;31");
-			} else if (strcasecmp("never", optarg) != 0 &&
-			    strcasecmp("none", optarg) != 0 &&
-			    strcasecmp("no", optarg) != 0)
-				errx(2, getstr(3), "--color");
-			break;
-		case DECOMPRESS_OPT:
-			filebehave = FILE_GZIP;
-			break;
-		case LABEL_OPT:
-			label = optarg;
-			break;
-		case LINEBUF_OPT:
-			lbflag = true;
-			break;
-		case R_INCLUDE_OPT:
-			finclude = true;
-			add_fpattern(optarg, INCL_PAT);
-			break;
-		case R_EXCLUDE_OPT:
-			fexclude = true;
-			add_fpattern(optarg, EXCL_PAT);
-			break;
-		case R_DINCLUDE_OPT:
-			dinclude = true;
-			add_dpattern(optarg, INCL_PAT);
-			break;
-		case R_DEXCLUDE_OPT:
-			dexclude = true;
-			add_dpattern(optarg, EXCL_PAT);
-			break;
-		case HELP_OPT:
-		default:
-			usage();
-		}
-		lastc = c;
-		newarg = optind != prevoptind;
-		prevoptind = optind;
-	}
-	aargc -= optind;
-	aargv += optind;
-
-	/* Fail if we don't have any pattern */
-	if (aargc == 0 && needpattern)
-		usage();
-
-	/* Process patterns from command line */
-	if (aargc != 0 && needpattern) {
-		add_pattern(*aargv, strlen(*aargv));
-		--aargc;
-		++aargv;
-	}
-
-	switch (grepbehave) {
-	case GREP_FIXED:
-	case GREP_BASIC:
-		break;
-	case GREP_EXTENDED:
-		cflags |= REG_EXTENDED;
-		break;
-	default:
-		/* NOTREACHED */
-		usage();
-	}
-
-	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
-	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
-/*
- * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance.
- * Optimizations should be done there.
- */
-		/* Check if cheating is allowed (always is for fgrep). */
-	if (grepbehave == GREP_FIXED) {
-		for (i = 0; i < patterns; ++i)
-			fgrepcomp(&fg_pattern[i], pattern[i]);
-	} else {
-		for (i = 0; i < patterns; ++i) {
-			if (fastcomp(&fg_pattern[i], pattern[i])) {
-				/* Fall back to full regex library */
-				c = regcomp(&r_pattern[i], pattern[i], cflags);
-				if (c != 0) {
-					regerror(c, &r_pattern[i], re_error,
-					    RE_ERROR_BUF);
-					errx(2, "%s", re_error);
-				}
-			}
-		}
-	}
-
-	if (lbflag)
-		setlinebuf(stdout);
-
-	if ((aargc == 0 || aargc == 1) && !Hflag)
-		hflag = true;
-
-	if (aargc == 0)
-		exit(!procfile("-"));
-
-	if (dirbehave == DIR_RECURSE)
-		c = grep_tree(aargv);
-	else
-		for (c = 0; aargc--; ++aargv) {
-			if ((finclude || fexclude) && !file_matching(*aargv))
-				continue;
-			c+= procfile(*aargv);
-		}
-
-#ifndef WITHOUT_NLS
-	catclose(catalog);
-#endif
-
-	/* Find out the correct return value according to the
-	   results and the command line option. */
-	exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
-}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/grep.h b/toolbox/upstream-netbsd/usr.bin/grep/grep.h
deleted file mode 100644
index fa2a3e3..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/grep.h
+++ /dev/null
@@ -1,162 +0,0 @@
-/*	$NetBSD: grep.h,v 1.8 2012/05/06 22:27:00 joerg Exp $	*/
-/*	$OpenBSD: grep.h,v 1.15 2010/04/05 03:03:55 tedu Exp $	*/
-/*	$FreeBSD: head/usr.bin/grep/grep.h 211496 2010-08-19 09:28:59Z des $	*/
-
-/*-
- * 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.
- */
-
-#ifndef __ANDROID__
-#include <bzlib.h>
-#endif
-#include <limits.h>
-#include <regex.h>
-#include <stdbool.h>
-#include <stdio.h>
-#ifndef __ANDROID__
-#include <zlib.h>
-#endif
-
-#ifdef WITHOUT_NLS
-#define getstr(n)	 errstr[n]
-#else
-#include <nl_types.h>
-
-extern nl_catd		 catalog;
-#define getstr(n)	 catgets(catalog, 1, n, errstr[n])
-#endif
-
-extern const char		*errstr[];
-
-#define VERSION		"2.5.1-FreeBSD"
-
-#define GREP_FIXED	0
-#define GREP_BASIC	1
-#define GREP_EXTENDED	2
-
-#define BINFILE_BIN	0
-#define BINFILE_SKIP	1
-#define BINFILE_TEXT	2
-
-#define FILE_STDIO	0
-#define FILE_GZIP	1
-#define FILE_BZIP	2
-
-#define DIR_READ	0
-#define DIR_SKIP	1
-#define DIR_RECURSE	2
-
-#define DEV_READ	0
-#define DEV_SKIP	1
-
-#define LINK_READ	0
-#define LINK_EXPLICIT	1
-#define LINK_SKIP	2
-
-#define EXCL_PAT	0
-#define INCL_PAT	1
-
-#define MAX_LINE_MATCHES	32
-
-struct file {
-	int		 fd;
-	bool		 binary;
-};
-
-struct str {
-	off_t		 off;
-	size_t		 len;
-	char		*dat;
-	char		*file;
-	int		 line_no;
-};
-
-struct epat {
-	char		*pat;
-	int		 mode;
-};
-
-typedef struct {
-	size_t		 len;
-	unsigned char	*pattern;
-	int		 qsBc[UCHAR_MAX + 1];
-	/* flags */
-	bool		 bol;
-	bool		 eol;
-	bool		 reversed;
-	bool		 word;
-} fastgrep_t;
-
-/* Flags passed to regcomp() and regexec() */
-extern int	 cflags, eflags;
-
-/* Command line flags */
-extern bool	 Eflag, Fflag, Gflag, Hflag, Lflag,
-		 bflag, cflag, hflag, iflag, lflag, mflag, nflag, oflag,
-		 qflag, sflag, vflag, wflag, xflag;
-extern bool	 dexclude, dinclude, fexclude, finclude, lbflag, nullflag, nulldataflag;
-extern unsigned char line_sep;
-extern unsigned long long Aflag, Bflag, mcount;
-extern char	*label;
-extern const char *color;
-extern int	 binbehave, devbehave, dirbehave, filebehave, grepbehave, linkbehave;
-
-extern bool	 notfound;
-extern int	 tail;
-extern unsigned int dpatterns, fpatterns, patterns;
-extern char    **pattern;
-extern struct epat *dpattern, *fpattern;
-extern regex_t	*er_pattern, *r_pattern;
-extern fastgrep_t *fg_pattern;
-
-/* For regex errors  */
-#define RE_ERROR_BUF	512
-extern char	 re_error[RE_ERROR_BUF + 1];	/* Seems big enough */
-
-/* util.c */
-bool	 file_matching(const char *fname);
-int	 procfile(const char *fn);
-int	 grep_tree(char **argv);
-void	*grep_malloc(size_t size);
-void	*grep_calloc(size_t nmemb, size_t size);
-void	*grep_realloc(void *ptr, size_t size);
-char	*grep_strdup(const char *str);
-void	 printline(struct str *line, int sep, regmatch_t *matches, int m);
-
-/* queue.c */
-void	 enqueue(struct str *x);
-void	 printqueue(void);
-void	 clearqueue(void);
-
-/* file.c */
-void		 grep_close(struct file *f);
-struct file	*grep_open(const char *path);
-char		*grep_fgetln(struct file *f, size_t *len);
-
-/* fastgrep.c */
-int		 fastcomp(fastgrep_t *, const char *);
-void		 fgrepcomp(fastgrep_t *, const char *);
-int		 grep_search(fastgrep_t *, const unsigned char *, size_t, regmatch_t *);
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/queue.c b/toolbox/upstream-netbsd/usr.bin/grep/queue.c
deleted file mode 100644
index e3c6be1..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/queue.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/*	$NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $	*/
-/*	$FreeBSD: head/usr.bin/grep/queue.c 211496 2010-08-19 09:28:59Z des $	*/
-/*-
- * 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.
- */
-
-/*
- * A really poor man's queue.  It does only what it has to and gets out of
- * Dodge.  It is used in place of <sys/queue.h> to get a better performance.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $");
-
-#include <sys/param.h>
-#include <sys/queue.h>
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "grep.h"
-
-struct qentry {
-	STAILQ_ENTRY(qentry)	list;
-	struct str	 	data;
-};
-
-static STAILQ_HEAD(, qentry)	queue = STAILQ_HEAD_INITIALIZER(queue);
-static unsigned long long	count;
-
-static struct qentry	*dequeue(void);
-
-void
-enqueue(struct str *x)
-{
-	struct qentry *item;
-
-	item = grep_malloc(sizeof(struct qentry));
-	item->data.dat = grep_malloc(sizeof(char) * x->len);
-	item->data.len = x->len;
-	item->data.line_no = x->line_no;
-	item->data.off = x->off;
-	memcpy(item->data.dat, x->dat, x->len);
-	item->data.file = x->file;
-
-	STAILQ_INSERT_TAIL(&queue, item, list);
-
-	if (++count > Bflag) {
-		item = dequeue();
-		free(item->data.dat);
-		free(item);
-	}
-}
-
-static struct qentry *
-dequeue(void)
-{
-	struct qentry *item;
-
-	item = STAILQ_FIRST(&queue);
-	if (item == NULL)
-		return (NULL);
-
-	STAILQ_REMOVE_HEAD(&queue, list);
-	--count;
-	return (item);
-}
-
-void
-printqueue(void)
-{
-	struct qentry *item;
-
-	while ((item = dequeue()) != NULL) {
-		printline(&item->data, '-', NULL, 0);
-		free(item->data.dat);
-		free(item);
-	}
-}
-
-void
-clearqueue(void)
-{
-	struct qentry *item;
-
-	while ((item = dequeue()) != NULL) {
-		free(item->data.dat);
-		free(item);
-	}
-}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/util.c b/toolbox/upstream-netbsd/usr.bin/grep/util.c
deleted file mode 100644
index ecd948d..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/util.c
+++ /dev/null
@@ -1,499 +0,0 @@
-/*	$NetBSD: util.c,v 1.17 2013/01/21 03:24:43 msaitoh Exp $	*/
-/*	$FreeBSD: head/usr.bin/grep/util.c 211496 2010-08-19 09:28:59Z des $	*/
-/*	$OpenBSD: util.c,v 1.39 2010/07/02 22:18:03 tedu Exp $	*/
-
-/*-
- * 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.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: util.c,v 1.17 2013/01/21 03:24:43 msaitoh Exp $");
-
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <fnmatch.h>
-#include <fts.h>
-#include <libgen.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <wchar.h>
-#include <wctype.h>
-
-#include "grep.h"
-
-static bool	 first, first_global = true;
-static unsigned long long since_printed;
-
-static int	 procline(struct str *l, int);
-
-bool
-file_matching(const char *fname)
-{
-	char *fname_base, *fname_copy;
-	unsigned int i;
-	bool ret;
-
-	ret = finclude ? false : true;
-	fname_copy = grep_strdup(fname);
-	fname_base = basename(fname_copy);
-
-	for (i = 0; i < fpatterns; ++i) {
-		if (fnmatch(fpattern[i].pat, fname, 0) == 0 ||
-		    fnmatch(fpattern[i].pat, fname_base, 0) == 0) {
-			if (fpattern[i].mode == EXCL_PAT) {
-				free(fname_copy);
-				return (false);
-			} else
-				ret = true;
-		}
-	}
-	free(fname_copy);
-	return (ret);
-}
-
-static inline bool
-dir_matching(const char *dname)
-{
-	unsigned int i;
-	bool ret;
-
-	ret = dinclude ? false : true;
-
-	for (i = 0; i < dpatterns; ++i) {
-		if (dname != NULL &&
-		    fnmatch(dname, dpattern[i].pat, 0) == 0) {
-			if (dpattern[i].mode == EXCL_PAT)
-				return (false);
-			else
-				ret = true;
-		}
-	}
-	return (ret);
-}
-
-/*
- * Processes a directory when a recursive search is performed with
- * the -R option.  Each appropriate file is passed to procfile().
- */
-int
-grep_tree(char **argv)
-{
-	FTS *fts;
-	FTSENT *p;
-	char *d, *dir = NULL;
-	int c, fts_flags;
-	bool ok;
-
-	c = fts_flags = 0;
-
-	switch(linkbehave) {
-	case LINK_EXPLICIT:
-		fts_flags = FTS_COMFOLLOW;
-		break;
-	case LINK_SKIP:
-		fts_flags = FTS_PHYSICAL;
-		break;
-	default:
-		fts_flags = FTS_LOGICAL;
-			
-	}
-
-	fts_flags |= FTS_NOSTAT | FTS_NOCHDIR;
-
-	if (!(fts = fts_open(argv, fts_flags, NULL)))
-		err(2, "fts_open");
-	while ((p = fts_read(fts)) != NULL) {
-		switch (p->fts_info) {
-		case FTS_DNR:
-			/* FALLTHROUGH */
-		case FTS_ERR:
-			errx(2, "%s: %s", p->fts_path, strerror(p->fts_errno));
-			break;
-		case FTS_D:
-			/* FALLTHROUGH */
-		case FTS_DP:
-			break;
-		case FTS_DC:
-			/* Print a warning for recursive directory loop */
-			warnx("warning: %s: recursive directory loop",
-				p->fts_path);
-			break;
-		default:
-			/* Check for file exclusion/inclusion */
-			ok = true;
-			if (dexclude || dinclude) {
-				if ((d = strrchr(p->fts_path, '/')) != NULL) {
-					dir = grep_malloc(sizeof(char) *
-					    (d - p->fts_path + 1));
-					memcpy(dir, p->fts_path,
-					    d - p->fts_path);
-					dir[d - p->fts_path] = '\0';
-				}
-				ok = dir_matching(dir);
-				free(dir);
-				dir = NULL;
-			}
-			if (fexclude || finclude)
-				ok &= file_matching(p->fts_path);
-
-			if (ok)
-				c += procfile(p->fts_path);
-			break;
-		}
-	}
-
-	fts_close(fts);
-	return (c);
-}
-
-/*
- * Opens a file and processes it.  Each file is processed line-by-line
- * passing the lines to procline().
- */
-int
-procfile(const char *fn)
-{
-	struct file *f;
-	struct stat sb;
-	struct str ln;
-	mode_t s;
-	int c, t;
-
-	if (mflag && (mcount <= 0))
-		return (0);
-
-	if (strcmp(fn, "-") == 0) {
-		fn = label != NULL ? label : getstr(1);
-		f = grep_open(NULL);
-	} else {
-		if (!stat(fn, &sb)) {
-			/* Check if we need to process the file */
-			s = sb.st_mode & S_IFMT;
-			if (s == S_IFDIR && dirbehave == DIR_SKIP)
-				return (0);
-			if ((s == S_IFIFO || s == S_IFCHR || s == S_IFBLK
-				|| s == S_IFSOCK) && devbehave == DEV_SKIP)
-					return (0);
-		}
-		f = grep_open(fn);
-	}
-	if (f == NULL) {
-		if (!sflag)
-			warn("%s", fn);
-		if (errno == ENOENT)
-			notfound = true;
-		return (0);
-	}
-
-	ln.file = grep_malloc(strlen(fn) + 1);
-	strcpy(ln.file, fn);
-	ln.line_no = 0;
-	ln.len = 0;
-	tail = 0;
-	ln.off = -1;
-
-	for (first = true, c = 0;  c == 0 || !(lflag || qflag); ) {
-		ln.off += ln.len + 1;
-		if ((ln.dat = grep_fgetln(f, &ln.len)) == NULL || ln.len == 0)
-			break;
-		if (ln.len > 0 && ln.dat[ln.len - 1] == line_sep)
-			--ln.len;
-		ln.line_no++;
-
-		/* Return if we need to skip a binary file */
-		if (f->binary && binbehave == BINFILE_SKIP) {
-			grep_close(f);
-			free(ln.file);
-			free(f);
-			return (0);
-		}
-		/* Process the file line-by-line */
-		t = procline(&ln, f->binary);
-		c += t;
-
-		/* Count the matches if we have a match limit */
-		if (mflag) {
-			mcount -= t;
-			if (mcount <= 0)
-				break;
-		}
-	}
-	if (Bflag > 0)
-		clearqueue();
-	grep_close(f);
-
-	if (cflag) {
-		if (!hflag)
-			printf("%s:", ln.file);
-		printf("%u%c", c, line_sep);
-	}
-	if (lflag && !qflag && c != 0)
-		printf("%s%c", fn, line_sep);
-	if (Lflag && !qflag && c == 0)
-		printf("%s%c", fn, line_sep);
-	if (c && !cflag && !lflag && !Lflag &&
-	    binbehave == BINFILE_BIN && f->binary && !qflag)
-		printf(getstr(8), fn);
-
-	free(ln.file);
-	free(f);
-	return (c);
-}
-
-#define iswword(x)	(iswalnum((x)) || (x) == L'_')
-
-/*
- * Processes a line comparing it with the specified patterns.  Each pattern
- * is looped to be compared along with the full string, saving each and every
- * match, which is necessary to colorize the output and to count the
- * matches.  The matching lines are passed to printline() to display the
- * appropriate output.
- */
-static int
-procline(struct str *l, int nottext)
-{
-	regmatch_t matches[MAX_LINE_MATCHES];
-	regmatch_t pmatch;
-	size_t st = 0;
-	unsigned int i;
-	int c = 0, m = 0, r = 0;
-
-	/* Loop to process the whole line */
-	while (st <= l->len) {
-		pmatch.rm_so = st;
-		pmatch.rm_eo = l->len;
-
-		/* Loop to compare with all the patterns */
-		for (i = 0; i < patterns; i++) {
-/*
- * XXX: grep_search() is a workaround for speed up and should be
- * removed in the future.  See fastgrep.c.
- */
-			if (fg_pattern[i].pattern) {
-				r = grep_search(&fg_pattern[i],
-				    (unsigned char *)l->dat,
-				    l->len, &pmatch);
-				r = (r == 0) ? 0 : REG_NOMATCH;
-				st = pmatch.rm_eo;
-			} else {
-				r = regexec(&r_pattern[i], l->dat, 1,
-				    &pmatch, eflags);
-				r = (r == 0) ? 0 : REG_NOMATCH;
-				st = pmatch.rm_eo;
-			}
-			if (r == REG_NOMATCH)
-				continue;
-			/* Check for full match */
-			if (xflag &&
-			    (pmatch.rm_so != 0 ||
-			     (size_t)pmatch.rm_eo != l->len))
-				continue;
-			/* Check for whole word match */
-			if (fg_pattern[i].word && pmatch.rm_so != 0) {
-				wchar_t wbegin, wend;
-
-				wbegin = wend = L' ';
-				if (pmatch.rm_so != 0 &&
-				    sscanf(&l->dat[pmatch.rm_so - 1],
-				    "%lc", &wbegin) != 1)
-					continue;
-				if ((size_t)pmatch.rm_eo != l->len &&
-				    sscanf(&l->dat[pmatch.rm_eo],
-				    "%lc", &wend) != 1)
-					continue;
-				if (iswword(wbegin) || iswword(wend))
-					continue;
-			}
-			c = 1;
-			if (m < MAX_LINE_MATCHES)
-				matches[m++] = pmatch;
-			/* matches - skip further patterns */
-			if ((color != NULL && !oflag) || qflag || lflag)
-				break;
-		}
-
-		if (vflag) {
-			c = !c;
-			break;
-		}
-		/* One pass if we are not recording matches */
-		if ((color != NULL && !oflag) || qflag || lflag)
-			break;
-
-		if (st == (size_t)pmatch.rm_so)
-			break; 	/* No matches */
-	}
-
-	if (c && binbehave == BINFILE_BIN && nottext)
-		return (c); /* Binary file */
-
-	/* Dealing with the context */
-	if ((tail || c) && !cflag && !qflag && !lflag && !Lflag) {
-		if (c) {
-			if ((Aflag || Bflag) && !first_global &&
-			    (first || since_printed > Bflag))
-				printf("--\n");
-			tail = Aflag;
-			if (Bflag > 0)
-				printqueue();
-			printline(l, ':', matches, m);
-		} else {
-			printline(l, '-', matches, m);
-			tail--;
-		}
-		first = false;
-		first_global = false;
-		since_printed = 0;
-	} else {
-		if (Bflag)
-			enqueue(l);
-		since_printed++;
-	}
-	return (c);
-}
-
-/*
- * Safe malloc() for internal use.
- */
-void *
-grep_malloc(size_t size)
-{
-	void *ptr;
-
-	if ((ptr = malloc(size)) == NULL)
-		err(2, "malloc");
-	return (ptr);
-}
-
-/*
- * Safe calloc() for internal use.
- */
-void *
-grep_calloc(size_t nmemb, size_t size)
-{
-	void *ptr;
-
-	if ((ptr = calloc(nmemb, size)) == NULL)
-		err(2, "calloc");
-	return (ptr);
-}
-
-/*
- * Safe realloc() for internal use.
- */
-void *
-grep_realloc(void *ptr, size_t size)
-{
-
-	if ((ptr = realloc(ptr, size)) == NULL)
-		err(2, "realloc");
-	return (ptr);
-}
-
-/*
- * Safe strdup() for internal use.
- */
-char *
-grep_strdup(const char *str)
-{
-	char *ret;
-
-	if ((ret = strdup(str)) == NULL)
-		err(2, "strdup");
-	return (ret);
-}
-
-/*
- * Prints a matching line according to the command line options.
- */
-void
-printline(struct str *line, int sep, regmatch_t *matches, int m)
-{
-	size_t a = 0;
-	int i, n = 0;
-
-	if (!hflag) {
-		if (nullflag == 0)
-			fputs(line->file, stdout);
-		else {
-			printf("%s", line->file);
-			putchar(0);
-		}
-		++n;
-	}
-	if (nflag) {
-		if (n > 0)
-			putchar(sep);
-		printf("%d", line->line_no);
-		++n;
-	}
-	if (bflag) {
-		if (n > 0)
-			putchar(sep);
-		printf("%lld", (long long)line->off);
-		++n;
-	}
-	if (n)
-		putchar(sep);
-	/* --color and -o */
-	if ((oflag || color) && m > 0) {
-		for (i = 0; i < m; i++) {
-			if (!oflag)
-				fwrite(line->dat + a, matches[i].rm_so - a, 1,
-				    stdout);
-			if (color) 
-				fprintf(stdout, "\33[%sm\33[K", color);
-
-				fwrite(line->dat + matches[i].rm_so, 
-				    matches[i].rm_eo - matches[i].rm_so, 1,
-				    stdout);
-			if (color) 
-				fprintf(stdout, "\33[m\33[K");
-			a = matches[i].rm_eo;
-			if (oflag)
-				putchar('\n');
-		}
-		if (!oflag) {
-			if (line->len - a > 0)
-				fwrite(line->dat + a, line->len - a, 1, stdout);
-			putchar(line_sep);
-		}
-	} else {
-		fwrite(line->dat, line->len, 1, stdout);
-		putchar(line_sep);
-	}
-}
diff --git a/trusty/OWNERS b/trusty/OWNERS
index 357b4f4..e807d71 100644
--- a/trusty/OWNERS
+++ b/trusty/OWNERS
@@ -1,3 +1,7 @@
-bohr@google.com
-swillden@google.com
+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 65b271a..1666cfb 100644
--- a/trusty/gatekeeper/Android.bp
+++ b/trusty/gatekeeper/Android.bp
@@ -1,4 +1,3 @@
-//
 // Copyright (C) 2015 The Android Open-Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,14 +19,15 @@
 // to only building on ARM if they include assembly. Individual makefiles
 // are responsible for having their own logic, for fine-grained control.
 
-cc_library_shared {
-    name: "gatekeeper.trusty",
+cc_binary {
+    name: "android.hardware.gatekeeper@1.0-service.trusty",
+    defaults: ["hidl_defaults"],
     vendor: true,
-
     relative_install_path: "hw",
+    init_rc: ["android.hardware.gatekeeper@1.0-service.trusty.rc"],
 
     srcs: [
-        "module.cpp",
+        "service.cpp",
         "trusty_gatekeeper_ipc.c",
         "trusty_gatekeeper.cpp",
     ],
@@ -39,10 +39,16 @@
     ],
 
     shared_libs: [
+        "android.hardware.gatekeeper@1.0",
+        "libbase",
+        "libhidlbase",
+        "libhidltransport",
         "libgatekeeper",
+        "libutils",
         "liblog",
         "libcutils",
         "libtrusty",
     ],
-    header_libs: ["libhardware_headers"],
+
+    vintf_fragments: ["android.hardware.gatekeeper@1.0-service.trusty.xml"],
 }
diff --git a/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.rc b/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.rc
new file mode 100644
index 0000000..5413a6c
--- /dev/null
+++ b/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.rc
@@ -0,0 +1,4 @@
+service vendor.gatekeeper-1-0 /vendor/bin/hw/android.hardware.gatekeeper@1.0-service.trusty
+    class hal
+    user system
+    group system
diff --git a/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.xml b/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.xml
new file mode 100644
index 0000000..19714a8
--- /dev/null
+++ b/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.gatekeeper</name>
+        <transport>hwbinder</transport>
+        <version>1.0</version>
+        <interface>
+        <name>IGatekeeper</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/trusty/gatekeeper/module.cpp b/trusty/gatekeeper/module.cpp
deleted file mode 100644
index 0ee3c2f..0000000
--- a/trusty/gatekeeper/module.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.
- */
-
-#include <hardware/hardware.h>
-
-#include <string.h>
-#include <errno.h>
-#include <stdlib.h>
-
-#include "trusty_gatekeeper.h"
-
-using gatekeeper::TrustyGateKeeperDevice;
-
-static int trusty_gatekeeper_open(const hw_module_t *module, const char *name,
-        hw_device_t **device) {
-
-    if (strcmp(name, HARDWARE_GATEKEEPER) != 0) {
-        return -EINVAL;
-    }
-
-    TrustyGateKeeperDevice *gatekeeper = new TrustyGateKeeperDevice(module);
-    if (gatekeeper == NULL) return -ENOMEM;
-    *device = gatekeeper->hw_device();
-
-    return 0;
-}
-
-static struct hw_module_methods_t gatekeeper_module_methods = {
-    .open = trusty_gatekeeper_open,
-};
-
-struct gatekeeper_module HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
-    .common = {
-        .tag = HARDWARE_MODULE_TAG,
-        .module_api_version = GATEKEEPER_MODULE_API_VERSION_0_1,
-        .hal_api_version = HARDWARE_HAL_API_VERSION,
-        .id = GATEKEEPER_HARDWARE_MODULE_ID,
-        .name = "Trusty GateKeeper HAL",
-        .author = "The Android Open Source Project",
-        .methods = &gatekeeper_module_methods,
-        .dso = 0,
-        .reserved = {}
-    },
-};
diff --git a/trusty/gatekeeper/service.cpp b/trusty/gatekeeper/service.cpp
new file mode 100644
index 0000000..c5ee488
--- /dev/null
+++ b/trusty/gatekeeper/service.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "android.hardware.gatekeeper@1.0-service.trusty"
+
+#include <android-base/logging.h>
+#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
+
+#include <hidl/LegacySupport.h>
+
+#include "trusty_gatekeeper.h"
+
+// Generated HIDL files
+using android::hardware::gatekeeper::V1_0::IGatekeeper;
+using gatekeeper::TrustyGateKeeperDevice;
+
+int main() {
+    ::android::hardware::configureRpcThreadpool(1, true /* willJoinThreadpool */);
+    android::sp<TrustyGateKeeperDevice> gatekeeper(new TrustyGateKeeperDevice());
+    auto status = gatekeeper->registerAsService();
+    if (status != android::OK) {
+        LOG(FATAL) << "Could not register service for Gatekeeper 1.0 (trusty) (" << status << ")";
+    }
+
+    android::hardware::joinRpcThreadpool();
+    return -1;  // Should never get here.
+}
diff --git a/trusty/gatekeeper/trusty_gatekeeper.cpp b/trusty/gatekeeper/trusty_gatekeeper.cpp
index b3fbfa9..d149664 100644
--- a/trusty/gatekeeper/trusty_gatekeeper.cpp
+++ b/trusty/gatekeeper/trusty_gatekeeper.cpp
@@ -16,147 +16,131 @@
 
 #define LOG_TAG "TrustyGateKeeper"
 
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-
-#include <type_traits>
-
-#include <log/log.h>
+#include <android-base/logging.h>
+#include <limits>
 
 #include "trusty_gatekeeper.h"
 #include "trusty_gatekeeper_ipc.h"
 #include "gatekeeper_ipc.h"
 
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::gatekeeper::V1_0::GatekeeperStatusCode;
+using ::gatekeeper::EnrollRequest;
+using ::gatekeeper::EnrollResponse;
+using ::gatekeeper::ERROR_INVALID;
+using ::gatekeeper::ERROR_MEMORY_ALLOCATION_FAILED;
+using ::gatekeeper::ERROR_NONE;
+using ::gatekeeper::ERROR_RETRY;
+using ::gatekeeper::SizedBuffer;
+using ::gatekeeper::VerifyRequest;
+using ::gatekeeper::VerifyResponse;
+
 namespace gatekeeper {
 
-const uint32_t SEND_BUF_SIZE = 8192;
-const uint32_t RECV_BUF_SIZE = 8192;
+constexpr const uint32_t SEND_BUF_SIZE = 8192;
+constexpr const uint32_t RECV_BUF_SIZE = 8192;
 
-TrustyGateKeeperDevice::TrustyGateKeeperDevice(const hw_module_t *module) {
-#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
-    static_assert(std::is_standard_layout<TrustyGateKeeperDevice>::value,
-                  "TrustyGateKeeperDevice must be standard layout");
-    static_assert(offsetof(TrustyGateKeeperDevice, device_) == 0,
-                  "device_ must be the first member of TrustyGateKeeperDevice");
-    static_assert(offsetof(TrustyGateKeeperDevice, device_.common) == 0,
-                  "common must be the first member of gatekeeper_device");
-#else
-    assert(reinterpret_cast<gatekeeper_device_t *>(this) == &device_);
-    assert(reinterpret_cast<hw_device_t *>(this) == &(device_.common));
-#endif
-
-    memset(&device_, 0, sizeof(device_));
-    device_.common.tag = HARDWARE_DEVICE_TAG;
-    device_.common.version = 1;
-    device_.common.module = const_cast<hw_module_t *>(module);
-    device_.common.close = close_device;
-
-    device_.enroll = enroll;
-    device_.verify = verify;
-    device_.delete_user = nullptr;
-    device_.delete_all_users = nullptr;
-
+TrustyGateKeeperDevice::TrustyGateKeeperDevice() {
     int rc = trusty_gatekeeper_connect();
     if (rc < 0) {
-        ALOGE("Error initializing trusty session: %d", rc);
+        LOG(ERROR) << "Error initializing trusty session: " << rc;
     }
 
     error_ = rc;
-
-}
-
-hw_device_t* TrustyGateKeeperDevice::hw_device() {
-    return &device_.common;
-}
-
-int TrustyGateKeeperDevice::close_device(hw_device_t* dev) {
-    delete reinterpret_cast<TrustyGateKeeperDevice *>(dev);
-    return 0;
 }
 
 TrustyGateKeeperDevice::~TrustyGateKeeperDevice() {
     trusty_gatekeeper_disconnect();
 }
 
-int TrustyGateKeeperDevice::Enroll(uint32_t uid, const uint8_t *current_password_handle,
-        uint32_t current_password_handle_length, const uint8_t *current_password,
-        uint32_t current_password_length, const uint8_t *desired_password,
-        uint32_t desired_password_length, uint8_t **enrolled_password_handle,
-        uint32_t *enrolled_password_handle_length) {
-
-    if (error_ != 0) {
-        return error_;
-    }
-
-    SizedBuffer desired_password_buffer(desired_password_length);
-    memcpy(desired_password_buffer.buffer.get(), desired_password, desired_password_length);
-
-    SizedBuffer current_password_handle_buffer(current_password_handle_length);
-    if (current_password_handle) {
-        memcpy(current_password_handle_buffer.buffer.get(), current_password_handle,
-                current_password_handle_length);
-    }
-
-    SizedBuffer current_password_buffer(current_password_length);
-    if (current_password) {
-        memcpy(current_password_buffer.buffer.get(), current_password, current_password_length);
-    }
-
-    EnrollRequest request(uid, &current_password_handle_buffer, &desired_password_buffer,
-            &current_password_buffer);
-    EnrollResponse response;
-
-    gatekeeper_error_t error = Send(request, &response);
-
-    if (error == ERROR_RETRY) {
-        return response.retry_timeout;
-    } else if (error != ERROR_NONE) {
-        return -EINVAL;
-    }
-
-    *enrolled_password_handle = response.enrolled_password_handle.buffer.release();
-    *enrolled_password_handle_length = response.enrolled_password_handle.length;
-
-
-    return 0;
+SizedBuffer hidl_vec2sized_buffer(const hidl_vec<uint8_t>& vec) {
+    if (vec.size() == 0 || vec.size() > std::numeric_limits<uint32_t>::max()) return {};
+    auto dummy = new uint8_t[vec.size()];
+    std::copy(vec.begin(), vec.end(), dummy);
+    return {dummy, static_cast<uint32_t>(vec.size())};
 }
 
-int TrustyGateKeeperDevice::Verify(uint32_t uid, uint64_t challenge,
-        const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
-        const uint8_t *provided_password, uint32_t provided_password_length,
-        uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) {
+Return<void> TrustyGateKeeperDevice::enroll(uint32_t uid,
+                                            const hidl_vec<uint8_t>& currentPasswordHandle,
+                                            const hidl_vec<uint8_t>& currentPassword,
+                                            const hidl_vec<uint8_t>& desiredPassword,
+                                            enroll_cb _hidl_cb) {
     if (error_ != 0) {
-        return error_;
+        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+        return {};
     }
 
-    SizedBuffer password_handle_buffer(enrolled_password_handle_length);
-    memcpy(password_handle_buffer.buffer.get(), enrolled_password_handle,
-            enrolled_password_handle_length);
-    SizedBuffer provided_password_buffer(provided_password_length);
-    memcpy(provided_password_buffer.buffer.get(), provided_password, provided_password_length);
+    if (desiredPassword.size() == 0) {
+        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+        return {};
+    }
 
-    VerifyRequest request(uid, challenge, &password_handle_buffer, &provided_password_buffer);
+    EnrollRequest request(uid, hidl_vec2sized_buffer(currentPasswordHandle),
+                          hidl_vec2sized_buffer(desiredPassword),
+                          hidl_vec2sized_buffer(currentPassword));
+    EnrollResponse response;
+    auto error = Send(request, &response);
+    if (error != ERROR_NONE) {
+        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+    } else if (response.error == ERROR_RETRY) {
+        _hidl_cb({GatekeeperStatusCode::ERROR_RETRY_TIMEOUT, response.retry_timeout, {}});
+    } else if (response.error != ERROR_NONE) {
+        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+    } else {
+        hidl_vec<uint8_t> new_handle(response.enrolled_password_handle.Data<uint8_t>(),
+                                     response.enrolled_password_handle.Data<uint8_t>() +
+                                             response.enrolled_password_handle.size());
+        _hidl_cb({GatekeeperStatusCode::STATUS_OK, response.retry_timeout, new_handle});
+    }
+    return {};
+}
+
+Return<void> TrustyGateKeeperDevice::verify(
+        uint32_t uid, uint64_t challenge,
+        const ::android::hardware::hidl_vec<uint8_t>& enrolledPasswordHandle,
+        const ::android::hardware::hidl_vec<uint8_t>& providedPassword, verify_cb _hidl_cb) {
+    if (error_ != 0) {
+        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+        return {};
+    }
+
+    if (enrolledPasswordHandle.size() == 0) {
+        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+        return {};
+    }
+
+    VerifyRequest request(uid, challenge, hidl_vec2sized_buffer(enrolledPasswordHandle),
+                          hidl_vec2sized_buffer(providedPassword));
     VerifyResponse response;
 
-    gatekeeper_error_t error = Send(request, &response);
+    auto error = Send(request, &response);
+    if (error != ERROR_NONE) {
+        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+    } else if (response.error == ERROR_RETRY) {
+        _hidl_cb({GatekeeperStatusCode::ERROR_RETRY_TIMEOUT, response.retry_timeout, {}});
+    } else if (response.error != ERROR_NONE) {
+        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+    } else {
+        hidl_vec<uint8_t> auth_token(
+                response.auth_token.Data<uint8_t>(),
+                response.auth_token.Data<uint8_t>() + response.auth_token.size());
 
-    if (error == ERROR_RETRY) {
-        return response.retry_timeout;
-    } else if (error != ERROR_NONE) {
-        return -EINVAL;
+        _hidl_cb({response.request_reenroll ? GatekeeperStatusCode::STATUS_REENROLL
+                                            : GatekeeperStatusCode::STATUS_OK,
+                  response.retry_timeout, auth_token});
     }
+    return {};
+}
 
-    if (auth_token != NULL && auth_token_length != NULL) {
-       *auth_token = response.auth_token.buffer.release();
-       *auth_token_length = response.auth_token.length;
-    }
+Return<void> TrustyGateKeeperDevice::deleteUser(uint32_t /*uid*/, deleteUser_cb _hidl_cb) {
+    _hidl_cb({GatekeeperStatusCode::ERROR_NOT_IMPLEMENTED, 0, {}});
+    return {};
+}
 
-    if (request_reenroll != NULL) {
-        *request_reenroll = response.request_reenroll;
-    }
-
-    return 0;
+Return<void> TrustyGateKeeperDevice::deleteAllUsers(deleteAllUsers_cb _hidl_cb) {
+    _hidl_cb({GatekeeperStatusCode::ERROR_NOT_IMPLEMENTED, 0, {}});
+    return {};
 }
 
 gatekeeper_error_t TrustyGateKeeperDevice::Send(uint32_t command, const GateKeeperMessage& request,
@@ -172,7 +156,7 @@
     uint32_t response_size = RECV_BUF_SIZE;
     int rc = trusty_gatekeeper_call(command, send_buf, request_size, recv_buf, &response_size);
     if (rc < 0) {
-        ALOGE("error (%d) calling gatekeeper TA", rc);
+        LOG(ERROR) << "error (" << rc << ") calling gatekeeper TA";
         return ERROR_INVALID;
     }
 
@@ -182,51 +166,4 @@
     return response->Deserialize(payload, payload + response_size);
 }
 
-static inline TrustyGateKeeperDevice *convert_device(const gatekeeper_device *dev) {
-    return reinterpret_cast<TrustyGateKeeperDevice *>(const_cast<gatekeeper_device *>(dev));
-}
-
-/* static */
-int TrustyGateKeeperDevice::enroll(const struct gatekeeper_device *dev, uint32_t uid,
-            const uint8_t *current_password_handle, uint32_t current_password_handle_length,
-            const uint8_t *current_password, uint32_t current_password_length,
-            const uint8_t *desired_password, uint32_t desired_password_length,
-            uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) {
-
-    if (dev == NULL ||
-            enrolled_password_handle == NULL || enrolled_password_handle_length == NULL ||
-            desired_password == NULL || desired_password_length == 0)
-        return -EINVAL;
-
-    // Current password and current password handle go together
-    if (current_password_handle == NULL || current_password_handle_length == 0 ||
-            current_password == NULL || current_password_length == 0) {
-        current_password_handle = NULL;
-        current_password_handle_length = 0;
-        current_password = NULL;
-        current_password_length = 0;
-    }
-
-    return convert_device(dev)->Enroll(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);
-
-}
-
-/* static */
-int TrustyGateKeeperDevice::verify(const struct gatekeeper_device *dev, uint32_t uid,
-        uint64_t challenge, const uint8_t *enrolled_password_handle,
-        uint32_t enrolled_password_handle_length, const uint8_t *provided_password,
-        uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length,
-        bool *request_reenroll) {
-
-    if (dev == NULL || enrolled_password_handle == NULL ||
-            provided_password == NULL) {
-        return -EINVAL;
-    }
-
-    return convert_device(dev)->Verify(uid, challenge, enrolled_password_handle,
-            enrolled_password_handle_length, provided_password, provided_password_length,
-            auth_token, auth_token_length, request_reenroll);
-}
 };
diff --git a/trusty/gatekeeper/trusty_gatekeeper.h b/trusty/gatekeeper/trusty_gatekeeper.h
index 2becc49..c0713f4 100644
--- a/trusty/gatekeeper/trusty_gatekeeper.h
+++ b/trusty/gatekeeper/trusty_gatekeeper.h
@@ -17,84 +17,34 @@
 #ifndef TRUSTY_GATEKEEPER_H
 #define TRUSTY_GATEKEEPER_H
 
-#include <hardware/gatekeeper.h>
+#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
+#include <hidl/Status.h>
+
+#include <memory>
+
 #include <gatekeeper/gatekeeper_messages.h>
 
 #include "gatekeeper_ipc.h"
 
 namespace gatekeeper {
 
-class TrustyGateKeeperDevice {
-    public:
-
-    explicit TrustyGateKeeperDevice(const hw_module_t* module);
+class TrustyGateKeeperDevice : public ::android::hardware::gatekeeper::V1_0::IGatekeeper {
+  public:
+    explicit TrustyGateKeeperDevice();
     ~TrustyGateKeeperDevice();
-
-    hw_device_t* hw_device();
-
     /**
      * Enrolls password_payload, which should be derived from a user selected pin or password,
      * with the authentication factor private key used only for enrolling authentication
      * factor data.
      *
      * Returns: 0 on success or an error code less than 0 on error.
-     * On error, enrolled_password will not be allocated.
-     */
-    int Enroll(uint32_t uid, const uint8_t *current_password_handle,
-            uint32_t current_password_handle_length, const uint8_t *current_password,
-            uint32_t current_password_length, const uint8_t *desired_password,
-            uint32_t desired_password_length, uint8_t **enrolled_password_handle,
-            uint32_t *enrolled_password_handle_length);
-
-    /**
-     * Verifies provided_password matches expected_password after enrolling
-     * with the authentication factor private key.
-     *
-     * Implementations of this module may retain the result of this call
-     * to attest to the recency of authentication.
-     *
-     * On success, writes the address of a verification token to verification_token,
-     *
-     * Returns: 0 on success or an error code less than 0 on error
-     * On error, verification token will not be allocated
-     */
-    int Verify(uint32_t uid, uint64_t challenge, const uint8_t *enrolled_password_handle,
-            uint32_t enrolled_password_handle_length, const uint8_t *provided_password,
-            uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length,
-            bool *request_reenroll);
-
-    private:
-
-    gatekeeper_error_t Send(uint32_t command, const GateKeeperMessage& request,
-                           GateKeeperMessage* response);
-
-    gatekeeper_error_t Send(const EnrollRequest& request, EnrollResponse *response) {
-        return Send(GK_ENROLL, request, response);
-    }
-
-    gatekeeper_error_t Send(const VerifyRequest& request, VerifyResponse *response) {
-        return Send(GK_VERIFY, request, response);
-    }
-
-    // Static methods interfacing the HAL API with the TrustyGateKeeper device
-
-    /**
-     * Enrolls desired_password, which should be derived from a user selected pin or password,
-     * with the authentication factor private key used only for enrolling authentication
-     * factor data.
-     *
-     * If there was already a password enrolled, it should be provided in
-     * current_password_handle, along with the current password in current_password
-     * that should validate against current_password_handle.
-     *
-     * Returns: 0 on success or an error code less than 0 on error.
      * On error, enrolled_password_handle will not be allocated.
      */
-    static int enroll(const struct gatekeeper_device *dev, uint32_t uid,
-            const uint8_t *current_password_handle, uint32_t current_password_handle_length,
-            const uint8_t *current_password, uint32_t current_password_length,
-            const uint8_t *desired_password, uint32_t desired_password_length,
-            uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length);
+    ::android::hardware::Return<void> enroll(
+            uint32_t uid, const ::android::hardware::hidl_vec<uint8_t>& currentPasswordHandle,
+            const ::android::hardware::hidl_vec<uint8_t>& currentPassword,
+            const ::android::hardware::hidl_vec<uint8_t>& desiredPassword,
+            enroll_cb _hidl_cb) override;
 
     /**
      * Verifies provided_password matches enrolled_password_handle.
@@ -109,18 +59,32 @@
      * Returns: 0 on success or an error code less than 0 on error
      * On error, verification token will not be allocated
      */
-    static int verify(const struct gatekeeper_device *dev, uint32_t uid, uint64_t challenge,
-            const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
-            const uint8_t *provided_password, uint32_t provided_password_length,
-            uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll);
+    ::android::hardware::Return<void> verify(
+            uint32_t uid, uint64_t challenge,
+            const ::android::hardware::hidl_vec<uint8_t>& enrolledPasswordHandle,
+            const ::android::hardware::hidl_vec<uint8_t>& providedPassword,
+            verify_cb _hidl_cb) override;
 
-    static int close_device(hw_device_t* dev);
+    ::android::hardware::Return<void> deleteUser(uint32_t uid, deleteUser_cb _hidl_cb) override;
 
-    gatekeeper_device device_;
+    ::android::hardware::Return<void> deleteAllUsers(deleteAllUsers_cb _hidl_cb) override;
+
+  private:
+    gatekeeper_error_t Send(uint32_t command, const GateKeeperMessage& request,
+                           GateKeeperMessage* response);
+
+    gatekeeper_error_t Send(const EnrollRequest& request, EnrollResponse *response) {
+        return Send(GK_ENROLL, request, response);
+    }
+
+    gatekeeper_error_t Send(const VerifyRequest& request, VerifyResponse *response) {
+        return Send(GK_VERIFY, request, response);
+    }
+
     int error_;
-
 };
-}
+
+}  // namespace gatekeeper
 
 #endif
 
diff --git a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
new file mode 100644
index 0000000..98cbcc3
--- /dev/null
+++ b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
@@ -0,0 +1,462 @@
+/*
+ **
+ ** 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.
+ */
+
+#define LOG_TAG "android.hardware.keymaster@3.0-impl.trusty"
+
+#include <authorization_set.h>
+#include <cutils/log.h>
+#include <keymaster/android_keymaster_messages.h>
+#include <trusty_keymaster/TrustyKeymaster3Device.h>
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
+
+using ::keymaster::AbortOperationRequest;
+using ::keymaster::AbortOperationResponse;
+using ::keymaster::AddEntropyRequest;
+using ::keymaster::AddEntropyResponse;
+using ::keymaster::AttestKeyRequest;
+using ::keymaster::AttestKeyResponse;
+using ::keymaster::AuthorizationSet;
+using ::keymaster::BeginOperationRequest;
+using ::keymaster::BeginOperationResponse;
+using ::keymaster::ExportKeyRequest;
+using ::keymaster::ExportKeyResponse;
+using ::keymaster::FinishOperationRequest;
+using ::keymaster::FinishOperationResponse;
+using ::keymaster::GenerateKeyRequest;
+using ::keymaster::GenerateKeyResponse;
+using ::keymaster::GetKeyCharacteristicsRequest;
+using ::keymaster::GetKeyCharacteristicsResponse;
+using ::keymaster::ImportKeyRequest;
+using ::keymaster::ImportKeyResponse;
+using ::keymaster::UpdateOperationRequest;
+using ::keymaster::UpdateOperationResponse;
+using ::keymaster::ng::Tag;
+
+namespace keymaster {
+
+namespace {
+
+inline keymaster_tag_t legacy_enum_conversion(const Tag value) {
+    return keymaster_tag_t(value);
+}
+inline Tag legacy_enum_conversion(const keymaster_tag_t value) {
+    return Tag(value);
+}
+inline keymaster_purpose_t legacy_enum_conversion(const KeyPurpose value) {
+    return keymaster_purpose_t(value);
+}
+inline keymaster_key_format_t legacy_enum_conversion(const KeyFormat value) {
+    return keymaster_key_format_t(value);
+}
+inline ErrorCode legacy_enum_conversion(const keymaster_error_t value) {
+    return ErrorCode(value);
+}
+
+inline keymaster_tag_type_t typeFromTag(const keymaster_tag_t tag) {
+    return keymaster_tag_get_type(tag);
+}
+
+class KmParamSet : public keymaster_key_param_set_t {
+  public:
+    KmParamSet(const hidl_vec<KeyParameter>& keyParams) {
+        params = new keymaster_key_param_t[keyParams.size()];
+        length = keyParams.size();
+        for (size_t i = 0; i < keyParams.size(); ++i) {
+            auto tag = legacy_enum_conversion(keyParams[i].tag);
+            switch (typeFromTag(tag)) {
+                case KM_ENUM:
+                case KM_ENUM_REP:
+                    params[i] = keymaster_param_enum(tag, keyParams[i].f.integer);
+                    break;
+                case KM_UINT:
+                case KM_UINT_REP:
+                    params[i] = keymaster_param_int(tag, keyParams[i].f.integer);
+                    break;
+                case KM_ULONG:
+                case KM_ULONG_REP:
+                    params[i] = keymaster_param_long(tag, keyParams[i].f.longInteger);
+                    break;
+                case KM_DATE:
+                    params[i] = keymaster_param_date(tag, keyParams[i].f.dateTime);
+                    break;
+                case KM_BOOL:
+                    if (keyParams[i].f.boolValue)
+                        params[i] = keymaster_param_bool(tag);
+                    else
+                        params[i].tag = KM_TAG_INVALID;
+                    break;
+                case KM_BIGNUM:
+                case KM_BYTES:
+                    params[i] = keymaster_param_blob(tag, &keyParams[i].blob[0],
+                                                     keyParams[i].blob.size());
+                    break;
+                case KM_INVALID:
+                default:
+                    params[i].tag = KM_TAG_INVALID;
+                    /* just skip */
+                    break;
+            }
+        }
+    }
+    KmParamSet(KmParamSet&& other) noexcept
+        : keymaster_key_param_set_t{other.params, other.length} {
+        other.length = 0;
+        other.params = nullptr;
+    }
+    KmParamSet(const KmParamSet&) = delete;
+    ~KmParamSet() { delete[] params; }
+};
+
+inline hidl_vec<uint8_t> kmBlob2hidlVec(const keymaster_key_blob_t& blob) {
+    hidl_vec<uint8_t> result;
+    result.setToExternal(const_cast<unsigned char*>(blob.key_material), blob.key_material_size);
+    return result;
+}
+
+inline hidl_vec<uint8_t> kmBlob2hidlVec(const keymaster_blob_t& blob) {
+    hidl_vec<uint8_t> result;
+    result.setToExternal(const_cast<unsigned char*>(blob.data), blob.data_length);
+    return result;
+}
+
+inline hidl_vec<uint8_t> kmBuffer2hidlVec(const ::keymaster::Buffer& buf) {
+    hidl_vec<uint8_t> result;
+    result.setToExternal(const_cast<unsigned char*>(buf.peek_read()), buf.available_read());
+    return result;
+}
+
+inline static hidl_vec<hidl_vec<uint8_t>> kmCertChain2Hidl(
+        const keymaster_cert_chain_t& cert_chain) {
+    hidl_vec<hidl_vec<uint8_t>> result;
+    if (!cert_chain.entry_count || !cert_chain.entries) return result;
+
+    result.resize(cert_chain.entry_count);
+    for (size_t i = 0; i < cert_chain.entry_count; ++i) {
+        result[i] = kmBlob2hidlVec(cert_chain.entries[i]);
+    }
+
+    return result;
+}
+
+static inline hidl_vec<KeyParameter> kmParamSet2Hidl(const keymaster_key_param_set_t& set) {
+    hidl_vec<KeyParameter> result;
+    if (set.length == 0 || set.params == nullptr) return result;
+
+    result.resize(set.length);
+    keymaster_key_param_t* params = set.params;
+    for (size_t i = 0; i < set.length; ++i) {
+        auto tag = params[i].tag;
+        result[i].tag = legacy_enum_conversion(tag);
+        switch (typeFromTag(tag)) {
+            case KM_ENUM:
+            case KM_ENUM_REP:
+                result[i].f.integer = params[i].enumerated;
+                break;
+            case KM_UINT:
+            case KM_UINT_REP:
+                result[i].f.integer = params[i].integer;
+                break;
+            case KM_ULONG:
+            case KM_ULONG_REP:
+                result[i].f.longInteger = params[i].long_integer;
+                break;
+            case KM_DATE:
+                result[i].f.dateTime = params[i].date_time;
+                break;
+            case KM_BOOL:
+                result[i].f.boolValue = params[i].boolean;
+                break;
+            case KM_BIGNUM:
+            case KM_BYTES:
+                result[i].blob.setToExternal(const_cast<unsigned char*>(params[i].blob.data),
+                                             params[i].blob.data_length);
+                break;
+            case KM_INVALID:
+            default:
+                params[i].tag = KM_TAG_INVALID;
+                /* just skip */
+                break;
+        }
+    }
+    return result;
+}
+
+void addClientAndAppData(const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,
+                         ::keymaster::AuthorizationSet* params) {
+    params->Clear();
+    if (clientId.size()) {
+        params->push_back(::keymaster::TAG_APPLICATION_ID, clientId.data(), clientId.size());
+    }
+    if (appData.size()) {
+        params->push_back(::keymaster::TAG_APPLICATION_DATA, appData.data(), appData.size());
+    }
+}
+
+}  // anonymous namespace
+
+TrustyKeymaster3Device::TrustyKeymaster3Device(TrustyKeymaster* impl) : impl_(impl) {}
+
+TrustyKeymaster3Device::~TrustyKeymaster3Device() {}
+
+Return<void> TrustyKeymaster3Device::getHardwareFeatures(getHardwareFeatures_cb _hidl_cb) {
+    _hidl_cb(true /* is_secure */, true /* supports_ec */,
+             true /* supports_symmetric_cryptography */, true /* supports_attestation */,
+             true /* supportsAllDigests */, "TrustyKeymaster", "Google");
+    return Void();
+}
+
+Return<ErrorCode> TrustyKeymaster3Device::addRngEntropy(const hidl_vec<uint8_t>& data) {
+    if (data.size() == 0) return ErrorCode::OK;
+    AddEntropyRequest request;
+    request.random_data.Reinitialize(data.data(), data.size());
+
+    AddEntropyResponse response;
+    impl_->AddRngEntropy(request, &response);
+
+    return legacy_enum_conversion(response.error);
+}
+
+Return<void> TrustyKeymaster3Device::generateKey(const hidl_vec<KeyParameter>& keyParams,
+                                                 generateKey_cb _hidl_cb) {
+    GenerateKeyRequest request;
+    request.key_description.Reinitialize(KmParamSet(keyParams));
+
+    GenerateKeyResponse response;
+    impl_->GenerateKey(request, &response);
+
+    KeyCharacteristics resultCharacteristics;
+    hidl_vec<uint8_t> resultKeyBlob;
+    if (response.error == KM_ERROR_OK) {
+        resultKeyBlob = kmBlob2hidlVec(response.key_blob);
+        resultCharacteristics.teeEnforced = kmParamSet2Hidl(response.enforced);
+        resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);
+    return Void();
+}
+
+Return<void> TrustyKeymaster3Device::getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,
+                                                           const hidl_vec<uint8_t>& clientId,
+                                                           const hidl_vec<uint8_t>& appData,
+                                                           getKeyCharacteristics_cb _hidl_cb) {
+    GetKeyCharacteristicsRequest request;
+    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+    addClientAndAppData(clientId, appData, &request.additional_params);
+
+    GetKeyCharacteristicsResponse response;
+    impl_->GetKeyCharacteristics(request, &response);
+
+    KeyCharacteristics resultCharacteristics;
+    if (response.error == KM_ERROR_OK) {
+        resultCharacteristics.teeEnforced = kmParamSet2Hidl(response.enforced);
+        resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultCharacteristics);
+    return Void();
+}
+
+Return<void> TrustyKeymaster3Device::importKey(const hidl_vec<KeyParameter>& params,
+                                               KeyFormat keyFormat,
+                                               const hidl_vec<uint8_t>& keyData,
+                                               importKey_cb _hidl_cb) {
+    ImportKeyRequest request;
+    request.key_description.Reinitialize(KmParamSet(params));
+    request.key_format = legacy_enum_conversion(keyFormat);
+    request.SetKeyMaterial(keyData.data(), keyData.size());
+
+    ImportKeyResponse response;
+    impl_->ImportKey(request, &response);
+
+    KeyCharacteristics resultCharacteristics;
+    hidl_vec<uint8_t> resultKeyBlob;
+    if (response.error == KM_ERROR_OK) {
+        resultKeyBlob = kmBlob2hidlVec(response.key_blob);
+        resultCharacteristics.teeEnforced = kmParamSet2Hidl(response.enforced);
+        resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);
+    return Void();
+}
+
+Return<void> TrustyKeymaster3Device::exportKey(KeyFormat exportFormat,
+                                               const hidl_vec<uint8_t>& keyBlob,
+                                               const hidl_vec<uint8_t>& clientId,
+                                               const hidl_vec<uint8_t>& appData,
+                                               exportKey_cb _hidl_cb) {
+    ExportKeyRequest request;
+    request.key_format = legacy_enum_conversion(exportFormat);
+    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+    addClientAndAppData(clientId, appData, &request.additional_params);
+
+    ExportKeyResponse response;
+    impl_->ExportKey(request, &response);
+
+    hidl_vec<uint8_t> resultKeyBlob;
+    if (response.error == KM_ERROR_OK) {
+        resultKeyBlob.setToExternal(response.key_data, response.key_data_length);
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob);
+    return Void();
+}
+
+Return<void> TrustyKeymaster3Device::attestKey(const hidl_vec<uint8_t>& keyToAttest,
+                                               const hidl_vec<KeyParameter>& attestParams,
+                                               attestKey_cb _hidl_cb) {
+    AttestKeyRequest request;
+    request.SetKeyMaterial(keyToAttest.data(), keyToAttest.size());
+    request.attest_params.Reinitialize(KmParamSet(attestParams));
+
+    AttestKeyResponse response;
+    impl_->AttestKey(request, &response);
+
+    hidl_vec<hidl_vec<uint8_t>> resultCertChain;
+    if (response.error == KM_ERROR_OK) {
+        resultCertChain = kmCertChain2Hidl(response.certificate_chain);
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultCertChain);
+    return Void();
+}
+
+Return<void> TrustyKeymaster3Device::upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
+                                                const hidl_vec<KeyParameter>& upgradeParams,
+                                                upgradeKey_cb _hidl_cb) {
+    UpgradeKeyRequest request;
+    request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());
+    request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));
+
+    UpgradeKeyResponse response;
+    impl_->UpgradeKey(request, &response);
+
+    if (response.error == KM_ERROR_OK) {
+        _hidl_cb(ErrorCode::OK, kmBlob2hidlVec(response.upgraded_key));
+    } else {
+        _hidl_cb(legacy_enum_conversion(response.error), hidl_vec<uint8_t>());
+    }
+    return Void();
+}
+
+Return<ErrorCode> TrustyKeymaster3Device::deleteKey(const hidl_vec<uint8_t>& keyBlob) {
+    DeleteKeyRequest request;
+    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+
+    DeleteKeyResponse response;
+    impl_->DeleteKey(request, &response);
+
+    return legacy_enum_conversion(response.error);
+}
+
+Return<ErrorCode> TrustyKeymaster3Device::deleteAllKeys() {
+    DeleteAllKeysRequest request;
+    DeleteAllKeysResponse response;
+    impl_->DeleteAllKeys(request, &response);
+
+    return legacy_enum_conversion(response.error);
+}
+
+Return<ErrorCode> TrustyKeymaster3Device::destroyAttestationIds() {
+    return ErrorCode::UNIMPLEMENTED;
+}
+
+Return<void> TrustyKeymaster3Device::begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
+                                           const hidl_vec<KeyParameter>& inParams,
+                                           begin_cb _hidl_cb) {
+    BeginOperationRequest request;
+    request.purpose = legacy_enum_conversion(purpose);
+    request.SetKeyMaterial(key.data(), key.size());
+    request.additional_params.Reinitialize(KmParamSet(inParams));
+
+    BeginOperationResponse response;
+    impl_->BeginOperation(request, &response);
+
+    hidl_vec<KeyParameter> resultParams;
+    if (response.error == KM_ERROR_OK) {
+        resultParams = kmParamSet2Hidl(response.output_params);
+    }
+
+    _hidl_cb(legacy_enum_conversion(response.error), resultParams, response.op_handle);
+    return Void();
+}
+
+Return<void> TrustyKeymaster3Device::update(uint64_t operationHandle,
+                                            const hidl_vec<KeyParameter>& inParams,
+                                            const hidl_vec<uint8_t>& input, update_cb _hidl_cb) {
+    UpdateOperationRequest request;
+    UpdateOperationResponse response;
+    hidl_vec<KeyParameter> resultParams;
+    hidl_vec<uint8_t> resultBlob;
+    uint32_t resultConsumed = 0;
+
+    request.op_handle = operationHandle;
+    request.additional_params.Reinitialize(KmParamSet(inParams));
+
+    size_t inp_size = input.size();
+    size_t ser_size = request.SerializedSize();
+
+    if (ser_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
+        response.error = KM_ERROR_INVALID_INPUT_LENGTH;
+    } else {
+        if (ser_size + inp_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
+            inp_size = TRUSTY_KEYMASTER_SEND_BUF_SIZE - ser_size;
+        }
+        request.input.Reinitialize(input.data(), inp_size);
+
+        impl_->UpdateOperation(request, &response);
+
+        if (response.error == KM_ERROR_OK) {
+            resultConsumed = response.input_consumed;
+            resultParams = kmParamSet2Hidl(response.output_params);
+            resultBlob = kmBuffer2hidlVec(response.output);
+        }
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultConsumed, resultParams, resultBlob);
+    return Void();
+}
+
+Return<void> TrustyKeymaster3Device::finish(uint64_t operationHandle,
+                                            const hidl_vec<KeyParameter>& inParams,
+                                            const hidl_vec<uint8_t>& input,
+                                            const hidl_vec<uint8_t>& signature,
+                                            finish_cb _hidl_cb) {
+    FinishOperationRequest request;
+    request.op_handle = operationHandle;
+    request.input.Reinitialize(input.data(), input.size());
+    request.signature.Reinitialize(signature.data(), signature.size());
+    request.additional_params.Reinitialize(KmParamSet(inParams));
+
+    FinishOperationResponse response;
+    impl_->FinishOperation(request, &response);
+
+    hidl_vec<KeyParameter> resultParams;
+    hidl_vec<uint8_t> resultBlob;
+    if (response.error == KM_ERROR_OK) {
+        resultParams = kmParamSet2Hidl(response.output_params);
+        resultBlob = kmBuffer2hidlVec(response.output);
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultParams, resultBlob);
+    return Void();
+}
+
+Return<ErrorCode> TrustyKeymaster3Device::abort(uint64_t operationHandle) {
+    AbortOperationRequest request;
+    request.op_handle = operationHandle;
+
+    AbortOperationResponse response;
+    impl_->AbortOperation(request, &response);
+
+    return legacy_enum_conversion(response.error);
+}
+}  // namespace keymaster
diff --git a/trusty/keymaster/3.0/android.hardware.keymaster@3.0-service.trusty.rc b/trusty/keymaster/3.0/android.hardware.keymaster@3.0-service.trusty.rc
new file mode 100644
index 0000000..503f3de
--- /dev/null
+++ b/trusty/keymaster/3.0/android.hardware.keymaster@3.0-service.trusty.rc
@@ -0,0 +1,4 @@
+service vendor.keymaster-3-0 /vendor/bin/hw/android.hardware.keymaster@3.0-service.trusty
+    class early_hal
+    user nobody
+    group drmrpc
diff --git a/trusty/keymaster/3.0/service.cpp b/trusty/keymaster/3.0/service.cpp
new file mode 100644
index 0000000..0d8436e
--- /dev/null
+++ b/trusty/keymaster/3.0/service.cpp
@@ -0,0 +1,43 @@
+/*
+**
+** 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.
+*/
+
+#include <android-base/logging.h>
+#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
+#include <hidl/HidlTransportSupport.h>
+#include <trusty_keymaster/TrustyKeymaster.h>
+#include <trusty_keymaster/TrustyKeymaster3Device.h>
+
+int main() {
+    ::android::hardware::configureRpcThreadpool(1, true);
+    auto trustyKeymaster = new keymaster::TrustyKeymaster();
+    int err = trustyKeymaster->Initialize();
+    if (err != 0) {
+        LOG(FATAL) << "Could not initialize TrustyKeymaster (" << err << ")";
+        return -1;
+    }
+
+    auto keymaster = new ::keymaster::TrustyKeymaster3Device(trustyKeymaster);
+
+    auto status = keymaster->registerAsService();
+    if (status != android::OK) {
+        LOG(FATAL) << "Could not register service for Keymaster 3.0 (" << status << ")";
+        return -1;
+    }
+
+    android::hardware::joinRpcThreadpool();
+    return -1;  // Should never get here.
+}
diff --git a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
new file mode 100644
index 0000000..b5fc6bf
--- /dev/null
+++ b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
@@ -0,0 +1,571 @@
+/*
+ **
+ ** 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.
+ */
+
+#define LOG_TAG "android.hardware.keymaster@4.0-impl.trusty"
+
+#include <authorization_set.h>
+#include <cutils/log.h>
+#include <keymaster/android_keymaster_messages.h>
+#include <trusty_keymaster/TrustyKeymaster4Device.h>
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
+
+using ::keymaster::AbortOperationRequest;
+using ::keymaster::AbortOperationResponse;
+using ::keymaster::AddEntropyRequest;
+using ::keymaster::AddEntropyResponse;
+using ::keymaster::AttestKeyRequest;
+using ::keymaster::AttestKeyResponse;
+using ::keymaster::AuthorizationSet;
+using ::keymaster::BeginOperationRequest;
+using ::keymaster::BeginOperationResponse;
+using ::keymaster::ExportKeyRequest;
+using ::keymaster::ExportKeyResponse;
+using ::keymaster::FinishOperationRequest;
+using ::keymaster::FinishOperationResponse;
+using ::keymaster::GenerateKeyRequest;
+using ::keymaster::GenerateKeyResponse;
+using ::keymaster::GetKeyCharacteristicsRequest;
+using ::keymaster::GetKeyCharacteristicsResponse;
+using ::keymaster::ImportKeyRequest;
+using ::keymaster::ImportKeyResponse;
+using ::keymaster::UpdateOperationRequest;
+using ::keymaster::UpdateOperationResponse;
+using ::keymaster::ng::Tag;
+
+namespace keymaster {
+namespace V4_0 {
+namespace {
+
+inline keymaster_tag_t legacy_enum_conversion(const Tag value) {
+    return keymaster_tag_t(value);
+}
+inline Tag legacy_enum_conversion(const keymaster_tag_t value) {
+    return Tag(value);
+}
+inline keymaster_purpose_t legacy_enum_conversion(const KeyPurpose value) {
+    return keymaster_purpose_t(value);
+}
+inline keymaster_key_format_t legacy_enum_conversion(const KeyFormat value) {
+    return keymaster_key_format_t(value);
+}
+
+inline SecurityLevel legacy_enum_conversion(const keymaster_security_level_t value) {
+    return static_cast<SecurityLevel>(value);
+}
+
+inline hw_authenticator_type_t legacy_enum_conversion(const HardwareAuthenticatorType value) {
+    return static_cast<hw_authenticator_type_t>(value);
+}
+
+inline ErrorCode legacy_enum_conversion(const keymaster_error_t value) {
+    return ErrorCode(value);
+}
+
+inline keymaster_tag_type_t typeFromTag(const keymaster_tag_t tag) {
+    return keymaster_tag_get_type(tag);
+}
+
+class KmParamSet : public keymaster_key_param_set_t {
+  public:
+    KmParamSet(const hidl_vec<KeyParameter>& keyParams) {
+        params = new keymaster_key_param_t[keyParams.size()];
+        length = keyParams.size();
+        for (size_t i = 0; i < keyParams.size(); ++i) {
+            auto tag = legacy_enum_conversion(keyParams[i].tag);
+            switch (typeFromTag(tag)) {
+                case KM_ENUM:
+                case KM_ENUM_REP:
+                    params[i] = keymaster_param_enum(tag, keyParams[i].f.integer);
+                    break;
+                case KM_UINT:
+                case KM_UINT_REP:
+                    params[i] = keymaster_param_int(tag, keyParams[i].f.integer);
+                    break;
+                case KM_ULONG:
+                case KM_ULONG_REP:
+                    params[i] = keymaster_param_long(tag, keyParams[i].f.longInteger);
+                    break;
+                case KM_DATE:
+                    params[i] = keymaster_param_date(tag, keyParams[i].f.dateTime);
+                    break;
+                case KM_BOOL:
+                    if (keyParams[i].f.boolValue)
+                        params[i] = keymaster_param_bool(tag);
+                    else
+                        params[i].tag = KM_TAG_INVALID;
+                    break;
+                case KM_BIGNUM:
+                case KM_BYTES:
+                    params[i] = keymaster_param_blob(tag, &keyParams[i].blob[0],
+                                                     keyParams[i].blob.size());
+                    break;
+                case KM_INVALID:
+                default:
+                    params[i].tag = KM_TAG_INVALID;
+                    /* just skip */
+                    break;
+            }
+        }
+    }
+    KmParamSet(KmParamSet&& other) noexcept
+        : keymaster_key_param_set_t{other.params, other.length} {
+        other.length = 0;
+        other.params = nullptr;
+    }
+    KmParamSet(const KmParamSet&) = delete;
+    ~KmParamSet() { delete[] params; }
+};
+
+inline hidl_vec<uint8_t> kmBlob2hidlVec(const keymaster_key_blob_t& blob) {
+    hidl_vec<uint8_t> result;
+    result.setToExternal(const_cast<unsigned char*>(blob.key_material), blob.key_material_size);
+    return result;
+}
+
+inline hidl_vec<uint8_t> kmBlob2hidlVec(const keymaster_blob_t& blob) {
+    hidl_vec<uint8_t> result;
+    result.setToExternal(const_cast<unsigned char*>(blob.data), blob.data_length);
+    return result;
+}
+
+inline hidl_vec<uint8_t> kmBuffer2hidlVec(const ::keymaster::Buffer& buf) {
+    hidl_vec<uint8_t> result;
+    result.setToExternal(const_cast<unsigned char*>(buf.peek_read()), buf.available_read());
+    return result;
+}
+
+inline static hidl_vec<hidl_vec<uint8_t>> kmCertChain2Hidl(
+        const keymaster_cert_chain_t& cert_chain) {
+    hidl_vec<hidl_vec<uint8_t>> result;
+    if (!cert_chain.entry_count || !cert_chain.entries) return result;
+
+    result.resize(cert_chain.entry_count);
+    for (size_t i = 0; i < cert_chain.entry_count; ++i) {
+        result[i] = kmBlob2hidlVec(cert_chain.entries[i]);
+    }
+
+    return result;
+}
+
+static inline hidl_vec<KeyParameter> kmParamSet2Hidl(const keymaster_key_param_set_t& set) {
+    hidl_vec<KeyParameter> result;
+    if (set.length == 0 || set.params == nullptr) return result;
+
+    result.resize(set.length);
+    keymaster_key_param_t* params = set.params;
+    for (size_t i = 0; i < set.length; ++i) {
+        auto tag = params[i].tag;
+        result[i].tag = legacy_enum_conversion(tag);
+        switch (typeFromTag(tag)) {
+            case KM_ENUM:
+            case KM_ENUM_REP:
+                result[i].f.integer = params[i].enumerated;
+                break;
+            case KM_UINT:
+            case KM_UINT_REP:
+                result[i].f.integer = params[i].integer;
+                break;
+            case KM_ULONG:
+            case KM_ULONG_REP:
+                result[i].f.longInteger = params[i].long_integer;
+                break;
+            case KM_DATE:
+                result[i].f.dateTime = params[i].date_time;
+                break;
+            case KM_BOOL:
+                result[i].f.boolValue = params[i].boolean;
+                break;
+            case KM_BIGNUM:
+            case KM_BYTES:
+                result[i].blob.setToExternal(const_cast<unsigned char*>(params[i].blob.data),
+                                             params[i].blob.data_length);
+                break;
+            case KM_INVALID:
+            default:
+                params[i].tag = KM_TAG_INVALID;
+                /* just skip */
+                break;
+        }
+    }
+    return result;
+}
+
+void addClientAndAppData(const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,
+                         ::keymaster::AuthorizationSet* params) {
+    params->Clear();
+    if (clientId.size()) {
+        params->push_back(::keymaster::TAG_APPLICATION_ID, clientId.data(), clientId.size());
+    }
+    if (appData.size()) {
+        params->push_back(::keymaster::TAG_APPLICATION_DATA, appData.data(), appData.size());
+    }
+}
+
+}  // anonymous namespace
+
+TrustyKeymaster4Device::TrustyKeymaster4Device(TrustyKeymaster* impl) : impl_(impl) {}
+
+TrustyKeymaster4Device::~TrustyKeymaster4Device() {}
+
+Return<void> TrustyKeymaster4Device::getHardwareInfo(getHardwareInfo_cb _hidl_cb) {
+    _hidl_cb(SecurityLevel::TRUSTED_ENVIRONMENT, "TrustyKeymaster", "Google");
+    return Void();
+}
+
+Return<void> TrustyKeymaster4Device::getHmacSharingParameters(
+        getHmacSharingParameters_cb _hidl_cb) {
+    const GetHmacSharingParametersResponse response = impl_->GetHmacSharingParameters();
+    // response.params is not the same as the HIDL structure, we need to convert it
+    V4_0::HmacSharingParameters params;
+    params.seed.setToExternal(const_cast<uint8_t*>(response.params.seed.data),
+                              response.params.seed.data_length);
+    static_assert(sizeof(response.params.nonce) == params.nonce.size(), "Nonce sizes don't match");
+    memcpy(params.nonce.data(), response.params.nonce, params.nonce.size());
+    _hidl_cb(legacy_enum_conversion(response.error), params);
+    return Void();
+}
+
+Return<void> TrustyKeymaster4Device::computeSharedHmac(
+        const hidl_vec<HmacSharingParameters>& params, computeSharedHmac_cb _hidl_cb) {
+    ComputeSharedHmacRequest request;
+    request.params_array.params_array = new keymaster::HmacSharingParameters[params.size()];
+    request.params_array.num_params = params.size();
+    for (size_t i = 0; i < params.size(); ++i) {
+        request.params_array.params_array[i].seed = {params[i].seed.data(), params[i].seed.size()};
+        static_assert(sizeof(request.params_array.params_array[i].nonce) ==
+                              decltype(params[i].nonce)::size(),
+                      "Nonce sizes don't match");
+        memcpy(request.params_array.params_array[i].nonce, params[i].nonce.data(),
+               params[i].nonce.size());
+    }
+
+    auto response = impl_->ComputeSharedHmac(request);
+    hidl_vec<uint8_t> sharing_check;
+    if (response.error == KM_ERROR_OK) {
+        sharing_check = kmBlob2hidlVec(response.sharing_check);
+    }
+
+    _hidl_cb(legacy_enum_conversion(response.error), sharing_check);
+    return Void();
+}
+
+Return<void> TrustyKeymaster4Device::verifyAuthorization(
+        uint64_t challenge, const hidl_vec<KeyParameter>& parametersToVerify,
+        const HardwareAuthToken& authToken, verifyAuthorization_cb _hidl_cb) {
+    VerifyAuthorizationRequest request;
+    request.challenge = challenge;
+    request.parameters_to_verify.Reinitialize(KmParamSet(parametersToVerify));
+    request.auth_token.challenge = authToken.challenge;
+    request.auth_token.user_id = authToken.userId;
+    request.auth_token.authenticator_id = authToken.authenticatorId;
+    request.auth_token.authenticator_type = legacy_enum_conversion(authToken.authenticatorType);
+    request.auth_token.timestamp = authToken.timestamp;
+    KeymasterBlob mac(authToken.mac.data(), authToken.mac.size());
+    request.auth_token.mac = mac;
+
+    auto response = impl_->VerifyAuthorization(request);
+
+    ::android::hardware::keymaster::V4_0::VerificationToken token;
+    token.challenge = response.token.challenge;
+    token.timestamp = response.token.timestamp;
+    token.parametersVerified = kmParamSet2Hidl(response.token.parameters_verified);
+    token.securityLevel = legacy_enum_conversion(response.token.security_level);
+    token.mac = kmBlob2hidlVec(response.token.mac);
+
+    _hidl_cb(legacy_enum_conversion(response.error), token);
+
+    return Void();
+}
+
+Return<ErrorCode> TrustyKeymaster4Device::addRngEntropy(const hidl_vec<uint8_t>& data) {
+    if (data.size() == 0) return ErrorCode::OK;
+    AddEntropyRequest request;
+    request.random_data.Reinitialize(data.data(), data.size());
+
+    AddEntropyResponse response;
+    impl_->AddRngEntropy(request, &response);
+
+    return legacy_enum_conversion(response.error);
+}
+
+Return<void> TrustyKeymaster4Device::generateKey(const hidl_vec<KeyParameter>& keyParams,
+                                                 generateKey_cb _hidl_cb) {
+    GenerateKeyRequest request;
+    request.key_description.Reinitialize(KmParamSet(keyParams));
+
+    GenerateKeyResponse response;
+    impl_->GenerateKey(request, &response);
+
+    KeyCharacteristics resultCharacteristics;
+    hidl_vec<uint8_t> resultKeyBlob;
+    if (response.error == KM_ERROR_OK) {
+        resultKeyBlob = kmBlob2hidlVec(response.key_blob);
+        resultCharacteristics.hardwareEnforced = kmParamSet2Hidl(response.enforced);
+        resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);
+    return Void();
+}
+
+Return<void> TrustyKeymaster4Device::getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,
+                                                           const hidl_vec<uint8_t>& clientId,
+                                                           const hidl_vec<uint8_t>& appData,
+                                                           getKeyCharacteristics_cb _hidl_cb) {
+    GetKeyCharacteristicsRequest request;
+    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+    addClientAndAppData(clientId, appData, &request.additional_params);
+
+    GetKeyCharacteristicsResponse response;
+    impl_->GetKeyCharacteristics(request, &response);
+
+    KeyCharacteristics resultCharacteristics;
+    if (response.error == KM_ERROR_OK) {
+        resultCharacteristics.hardwareEnforced = kmParamSet2Hidl(response.enforced);
+        resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultCharacteristics);
+    return Void();
+}
+
+Return<void> TrustyKeymaster4Device::importKey(const hidl_vec<KeyParameter>& params,
+                                               KeyFormat keyFormat,
+                                               const hidl_vec<uint8_t>& keyData,
+                                               importKey_cb _hidl_cb) {
+    ImportKeyRequest request;
+    request.key_description.Reinitialize(KmParamSet(params));
+    request.key_format = legacy_enum_conversion(keyFormat);
+    request.SetKeyMaterial(keyData.data(), keyData.size());
+
+    ImportKeyResponse response;
+    impl_->ImportKey(request, &response);
+
+    KeyCharacteristics resultCharacteristics;
+    hidl_vec<uint8_t> resultKeyBlob;
+    if (response.error == KM_ERROR_OK) {
+        resultKeyBlob = kmBlob2hidlVec(response.key_blob);
+        resultCharacteristics.hardwareEnforced = kmParamSet2Hidl(response.enforced);
+        resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);
+    return Void();
+}
+
+Return<void> TrustyKeymaster4Device::importWrappedKey(
+        const hidl_vec<uint8_t>& wrappedKeyData, const hidl_vec<uint8_t>& wrappingKeyBlob,
+        const hidl_vec<uint8_t>& maskingKey, const hidl_vec<KeyParameter>& unwrappingParams,
+        uint64_t passwordSid, uint64_t biometricSid, importWrappedKey_cb _hidl_cb) {
+    ImportWrappedKeyRequest request;
+    request.SetWrappedMaterial(wrappedKeyData.data(), wrappedKeyData.size());
+    request.SetWrappingMaterial(wrappingKeyBlob.data(), wrappingKeyBlob.size());
+    request.SetMaskingKeyMaterial(maskingKey.data(), maskingKey.size());
+    request.additional_params.Reinitialize(KmParamSet(unwrappingParams));
+    request.password_sid = passwordSid;
+    request.biometric_sid = biometricSid;
+
+    ImportWrappedKeyResponse response;
+    impl_->ImportWrappedKey(request, &response);
+
+    KeyCharacteristics resultCharacteristics;
+    hidl_vec<uint8_t> resultKeyBlob;
+    if (response.error == KM_ERROR_OK) {
+        resultKeyBlob = kmBlob2hidlVec(response.key_blob);
+        resultCharacteristics.hardwareEnforced = kmParamSet2Hidl(response.enforced);
+        resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);
+    return Void();
+}
+
+Return<void> TrustyKeymaster4Device::exportKey(KeyFormat exportFormat,
+                                               const hidl_vec<uint8_t>& keyBlob,
+                                               const hidl_vec<uint8_t>& clientId,
+                                               const hidl_vec<uint8_t>& appData,
+                                               exportKey_cb _hidl_cb) {
+    ExportKeyRequest request;
+    request.key_format = legacy_enum_conversion(exportFormat);
+    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+    addClientAndAppData(clientId, appData, &request.additional_params);
+
+    ExportKeyResponse response;
+    impl_->ExportKey(request, &response);
+
+    hidl_vec<uint8_t> resultKeyBlob;
+    if (response.error == KM_ERROR_OK) {
+        resultKeyBlob.setToExternal(response.key_data, response.key_data_length);
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob);
+    return Void();
+}
+
+Return<void> TrustyKeymaster4Device::attestKey(const hidl_vec<uint8_t>& keyToAttest,
+                                               const hidl_vec<KeyParameter>& attestParams,
+                                               attestKey_cb _hidl_cb) {
+    AttestKeyRequest request;
+    request.SetKeyMaterial(keyToAttest.data(), keyToAttest.size());
+    request.attest_params.Reinitialize(KmParamSet(attestParams));
+
+    AttestKeyResponse response;
+    impl_->AttestKey(request, &response);
+
+    hidl_vec<hidl_vec<uint8_t>> resultCertChain;
+    if (response.error == KM_ERROR_OK) {
+        resultCertChain = kmCertChain2Hidl(response.certificate_chain);
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultCertChain);
+    return Void();
+}
+
+Return<void> TrustyKeymaster4Device::upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
+                                                const hidl_vec<KeyParameter>& upgradeParams,
+                                                upgradeKey_cb _hidl_cb) {
+    UpgradeKeyRequest request;
+    request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());
+    request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));
+
+    UpgradeKeyResponse response;
+    impl_->UpgradeKey(request, &response);
+
+    if (response.error == KM_ERROR_OK) {
+        _hidl_cb(ErrorCode::OK, kmBlob2hidlVec(response.upgraded_key));
+    } else {
+        _hidl_cb(legacy_enum_conversion(response.error), hidl_vec<uint8_t>());
+    }
+    return Void();
+}
+
+Return<ErrorCode> TrustyKeymaster4Device::deleteKey(const hidl_vec<uint8_t>& keyBlob) {
+    DeleteKeyRequest request;
+    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+
+    DeleteKeyResponse response;
+    impl_->DeleteKey(request, &response);
+
+    return legacy_enum_conversion(response.error);
+}
+
+Return<ErrorCode> TrustyKeymaster4Device::deleteAllKeys() {
+    DeleteAllKeysRequest request;
+    DeleteAllKeysResponse response;
+    impl_->DeleteAllKeys(request, &response);
+
+    return legacy_enum_conversion(response.error);
+}
+
+Return<ErrorCode> TrustyKeymaster4Device::destroyAttestationIds() {
+    return ErrorCode::UNIMPLEMENTED;
+}
+
+Return<void> TrustyKeymaster4Device::begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
+                                           const hidl_vec<KeyParameter>& inParams,
+                                           const HardwareAuthToken& authToken, begin_cb _hidl_cb) {
+    (void)authToken;
+    BeginOperationRequest request;
+    request.purpose = legacy_enum_conversion(purpose);
+    request.SetKeyMaterial(key.data(), key.size());
+    request.additional_params.Reinitialize(KmParamSet(inParams));
+
+    BeginOperationResponse response;
+    impl_->BeginOperation(request, &response);
+
+    hidl_vec<KeyParameter> resultParams;
+    if (response.error == KM_ERROR_OK) {
+        resultParams = kmParamSet2Hidl(response.output_params);
+    }
+
+    _hidl_cb(legacy_enum_conversion(response.error), resultParams, response.op_handle);
+    return Void();
+}
+
+Return<void> TrustyKeymaster4Device::update(uint64_t operationHandle,
+                                            const hidl_vec<KeyParameter>& inParams,
+                                            const hidl_vec<uint8_t>& input,
+                                            const HardwareAuthToken& authToken,
+                                            const VerificationToken& verificationToken,
+                                            update_cb _hidl_cb) {
+    (void)authToken;
+    (void)verificationToken;
+    UpdateOperationRequest request;
+    UpdateOperationResponse response;
+    hidl_vec<KeyParameter> resultParams;
+    hidl_vec<uint8_t> resultBlob;
+    uint32_t resultConsumed = 0;
+
+    request.op_handle = operationHandle;
+    request.additional_params.Reinitialize(KmParamSet(inParams));
+
+    size_t inp_size = input.size();
+    size_t ser_size = request.SerializedSize();
+
+    if (ser_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
+        response.error = KM_ERROR_INVALID_INPUT_LENGTH;
+    } else {
+        if (ser_size + inp_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
+            inp_size = TRUSTY_KEYMASTER_SEND_BUF_SIZE - ser_size;
+        }
+        request.input.Reinitialize(input.data(), inp_size);
+
+        impl_->UpdateOperation(request, &response);
+
+        if (response.error == KM_ERROR_OK) {
+            resultConsumed = response.input_consumed;
+            resultParams = kmParamSet2Hidl(response.output_params);
+            resultBlob = kmBuffer2hidlVec(response.output);
+        }
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultConsumed, resultParams, resultBlob);
+    return Void();
+}
+
+Return<void> TrustyKeymaster4Device::finish(uint64_t operationHandle,
+                                            const hidl_vec<KeyParameter>& inParams,
+                                            const hidl_vec<uint8_t>& input,
+                                            const hidl_vec<uint8_t>& signature,
+                                            const HardwareAuthToken& authToken,
+                                            const VerificationToken& verificationToken,
+                                            finish_cb _hidl_cb) {
+    (void)authToken;
+    (void)verificationToken;
+    FinishOperationRequest request;
+    request.op_handle = operationHandle;
+    request.input.Reinitialize(input.data(), input.size());
+    request.signature.Reinitialize(signature.data(), signature.size());
+    request.additional_params.Reinitialize(KmParamSet(inParams));
+
+    FinishOperationResponse response;
+    impl_->FinishOperation(request, &response);
+
+    hidl_vec<KeyParameter> resultParams;
+    hidl_vec<uint8_t> resultBlob;
+    if (response.error == KM_ERROR_OK) {
+        resultParams = kmParamSet2Hidl(response.output_params);
+        resultBlob = kmBuffer2hidlVec(response.output);
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultParams, resultBlob);
+    return Void();
+}
+
+Return<ErrorCode> TrustyKeymaster4Device::abort(uint64_t operationHandle) {
+    AbortOperationRequest request;
+    request.op_handle = operationHandle;
+
+    AbortOperationResponse response;
+    impl_->AbortOperation(request, &response);
+
+    return legacy_enum_conversion(response.error);
+}
+}  // namespace V4_0
+}  // namespace keymaster
diff --git a/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.rc b/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.rc
new file mode 100644
index 0000000..72c9167
--- /dev/null
+++ b/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.rc
@@ -0,0 +1,4 @@
+service vendor.keymaster-4-0 /vendor/bin/hw/android.hardware.keymaster@4.0-service.trusty
+    class early_hal
+    user nobody
+    group drmrpc
diff --git a/trusty/keymaster/4.0/service.cpp b/trusty/keymaster/4.0/service.cpp
new file mode 100644
index 0000000..96eb584
--- /dev/null
+++ b/trusty/keymaster/4.0/service.cpp
@@ -0,0 +1,43 @@
+/*
+**
+** 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.
+*/
+
+#include <android-base/logging.h>
+#include <android/hardware/keymaster/4.0/IKeymasterDevice.h>
+#include <hidl/HidlTransportSupport.h>
+#include <trusty_keymaster/TrustyKeymaster.h>
+#include <trusty_keymaster/TrustyKeymaster4Device.h>
+
+int main() {
+    ::android::hardware::configureRpcThreadpool(1, true);
+    auto trustyKeymaster = new keymaster::TrustyKeymaster();
+    int err = trustyKeymaster->Initialize();
+    if (err != 0) {
+        LOG(FATAL) << "Could not initialize TrustyKeymaster (" << err << ")";
+        return -1;
+    }
+
+    auto keymaster = new ::keymaster::V4_0::TrustyKeymaster4Device(trustyKeymaster);
+
+    auto status = keymaster->registerAsService();
+    if (status != android::OK) {
+        LOG(FATAL) << "Could not register service for Keymaster 4.0 (" << status << ")";
+        return -1;
+    }
+
+    android::hardware::joinRpcThreadpool();
+    return -1;  // Should never get here.
+}
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 899f8a3..d107b78 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -27,14 +27,17 @@
     name: "trusty_keymaster_tipc",
     vendor: true,
     srcs: [
-        "trusty_keymaster_device.cpp",
-        "trusty_keymaster_ipc.cpp",
-        "trusty_keymaster_main.cpp",
+        "ipc/trusty_keymaster_ipc.cpp",
+        "legacy/trusty_keymaster_device.cpp",
+        "legacy/trusty_keymaster_main.cpp",
     ],
     cflags: [
         "-Wall",
         "-Werror",
     ],
+
+    local_include_dirs: ["include"],
+
     shared_libs: [
         "libcrypto",
         "libcutils",
@@ -52,9 +55,9 @@
     vendor: true,
     relative_install_path: "hw",
     srcs: [
-        "module.cpp",
-        "trusty_keymaster_ipc.cpp",
-        "trusty_keymaster_device.cpp",
+        "ipc/trusty_keymaster_ipc.cpp",
+        "legacy/module.cpp",
+        "legacy/trusty_keymaster_device.cpp",
     ],
 
     cflags: [
@@ -63,6 +66,8 @@
         "-Werror",
     ],
 
+    local_include_dirs: ["include"],
+
     shared_libs: [
         "libcrypto",
         "libkeymaster_messages",
@@ -72,3 +77,65 @@
     ],
     header_libs: ["libhardware_headers"],
 }
+
+cc_binary {
+    name: "android.hardware.keymaster@3.0-service.trusty",
+    defaults: ["hidl_defaults"],
+    relative_install_path: "hw",
+    vendor: true,
+    init_rc: ["3.0/android.hardware.keymaster@3.0-service.trusty.rc"],
+    srcs: [
+        "3.0/service.cpp",
+        "3.0/TrustyKeymaster3Device.cpp",
+        "ipc/trusty_keymaster_ipc.cpp",
+        "TrustyKeymaster.cpp",
+    ],
+
+    local_include_dirs: ["include"],
+
+    shared_libs: [
+        "liblog",
+        "libcutils",
+        "libdl",
+        "libbase",
+        "libutils",
+        "libhardware",
+        "libhidlbase",
+        "libhidltransport",
+        "libtrusty",
+        "libkeymaster_messages",
+        "libkeymaster3device",
+        "android.hardware.keymaster@3.0"
+    ],
+}
+
+cc_binary {
+    name: "android.hardware.keymaster@4.0-service.trusty",
+    defaults: ["hidl_defaults"],
+    relative_install_path: "hw",
+    vendor: true,
+    init_rc: ["4.0/android.hardware.keymaster@4.0-service.trusty.rc"],
+    srcs: [
+        "4.0/service.cpp",
+        "4.0/TrustyKeymaster4Device.cpp",
+        "ipc/trusty_keymaster_ipc.cpp",
+        "TrustyKeymaster.cpp",
+    ],
+
+    local_include_dirs: ["include"],
+
+    shared_libs: [
+        "liblog",
+        "libcutils",
+        "libdl",
+        "libbase",
+        "libutils",
+        "libhardware",
+        "libhidlbase",
+        "libhidltransport",
+        "libtrusty",
+        "libkeymaster_messages",
+        "libkeymaster4",
+        "android.hardware.keymaster@4.0"
+    ],
+}
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
new file mode 100644
index 0000000..f3ef747
--- /dev/null
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -0,0 +1,197 @@
+/*
+ * 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.
+ */
+
+#include <cutils/log.h>
+#include <keymaster/android_keymaster_messages.h>
+#include <keymaster/keymaster_configuration.h>
+#include <trusty_keymaster/TrustyKeymaster.h>
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
+
+namespace keymaster {
+
+int TrustyKeymaster::Initialize() {
+    int err;
+
+    err = trusty_keymaster_connect();
+    if (err) {
+        ALOGE("Failed to connect to trusty keymaster %d", err);
+        return err;
+    }
+
+    ConfigureRequest req;
+    req.os_version = GetOsVersion();
+    req.os_patchlevel = GetOsPatchlevel();
+
+    ConfigureResponse rsp;
+    Configure(req, &rsp);
+
+    if (rsp.error != KM_ERROR_OK) {
+        ALOGE("Failed to configure keymaster %d", rsp.error);
+        return -1;
+    }
+
+    return 0;
+}
+
+TrustyKeymaster::TrustyKeymaster() {}
+
+TrustyKeymaster::~TrustyKeymaster() {
+    trusty_keymaster_disconnect();
+}
+
+static void ForwardCommand(enum keymaster_command command, const Serializable& req,
+                           KeymasterResponse* rsp) {
+    keymaster_error_t err;
+    err = trusty_keymaster_send(command, req, rsp);
+    if (err != KM_ERROR_OK) {
+        ALOGE("Failed to send cmd %d err: %d", command, err);
+        rsp->error = err;
+    }
+}
+
+void TrustyKeymaster::GetVersion(const GetVersionRequest& request, GetVersionResponse* response) {
+    ForwardCommand(KM_GET_VERSION, request, response);
+}
+
+void TrustyKeymaster::SupportedAlgorithms(const SupportedAlgorithmsRequest& request,
+                                          SupportedAlgorithmsResponse* response) {
+    ForwardCommand(KM_GET_SUPPORTED_ALGORITHMS, request, response);
+}
+
+void TrustyKeymaster::SupportedBlockModes(const SupportedBlockModesRequest& request,
+                                          SupportedBlockModesResponse* response) {
+    ForwardCommand(KM_GET_SUPPORTED_BLOCK_MODES, request, response);
+}
+
+void TrustyKeymaster::SupportedPaddingModes(const SupportedPaddingModesRequest& request,
+                                            SupportedPaddingModesResponse* response) {
+    ForwardCommand(KM_GET_SUPPORTED_PADDING_MODES, request, response);
+}
+
+void TrustyKeymaster::SupportedDigests(const SupportedDigestsRequest& request,
+                                       SupportedDigestsResponse* response) {
+    ForwardCommand(KM_GET_SUPPORTED_DIGESTS, request, response);
+}
+
+void TrustyKeymaster::SupportedImportFormats(const SupportedImportFormatsRequest& request,
+                                             SupportedImportFormatsResponse* response) {
+    ForwardCommand(KM_GET_SUPPORTED_IMPORT_FORMATS, request, response);
+}
+
+void TrustyKeymaster::SupportedExportFormats(const SupportedExportFormatsRequest& request,
+                                             SupportedExportFormatsResponse* response) {
+    ForwardCommand(KM_GET_SUPPORTED_EXPORT_FORMATS, request, response);
+}
+
+void TrustyKeymaster::AddRngEntropy(const AddEntropyRequest& request,
+                                    AddEntropyResponse* response) {
+    ForwardCommand(KM_ADD_RNG_ENTROPY, request, response);
+}
+
+void TrustyKeymaster::Configure(const ConfigureRequest& request, ConfigureResponse* response) {
+    ForwardCommand(KM_CONFIGURE, request, response);
+}
+
+void TrustyKeymaster::GenerateKey(const GenerateKeyRequest& request,
+                                  GenerateKeyResponse* response) {
+    GenerateKeyRequest datedRequest(request.message_version);
+    datedRequest.key_description = request.key_description;
+
+    if (!request.key_description.Contains(TAG_CREATION_DATETIME)) {
+        datedRequest.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
+    }
+
+    ForwardCommand(KM_GENERATE_KEY, datedRequest, response);
+}
+
+void TrustyKeymaster::GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
+                                            GetKeyCharacteristicsResponse* response) {
+    ForwardCommand(KM_GET_KEY_CHARACTERISTICS, request, response);
+}
+
+void TrustyKeymaster::ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response) {
+    ForwardCommand(KM_IMPORT_KEY, request, response);
+}
+
+void TrustyKeymaster::ImportWrappedKey(const ImportWrappedKeyRequest& request,
+                                       ImportWrappedKeyResponse* response) {
+    ForwardCommand(KM_IMPORT_WRAPPED_KEY, request, response);
+}
+
+void TrustyKeymaster::ExportKey(const ExportKeyRequest& request, ExportKeyResponse* response) {
+    ForwardCommand(KM_EXPORT_KEY, request, response);
+}
+
+void TrustyKeymaster::AttestKey(const AttestKeyRequest& request, AttestKeyResponse* response) {
+    ForwardCommand(KM_ATTEST_KEY, request, response);
+}
+
+void TrustyKeymaster::UpgradeKey(const UpgradeKeyRequest& request, UpgradeKeyResponse* response) {
+    ForwardCommand(KM_UPGRADE_KEY, request, response);
+}
+
+void TrustyKeymaster::DeleteKey(const DeleteKeyRequest& request, DeleteKeyResponse* response) {
+    ForwardCommand(KM_DELETE_KEY, request, response);
+}
+
+void TrustyKeymaster::DeleteAllKeys(const DeleteAllKeysRequest& request,
+                                    DeleteAllKeysResponse* response) {
+    ForwardCommand(KM_DELETE_ALL_KEYS, request, response);
+}
+
+void TrustyKeymaster::BeginOperation(const BeginOperationRequest& request,
+                                     BeginOperationResponse* response) {
+    ForwardCommand(KM_BEGIN_OPERATION, request, response);
+}
+
+void TrustyKeymaster::UpdateOperation(const UpdateOperationRequest& request,
+                                      UpdateOperationResponse* response) {
+    ForwardCommand(KM_UPDATE_OPERATION, request, response);
+}
+
+void TrustyKeymaster::FinishOperation(const FinishOperationRequest& request,
+                                      FinishOperationResponse* response) {
+    ForwardCommand(KM_FINISH_OPERATION, request, response);
+}
+
+void TrustyKeymaster::AbortOperation(const AbortOperationRequest& request,
+                                     AbortOperationResponse* response) {
+    ForwardCommand(KM_ABORT_OPERATION, request, response);
+}
+
+GetHmacSharingParametersResponse TrustyKeymaster::GetHmacSharingParameters() {
+    // Dummy empty buffer to allow ForwardCommand to have something to serialize
+    Buffer request;
+    GetHmacSharingParametersResponse response;
+    ForwardCommand(KM_GET_HMAC_SHARING_PARAMETERS, request, &response);
+    return response;
+}
+
+ComputeSharedHmacResponse TrustyKeymaster::ComputeSharedHmac(
+        const ComputeSharedHmacRequest& request) {
+    ComputeSharedHmacResponse response;
+    ForwardCommand(KM_COMPUTE_SHARED_HMAC, request, &response);
+    return response;
+}
+
+VerifyAuthorizationResponse TrustyKeymaster::VerifyAuthorization(
+        const VerifyAuthorizationRequest& request) {
+    VerifyAuthorizationResponse response;
+    ForwardCommand(KM_VERIFY_AUTHORIZATION, request, &response);
+    return response;
+}
+
+}  // namespace keymaster
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
new file mode 100644
index 0000000..030b645
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef TRUSTY_KEYMASTER_H_
+#define TRUSTY_KEYMASTER_H_
+
+#include <keymaster/android_keymaster_messages.h>
+
+namespace keymaster {
+
+class TrustyKeymaster {
+  public:
+    TrustyKeymaster();
+    ~TrustyKeymaster();
+    int Initialize();
+    void GetVersion(const GetVersionRequest& request, GetVersionResponse* response);
+    void SupportedAlgorithms(const SupportedAlgorithmsRequest& request,
+                             SupportedAlgorithmsResponse* response);
+    void SupportedBlockModes(const SupportedBlockModesRequest& request,
+                             SupportedBlockModesResponse* response);
+    void SupportedPaddingModes(const SupportedPaddingModesRequest& request,
+                               SupportedPaddingModesResponse* response);
+    void SupportedDigests(const SupportedDigestsRequest& request,
+                          SupportedDigestsResponse* response);
+    void SupportedImportFormats(const SupportedImportFormatsRequest& request,
+                                SupportedImportFormatsResponse* response);
+    void SupportedExportFormats(const SupportedExportFormatsRequest& request,
+                                SupportedExportFormatsResponse* response);
+    void AddRngEntropy(const AddEntropyRequest& request, AddEntropyResponse* response);
+    void Configure(const ConfigureRequest& request, ConfigureResponse* response);
+    void GenerateKey(const GenerateKeyRequest& request, GenerateKeyResponse* response);
+    void GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
+                               GetKeyCharacteristicsResponse* response);
+    void ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response);
+    void ImportWrappedKey(const ImportWrappedKeyRequest& request,
+                          ImportWrappedKeyResponse* response);
+    void ExportKey(const ExportKeyRequest& request, ExportKeyResponse* response);
+    void AttestKey(const AttestKeyRequest& request, AttestKeyResponse* response);
+    void UpgradeKey(const UpgradeKeyRequest& request, UpgradeKeyResponse* response);
+    void DeleteKey(const DeleteKeyRequest& request, DeleteKeyResponse* response);
+    void DeleteAllKeys(const DeleteAllKeysRequest& request, DeleteAllKeysResponse* response);
+    void BeginOperation(const BeginOperationRequest& request, BeginOperationResponse* response);
+    void UpdateOperation(const UpdateOperationRequest& request, UpdateOperationResponse* response);
+    void FinishOperation(const FinishOperationRequest& request, FinishOperationResponse* response);
+    void AbortOperation(const AbortOperationRequest& request, AbortOperationResponse* response);
+    GetHmacSharingParametersResponse GetHmacSharingParameters();
+    ComputeSharedHmacResponse ComputeSharedHmac(const ComputeSharedHmacRequest& request);
+    VerifyAuthorizationResponse VerifyAuthorization(const VerifyAuthorizationRequest& request);
+};
+
+}  // namespace keymaster
+
+#endif  // TRUSTY_KEYMASTER_H_
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster3Device.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster3Device.h
new file mode 100644
index 0000000..6fc79ce
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster3Device.h
@@ -0,0 +1,84 @@
+/*
+ **
+ ** 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.
+ */
+
+#ifndef HIDL_android_hardware_keymaster_V3_0_TrustyKeymaster3Device_H_
+#define HIDL_android_hardware_keymaster_V3_0_TrustyKeymaster3Device_H_
+
+#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
+
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <trusty_keymaster/TrustyKeymaster.h>
+
+namespace keymaster {
+
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::keymaster::V3_0::ErrorCode;
+using ::android::hardware::keymaster::V3_0::IKeymasterDevice;
+using ::android::hardware::keymaster::V3_0::KeyCharacteristics;
+using ::android::hardware::keymaster::V3_0::KeyFormat;
+using ::android::hardware::keymaster::V3_0::KeyParameter;
+using ::android::hardware::keymaster::V3_0::KeyPurpose;
+
+class TrustyKeymaster3Device : public IKeymasterDevice {
+  public:
+    TrustyKeymaster3Device(TrustyKeymaster* impl);
+    virtual ~TrustyKeymaster3Device();
+
+    Return<void> getHardwareFeatures(getHardwareFeatures_cb _hidl_cb);
+    Return<ErrorCode> addRngEntropy(const hidl_vec<uint8_t>& data) override;
+    Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
+                             generateKey_cb _hidl_cb) override;
+    Return<void> getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,
+                                       const hidl_vec<uint8_t>& clientId,
+                                       const hidl_vec<uint8_t>& appData,
+                                       getKeyCharacteristics_cb _hidl_cb) override;
+    Return<void> importKey(const hidl_vec<KeyParameter>& params, KeyFormat keyFormat,
+                           const hidl_vec<uint8_t>& keyData, importKey_cb _hidl_cb) override;
+    Return<void> exportKey(KeyFormat exportFormat, const hidl_vec<uint8_t>& keyBlob,
+                           const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,
+                           exportKey_cb _hidl_cb) override;
+    Return<void> attestKey(const hidl_vec<uint8_t>& keyToAttest,
+                           const hidl_vec<KeyParameter>& attestParams,
+                           attestKey_cb _hidl_cb) override;
+    Return<void> upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
+                            const hidl_vec<KeyParameter>& upgradeParams,
+                            upgradeKey_cb _hidl_cb) override;
+    Return<ErrorCode> deleteKey(const hidl_vec<uint8_t>& keyBlob) override;
+    Return<ErrorCode> deleteAllKeys() override;
+    Return<ErrorCode> destroyAttestationIds() override;
+    Return<void> begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
+                       const hidl_vec<KeyParameter>& inParams, begin_cb _hidl_cb) override;
+    Return<void> update(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
+                        const hidl_vec<uint8_t>& input, update_cb _hidl_cb) override;
+    Return<void> finish(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
+                        const hidl_vec<uint8_t>& input, const hidl_vec<uint8_t>& signature,
+                        finish_cb _hidl_cb) override;
+    Return<ErrorCode> abort(uint64_t operationHandle) override;
+
+  private:
+    std::unique_ptr<TrustyKeymaster> impl_;
+};
+
+}  // namespace keymaster
+
+#endif  // HIDL_android_hardware_keymaster_V3_0_TrustyKeymaster3Device_H_
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster4Device.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster4Device.h
new file mode 100644
index 0000000..2be15bc
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster4Device.h
@@ -0,0 +1,105 @@
+/*
+ **
+ ** 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 keymaster_V4_0_TrustyKeymaster4Device_H_
+#define keymaster_V4_0_TrustyKeymaster4Device_H_
+
+#include <android/hardware/keymaster/4.0/IKeymasterDevice.h>
+#include <hidl/Status.h>
+#include <trusty_keymaster/TrustyKeymaster.h>
+
+namespace keymaster {
+
+namespace V4_0 {
+
+using ::android::sp;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::keymaster::V4_0::ErrorCode;
+using ::android::hardware::keymaster::V4_0::HardwareAuthenticatorType;
+using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
+using ::android::hardware::keymaster::V4_0::HmacSharingParameters;
+using ::android::hardware::keymaster::V4_0::IKeymasterDevice;
+using ::android::hardware::keymaster::V4_0::KeyCharacteristics;
+using ::android::hardware::keymaster::V4_0::KeyFormat;
+using ::android::hardware::keymaster::V4_0::KeyParameter;
+using ::android::hardware::keymaster::V4_0::KeyPurpose;
+using ::android::hardware::keymaster::V4_0::SecurityLevel;
+using ::android::hardware::keymaster::V4_0::Tag;
+using ::android::hardware::keymaster::V4_0::VerificationToken;
+
+class TrustyKeymaster4Device : public IKeymasterDevice {
+  public:
+    explicit TrustyKeymaster4Device(TrustyKeymaster* impl);
+    virtual ~TrustyKeymaster4Device();
+
+    Return<void> getHardwareInfo(getHardwareInfo_cb _hidl_cb) override;
+    Return<void> getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb) override;
+    Return<void> computeSharedHmac(const hidl_vec<HmacSharingParameters>& params,
+                                   computeSharedHmac_cb) override;
+    Return<void> verifyAuthorization(uint64_t challenge,
+                                     const hidl_vec<KeyParameter>& parametersToVerify,
+                                     const HardwareAuthToken& authToken,
+                                     verifyAuthorization_cb _hidl_cb) override;
+    Return<ErrorCode> addRngEntropy(const hidl_vec<uint8_t>& data) override;
+    Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
+                             generateKey_cb _hidl_cb) override;
+    Return<void> getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,
+                                       const hidl_vec<uint8_t>& clientId,
+                                       const hidl_vec<uint8_t>& appData,
+                                       getKeyCharacteristics_cb _hidl_cb) override;
+    Return<void> importKey(const hidl_vec<KeyParameter>& params, KeyFormat keyFormat,
+                           const hidl_vec<uint8_t>& keyData, importKey_cb _hidl_cb) override;
+    Return<void> importWrappedKey(const hidl_vec<uint8_t>& wrappedKeyData,
+                                  const hidl_vec<uint8_t>& wrappingKeyBlob,
+                                  const hidl_vec<uint8_t>& maskingKey,
+                                  const hidl_vec<KeyParameter>& unwrappingParams,
+                                  uint64_t passwordSid, uint64_t biometricSid,
+                                  importWrappedKey_cb _hidl_cb) override;
+    Return<void> exportKey(KeyFormat exportFormat, const hidl_vec<uint8_t>& keyBlob,
+                           const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,
+                           exportKey_cb _hidl_cb) override;
+    Return<void> attestKey(const hidl_vec<uint8_t>& keyToAttest,
+                           const hidl_vec<KeyParameter>& attestParams,
+                           attestKey_cb _hidl_cb) override;
+    Return<void> upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
+                            const hidl_vec<KeyParameter>& upgradeParams,
+                            upgradeKey_cb _hidl_cb) override;
+    Return<ErrorCode> deleteKey(const hidl_vec<uint8_t>& keyBlob) override;
+    Return<ErrorCode> deleteAllKeys() override;
+    Return<ErrorCode> destroyAttestationIds() override;
+    Return<void> begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
+                       const hidl_vec<KeyParameter>& inParams, const HardwareAuthToken& authToken,
+                       begin_cb _hidl_cb) override;
+    Return<void> update(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
+                        const hidl_vec<uint8_t>& input, const HardwareAuthToken& authToken,
+                        const VerificationToken& verificationToken, update_cb _hidl_cb) override;
+    Return<void> finish(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
+                        const hidl_vec<uint8_t>& input, const hidl_vec<uint8_t>& signature,
+                        const HardwareAuthToken& authToken,
+                        const VerificationToken& verificationToken, finish_cb _hidl_cb) override;
+    Return<ErrorCode> abort(uint64_t operationHandle) override;
+
+  private:
+    std::unique_ptr<::keymaster::TrustyKeymaster> impl_;
+};
+
+}  // namespace V4_0
+}  // namespace keymaster
+
+#endif  // keymaster_V4_0_TrustyKeymaster4Device_H_
diff --git a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
new file mode 100644
index 0000000..13e6725
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+// clang-format off
+
+#define KEYMASTER_PORT "com.android.trusty.keymaster"
+#define KEYMASTER_MAX_BUFFER_LENGTH 4096
+
+// Commands
+enum keymaster_command : uint32_t {
+    KEYMASTER_RESP_BIT              = 1,
+    KEYMASTER_STOP_BIT              = 2,
+    KEYMASTER_REQ_SHIFT             = 2,
+
+    KM_GENERATE_KEY                 = (0 << KEYMASTER_REQ_SHIFT),
+    KM_BEGIN_OPERATION              = (1 << KEYMASTER_REQ_SHIFT),
+    KM_UPDATE_OPERATION             = (2 << KEYMASTER_REQ_SHIFT),
+    KM_FINISH_OPERATION             = (3 << KEYMASTER_REQ_SHIFT),
+    KM_ABORT_OPERATION              = (4 << KEYMASTER_REQ_SHIFT),
+    KM_IMPORT_KEY                   = (5 << KEYMASTER_REQ_SHIFT),
+    KM_EXPORT_KEY                   = (6 << KEYMASTER_REQ_SHIFT),
+    KM_GET_VERSION                  = (7 << KEYMASTER_REQ_SHIFT),
+    KM_ADD_RNG_ENTROPY              = (8 << KEYMASTER_REQ_SHIFT),
+    KM_GET_SUPPORTED_ALGORITHMS     = (9 << KEYMASTER_REQ_SHIFT),
+    KM_GET_SUPPORTED_BLOCK_MODES    = (10 << KEYMASTER_REQ_SHIFT),
+    KM_GET_SUPPORTED_PADDING_MODES  = (11 << KEYMASTER_REQ_SHIFT),
+    KM_GET_SUPPORTED_DIGESTS        = (12 << KEYMASTER_REQ_SHIFT),
+    KM_GET_SUPPORTED_IMPORT_FORMATS = (13 << KEYMASTER_REQ_SHIFT),
+    KM_GET_SUPPORTED_EXPORT_FORMATS = (14 << KEYMASTER_REQ_SHIFT),
+    KM_GET_KEY_CHARACTERISTICS      = (15 << KEYMASTER_REQ_SHIFT),
+    KM_ATTEST_KEY                   = (16 << KEYMASTER_REQ_SHIFT),
+    KM_UPGRADE_KEY                  = (17 << KEYMASTER_REQ_SHIFT),
+    KM_CONFIGURE                    = (18 << KEYMASTER_REQ_SHIFT),
+    KM_GET_HMAC_SHARING_PARAMETERS  = (19 << KEYMASTER_REQ_SHIFT),
+    KM_COMPUTE_SHARED_HMAC          = (20 << KEYMASTER_REQ_SHIFT),
+    KM_VERIFY_AUTHORIZATION         = (21 << KEYMASTER_REQ_SHIFT),
+    KM_DELETE_KEY                   = (22 << KEYMASTER_REQ_SHIFT),
+    KM_DELETE_ALL_KEYS              = (23 << KEYMASTER_REQ_SHIFT),
+    KM_DESTROY_ATTESTATION_IDS      = (24 << KEYMASTER_REQ_SHIFT),
+    KM_IMPORT_WRAPPED_KEY           = (25 << KEYMASTER_REQ_SHIFT),
+};
+
+#ifdef __ANDROID__
+
+/**
+ * keymaster_message - Serial header for communicating with KM server
+ * @cmd: the command, one of keymaster_command.
+ * @payload: start of the serialized command specific payload
+ */
+struct keymaster_message {
+    uint32_t cmd;
+    uint8_t payload[0];
+};
+
+#endif
diff --git a/trusty/keymaster/include/trusty_keymaster/ipc/trusty_keymaster_ipc.h b/trusty/keymaster/include/trusty_keymaster/ipc/trusty_keymaster_ipc.h
new file mode 100644
index 0000000..16207e6
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/ipc/trusty_keymaster_ipc.h
@@ -0,0 +1,40 @@
+/*
+ * 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 TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
+#define TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
+
+#include <keymaster/android_keymaster_messages.h>
+#include <trusty_keymaster/ipc/keymaster_ipc.h>
+
+__BEGIN_DECLS
+
+const uint32_t TRUSTY_KEYMASTER_RECV_BUF_SIZE = 2 * PAGE_SIZE;
+const uint32_t TRUSTY_KEYMASTER_SEND_BUF_SIZE =
+        (PAGE_SIZE - sizeof(struct keymaster_message) - 16 /* tipc header */);
+
+int trusty_keymaster_connect(void);
+int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
+                          uint32_t* out_size);
+void trusty_keymaster_disconnect(void);
+
+keymaster_error_t translate_error(int err);
+keymaster_error_t trusty_keymaster_send(uint32_t command, const keymaster::Serializable& req,
+                                        keymaster::KeymasterResponse* rsp);
+
+__END_DECLS
+
+#endif  // TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
diff --git a/trusty/keymaster/include/trusty_keymaster/legacy/trusty_keymaster_device.h b/trusty/keymaster/include/trusty_keymaster/legacy/trusty_keymaster_device.h
new file mode 100644
index 0000000..a483c0d
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/legacy/trusty_keymaster_device.h
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+
+#ifndef TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
+#define TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
+
+#include <hardware/keymaster2.h>
+#include <keymaster/android_keymaster_messages.h>
+
+namespace keymaster {
+
+/**
+ * Trusty Keymaster device.
+ *
+ * IMPORTANT MAINTAINER NOTE: Pointers to instances of this class must be castable to hw_device_t
+ * and keymaster_device. This means it must remain a standard layout class (no virtual functions and
+ * no data members which aren't standard layout), and device_ must be the first data member.
+ * Assertions in the constructor validate compliance with those constraints.
+ */
+class TrustyKeymasterDevice {
+  public:
+    /*
+     * These are the only symbols that will be exported by libtrustykeymaster.  All functionality
+     * can be reached via the function pointers in device_.
+     */
+    __attribute__((visibility("default"))) explicit TrustyKeymasterDevice(
+            const hw_module_t* module);
+    __attribute__((visibility("default"))) hw_device_t* hw_device();
+
+    ~TrustyKeymasterDevice();
+
+    keymaster_error_t session_error() { return error_; }
+
+    keymaster_error_t configure(const keymaster_key_param_set_t* params);
+    keymaster_error_t add_rng_entropy(const uint8_t* data, size_t data_length);
+    keymaster_error_t generate_key(const keymaster_key_param_set_t* params,
+                                   keymaster_key_blob_t* key_blob,
+                                   keymaster_key_characteristics_t* characteristics);
+    keymaster_error_t get_key_characteristics(const keymaster_key_blob_t* key_blob,
+                                              const keymaster_blob_t* client_id,
+                                              const keymaster_blob_t* app_data,
+                                              keymaster_key_characteristics_t* character);
+    keymaster_error_t import_key(const keymaster_key_param_set_t* params,
+                                 keymaster_key_format_t key_format,
+                                 const keymaster_blob_t* key_data, keymaster_key_blob_t* key_blob,
+                                 keymaster_key_characteristics_t* characteristics);
+    keymaster_error_t export_key(keymaster_key_format_t export_format,
+                                 const keymaster_key_blob_t* key_to_export,
+                                 const keymaster_blob_t* client_id,
+                                 const keymaster_blob_t* app_data, keymaster_blob_t* export_data);
+    keymaster_error_t attest_key(const keymaster_key_blob_t* key_to_attest,
+                                 const keymaster_key_param_set_t* attest_params,
+                                 keymaster_cert_chain_t* cert_chain);
+    keymaster_error_t upgrade_key(const keymaster_key_blob_t* key_to_upgrade,
+                                  const keymaster_key_param_set_t* upgrade_params,
+                                  keymaster_key_blob_t* upgraded_key);
+    keymaster_error_t begin(keymaster_purpose_t purpose, const keymaster_key_blob_t* key,
+                            const keymaster_key_param_set_t* in_params,
+                            keymaster_key_param_set_t* out_params,
+                            keymaster_operation_handle_t* operation_handle);
+    keymaster_error_t update(keymaster_operation_handle_t operation_handle,
+                             const keymaster_key_param_set_t* in_params,
+                             const keymaster_blob_t* input, size_t* input_consumed,
+                             keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+    keymaster_error_t finish(keymaster_operation_handle_t operation_handle,
+                             const keymaster_key_param_set_t* in_params,
+                             const keymaster_blob_t* input, const keymaster_blob_t* signature,
+                             keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+    keymaster_error_t abort(keymaster_operation_handle_t operation_handle);
+    keymaster_error_t delete_key(const keymaster_key_blob_t* key);
+    keymaster_error_t delete_all_keys();
+
+  private:
+    keymaster_error_t Send(uint32_t command, const Serializable& request,
+                           KeymasterResponse* response);
+
+    /*
+     * These static methods are the functions referenced through the function pointers in
+     * keymaster_device.  They're all trivial wrappers.
+     */
+    static int close_device(hw_device_t* dev);
+    static keymaster_error_t configure(const keymaster2_device_t* dev,
+                                       const keymaster_key_param_set_t* params);
+    static keymaster_error_t add_rng_entropy(const keymaster2_device_t* dev, const uint8_t* data,
+                                             size_t data_length);
+    static keymaster_error_t generate_key(const keymaster2_device_t* dev,
+                                          const keymaster_key_param_set_t* params,
+                                          keymaster_key_blob_t* key_blob,
+                                          keymaster_key_characteristics_t* characteristics);
+    static keymaster_error_t get_key_characteristics(const keymaster2_device_t* dev,
+                                                     const keymaster_key_blob_t* key_blob,
+                                                     const keymaster_blob_t* client_id,
+                                                     const keymaster_blob_t* app_data,
+                                                     keymaster_key_characteristics_t* character);
+    static keymaster_error_t import_key(const keymaster2_device_t* dev,
+                                        const keymaster_key_param_set_t* params,
+                                        keymaster_key_format_t key_format,
+                                        const keymaster_blob_t* key_data,
+                                        keymaster_key_blob_t* key_blob,
+                                        keymaster_key_characteristics_t* characteristics);
+    static keymaster_error_t export_key(const keymaster2_device_t* dev,
+                                        keymaster_key_format_t export_format,
+                                        const keymaster_key_blob_t* key_to_export,
+                                        const keymaster_blob_t* client_id,
+                                        const keymaster_blob_t* app_data,
+                                        keymaster_blob_t* export_data);
+    static keymaster_error_t attest_key(const keymaster2_device_t* dev,
+                                        const keymaster_key_blob_t* key_to_attest,
+                                        const keymaster_key_param_set_t* attest_params,
+                                        keymaster_cert_chain_t* cert_chain);
+    static keymaster_error_t upgrade_key(const keymaster2_device_t* dev,
+                                         const keymaster_key_blob_t* key_to_upgrade,
+                                         const keymaster_key_param_set_t* upgrade_params,
+                                         keymaster_key_blob_t* upgraded_key);
+    static keymaster_error_t delete_key(const keymaster2_device_t* dev,
+                                        const keymaster_key_blob_t* key);
+    static keymaster_error_t delete_all_keys(const keymaster2_device_t* dev);
+    static keymaster_error_t begin(const keymaster2_device_t* dev, keymaster_purpose_t purpose,
+                                   const keymaster_key_blob_t* key,
+                                   const keymaster_key_param_set_t* in_params,
+                                   keymaster_key_param_set_t* out_params,
+                                   keymaster_operation_handle_t* operation_handle);
+    static keymaster_error_t update(const keymaster2_device_t* dev,
+                                    keymaster_operation_handle_t operation_handle,
+                                    const keymaster_key_param_set_t* in_params,
+                                    const keymaster_blob_t* input, size_t* input_consumed,
+                                    keymaster_key_param_set_t* out_params,
+                                    keymaster_blob_t* output);
+    static keymaster_error_t finish(const keymaster2_device_t* dev,
+                                    keymaster_operation_handle_t operation_handle,
+                                    const keymaster_key_param_set_t* in_params,
+                                    const keymaster_blob_t* input,
+                                    const keymaster_blob_t* signature,
+                                    keymaster_key_param_set_t* out_params,
+                                    keymaster_blob_t* output);
+    static keymaster_error_t abort(const keymaster2_device_t* dev,
+                                   keymaster_operation_handle_t operation_handle);
+
+    keymaster2_device_t device_;
+    keymaster_error_t error_;
+    int32_t message_version_;
+};
+
+}  // namespace keymaster
+
+#endif  // TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
diff --git a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
new file mode 100644
index 0000000..0956fe6
--- /dev/null
+++ b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
@@ -0,0 +1,182 @@
+/*
+ * 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 "TrustyKeymaster"
+
+// TODO: make this generic in libtrusty
+
+#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>
+
+#include <trusty_keymaster/ipc/keymaster_ipc.h>
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
+
+#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
+
+static int handle_ = -1;
+
+int trusty_keymaster_connect() {
+    int rc = tipc_connect(TRUSTY_DEVICE_NAME, KEYMASTER_PORT);
+    if (rc < 0) {
+        return rc;
+    }
+
+    handle_ = rc;
+    return 0;
+}
+
+int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
+                          uint32_t* out_size) {
+    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);
+
+    ssize_t rc = write(handle_, msg, msg_size);
+    free(msg);
+
+    if (rc < 0) {
+        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;
+        }
+
+        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;
+        }
+    }
+
+    return rc;
+}
+
+void trusty_keymaster_disconnect() {
+    if (handle_ >= 0) {
+        tipc_close(handle_);
+    }
+    handle_ = -1;
+}
+
+keymaster_error_t translate_error(int err) {
+    switch (err) {
+        case 0:
+            return KM_ERROR_OK;
+        case -EPERM:
+        case -EACCES:
+            return KM_ERROR_SECURE_HW_ACCESS_DENIED;
+
+        case -ECANCELED:
+            return KM_ERROR_OPERATION_CANCELLED;
+
+        case -ENODEV:
+            return KM_ERROR_UNIMPLEMENTED;
+
+        case -ENOMEM:
+            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+        case -EBUSY:
+            return KM_ERROR_SECURE_HW_BUSY;
+
+        case -EIO:
+            return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+
+        case -EOVERFLOW:
+            return KM_ERROR_INVALID_INPUT_LENGTH;
+
+        default:
+            return KM_ERROR_UNKNOWN_ERROR;
+    }
+}
+
+keymaster_error_t trusty_keymaster_send(uint32_t command, const keymaster::Serializable& req,
+                                        keymaster::KeymasterResponse* rsp) {
+    uint32_t req_size = req.SerializedSize();
+    if (req_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
+        ALOGE("Request too big: %u Max size: %u", req_size, TRUSTY_KEYMASTER_SEND_BUF_SIZE);
+        return KM_ERROR_INVALID_INPUT_LENGTH;
+    }
+
+    uint8_t send_buf[TRUSTY_KEYMASTER_SEND_BUF_SIZE];
+    keymaster::Eraser send_buf_eraser(send_buf, TRUSTY_KEYMASTER_SEND_BUF_SIZE);
+    req.Serialize(send_buf, send_buf + req_size);
+
+    // Send it
+    uint8_t recv_buf[TRUSTY_KEYMASTER_RECV_BUF_SIZE];
+    keymaster::Eraser recv_buf_eraser(recv_buf, TRUSTY_KEYMASTER_RECV_BUF_SIZE);
+    uint32_t rsp_size = TRUSTY_KEYMASTER_RECV_BUF_SIZE;
+    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);
+    } else {
+        ALOGV("Received %d byte response\n", rsp_size);
+    }
+
+    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;
+    } else if (rsp->error != KM_ERROR_OK) {
+        ALOGE("Response of size %d contained error code %d\n", (int)rsp_size, (int)rsp->error);
+        return rsp->error;
+    }
+    return rsp->error;
+}
diff --git a/trusty/keymaster/keymaster_ipc.h b/trusty/keymaster/keymaster_ipc.h
deleted file mode 100644
index d63757b..0000000
--- a/trusty/keymaster/keymaster_ipc.h
+++ /dev/null
@@ -1,63 +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.
- */
-
-#pragma once
-
-// clang-format off
-
-#define KEYMASTER_PORT "com.android.trusty.keymaster"
-#define KEYMASTER_MAX_BUFFER_LENGTH 4096
-
-// Commands
-enum keymaster_command : uint32_t {
-    KEYMASTER_RESP_BIT              = 1,
-    KEYMASTER_STOP_BIT              = 2,
-    KEYMASTER_REQ_SHIFT             = 2,
-
-    KM_GENERATE_KEY                 = (0 << KEYMASTER_REQ_SHIFT),
-    KM_BEGIN_OPERATION              = (1 << KEYMASTER_REQ_SHIFT),
-    KM_UPDATE_OPERATION             = (2 << KEYMASTER_REQ_SHIFT),
-    KM_FINISH_OPERATION             = (3 << KEYMASTER_REQ_SHIFT),
-    KM_ABORT_OPERATION              = (4 << KEYMASTER_REQ_SHIFT),
-    KM_IMPORT_KEY                   = (5 << KEYMASTER_REQ_SHIFT),
-    KM_EXPORT_KEY                   = (6 << KEYMASTER_REQ_SHIFT),
-    KM_GET_VERSION                  = (7 << KEYMASTER_REQ_SHIFT),
-    KM_ADD_RNG_ENTROPY              = (8 << KEYMASTER_REQ_SHIFT),
-    KM_GET_SUPPORTED_ALGORITHMS     = (9 << KEYMASTER_REQ_SHIFT),
-    KM_GET_SUPPORTED_BLOCK_MODES    = (10 << KEYMASTER_REQ_SHIFT),
-    KM_GET_SUPPORTED_PADDING_MODES  = (11 << KEYMASTER_REQ_SHIFT),
-    KM_GET_SUPPORTED_DIGESTS        = (12 << KEYMASTER_REQ_SHIFT),
-    KM_GET_SUPPORTED_IMPORT_FORMATS = (13 << KEYMASTER_REQ_SHIFT),
-    KM_GET_SUPPORTED_EXPORT_FORMATS = (14 << KEYMASTER_REQ_SHIFT),
-    KM_GET_KEY_CHARACTERISTICS      = (15 << KEYMASTER_REQ_SHIFT),
-    KM_ATTEST_KEY                   = (16 << KEYMASTER_REQ_SHIFT),
-    KM_UPGRADE_KEY                  = (17 << KEYMASTER_REQ_SHIFT),
-    KM_CONFIGURE                    = (18 << KEYMASTER_REQ_SHIFT),
-};
-
-#ifdef __ANDROID__
-
-/**
- * keymaster_message - Serial header for communicating with KM server
- * @cmd: the command, one of keymaster_command.
- * @payload: start of the serialized command specific payload
- */
-struct keymaster_message {
-    uint32_t cmd;
-    uint8_t payload[0];
-};
-
-#endif
diff --git a/trusty/keymaster/Makefile b/trusty/keymaster/legacy/Makefile
similarity index 100%
rename from trusty/keymaster/Makefile
rename to trusty/keymaster/legacy/Makefile
diff --git a/trusty/keymaster/legacy/module.cpp b/trusty/keymaster/legacy/module.cpp
new file mode 100644
index 0000000..7aa1a4e
--- /dev/null
+++ b/trusty/keymaster/legacy/module.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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 <string.h>
+
+#include <hardware/hardware.h>
+#include <hardware/keymaster0.h>
+
+#include <trusty_keymaster/legacy/trusty_keymaster_device.h>
+
+using keymaster::TrustyKeymasterDevice;
+
+/*
+ * Generic device handling
+ */
+static int trusty_keymaster_open(const hw_module_t* module, const char* name,
+                                 hw_device_t** device) {
+    if (strcmp(name, KEYSTORE_KEYMASTER) != 0) {
+        return -EINVAL;
+    }
+
+    TrustyKeymasterDevice* dev = new TrustyKeymasterDevice(module);
+    if (dev == NULL) {
+        return -ENOMEM;
+    }
+    *device = dev->hw_device();
+    // Do not delete dev; it will get cleaned up when the caller calls device->close(), and must
+    // exist until then.
+    return 0;
+}
+
+static struct hw_module_methods_t keystore_module_methods = {
+        .open = trusty_keymaster_open,
+};
+
+struct keystore_module HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
+        .common =
+                {
+                        .tag = HARDWARE_MODULE_TAG,
+                        .module_api_version = KEYMASTER_MODULE_API_VERSION_2_0,
+                        .hal_api_version = HARDWARE_HAL_API_VERSION,
+                        .id = KEYSTORE_HARDWARE_MODULE_ID,
+                        .name = "Trusty Keymaster HAL",
+                        .author = "The Android Open Source Project",
+                        .methods = &keystore_module_methods,
+                        .dso = 0,
+                        .reserved = {},
+                },
+};
diff --git a/trusty/keymaster/legacy/trusty_keymaster_device.cpp b/trusty/keymaster/legacy/trusty_keymaster_device.cpp
new file mode 100644
index 0000000..88c3e7b
--- /dev/null
+++ b/trusty/keymaster/legacy/trusty_keymaster_device.cpp
@@ -0,0 +1,761 @@
+/*
+ * 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 "TrustyKeymaster"
+
+#include <assert.h>
+#include <errno.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <algorithm>
+#include <type_traits>
+
+#include <hardware/keymaster2.h>
+#include <keymaster/authorization_set.h>
+#include <log/log.h>
+
+#include <trusty_keymaster/ipc/keymaster_ipc.h>
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
+#include <trusty_keymaster/legacy/trusty_keymaster_device.h>
+
+const size_t kMaximumAttestationChallengeLength = 128;
+const size_t kMaximumFinishInputLength = 2048;
+
+namespace keymaster {
+
+TrustyKeymasterDevice::TrustyKeymasterDevice(const hw_module_t* module) {
+    static_assert(std::is_standard_layout<TrustyKeymasterDevice>::value,
+                  "TrustyKeymasterDevice must be standard layout");
+    static_assert(offsetof(TrustyKeymasterDevice, device_) == 0,
+                  "device_ must be the first member of TrustyKeymasterDevice");
+    static_assert(offsetof(TrustyKeymasterDevice, device_.common) == 0,
+                  "common must be the first member of keymaster2_device");
+
+    ALOGI("Creating device");
+    ALOGD("Device address: %p", this);
+
+    device_ = {};
+
+    device_.common.tag = HARDWARE_DEVICE_TAG;
+    device_.common.version = 1;
+    device_.common.module = const_cast<hw_module_t*>(module);
+    device_.common.close = close_device;
+
+    device_.flags = KEYMASTER_SUPPORTS_EC;
+
+    device_.configure = configure;
+    device_.add_rng_entropy = add_rng_entropy;
+    device_.generate_key = generate_key;
+    device_.get_key_characteristics = get_key_characteristics;
+    device_.import_key = import_key;
+    device_.export_key = export_key;
+    device_.attest_key = attest_key;
+    device_.upgrade_key = upgrade_key;
+    device_.delete_key = delete_key;
+    device_.delete_all_keys = delete_all_keys;
+    device_.begin = begin;
+    device_.update = update;
+    device_.finish = finish;
+    device_.abort = abort;
+
+    int rc = trusty_keymaster_connect();
+    error_ = translate_error(rc);
+    if (rc < 0) {
+        ALOGE("failed to connect to keymaster (%d)", rc);
+        return;
+    }
+
+    GetVersionRequest version_request;
+    GetVersionResponse version_response;
+    error_ = trusty_keymaster_send(KM_GET_VERSION, version_request, &version_response);
+    if (error_ == KM_ERROR_INVALID_ARGUMENT || error_ == KM_ERROR_UNIMPLEMENTED) {
+        ALOGE("\"Bad parameters\" error on GetVersion call.  Version 0 is not supported.");
+        error_ = KM_ERROR_VERSION_MISMATCH;
+        return;
+    }
+    message_version_ = MessageVersion(version_response.major_ver, version_response.minor_ver,
+                                      version_response.subminor_ver);
+    if (message_version_ < 0) {
+        // Can't translate version?  Keymaster implementation must be newer.
+        ALOGE("Keymaster version %d.%d.%d not supported.", version_response.major_ver,
+              version_response.minor_ver, version_response.subminor_ver);
+        error_ = KM_ERROR_VERSION_MISMATCH;
+    }
+}
+
+TrustyKeymasterDevice::~TrustyKeymasterDevice() {
+    trusty_keymaster_disconnect();
+}
+
+namespace {
+
+// Allocates a new buffer with malloc and copies the contents of |buffer| to it. Caller takes
+// ownership of the returned buffer.
+uint8_t* DuplicateBuffer(const uint8_t* buffer, size_t size) {
+    uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(size));
+    if (tmp) {
+        memcpy(tmp, buffer, size);
+    }
+    return tmp;
+}
+
+template <typename RequestType>
+void AddClientAndAppData(const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
+                         RequestType* request) {
+    request->additional_params.Clear();
+    if (client_id && client_id->data_length > 0) {
+        request->additional_params.push_back(TAG_APPLICATION_ID, *client_id);
+    }
+    if (app_data && app_data->data_length > 0) {
+        request->additional_params.push_back(TAG_APPLICATION_DATA, *app_data);
+    }
+}
+
+}  //  unnamed namespace
+
+keymaster_error_t TrustyKeymasterDevice::configure(const keymaster_key_param_set_t* params) {
+    ALOGD("Device received configure\n");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!params) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+
+    AuthorizationSet params_copy(*params);
+    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(message_version_);
+    keymaster_error_t err = trusty_keymaster_send(KM_CONFIGURE, request, &response);
+    if (err != KM_ERROR_OK) {
+        return err;
+    }
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::add_rng_entropy(const uint8_t* data, size_t data_length) {
+    ALOGD("Device received add_rng_entropy");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+
+    AddEntropyRequest request(message_version_);
+    request.random_data.Reinitialize(data, data_length);
+    AddEntropyResponse response(message_version_);
+    return trusty_keymaster_send(KM_ADD_RNG_ENTROPY, request, &response);
+}
+
+keymaster_error_t TrustyKeymasterDevice::generate_key(
+        const keymaster_key_param_set_t* params, keymaster_key_blob_t* key_blob,
+        keymaster_key_characteristics_t* characteristics) {
+    ALOGD("Device received generate_key");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!params) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+    if (!key_blob) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    GenerateKeyRequest request(message_version_);
+    request.key_description.Reinitialize(*params);
+    request.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
+
+    GenerateKeyResponse response(message_version_);
+    keymaster_error_t err = trusty_keymaster_send(KM_GENERATE_KEY, request, &response);
+    if (err != KM_ERROR_OK) {
+        return err;
+    }
+
+    key_blob->key_material_size = response.key_blob.key_material_size;
+    key_blob->key_material =
+            DuplicateBuffer(response.key_blob.key_material, response.key_blob.key_material_size);
+    if (!key_blob->key_material) {
+        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
+
+    if (characteristics) {
+        response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+        response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+    }
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::get_key_characteristics(
+        const keymaster_key_blob_t* key_blob, const keymaster_blob_t* client_id,
+        const keymaster_blob_t* app_data, keymaster_key_characteristics_t* characteristics) {
+    ALOGD("Device received get_key_characteristics");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!key_blob || !key_blob->key_material) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+    if (!characteristics) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    GetKeyCharacteristicsRequest request(message_version_);
+    request.SetKeyMaterial(*key_blob);
+    AddClientAndAppData(client_id, app_data, &request);
+
+    GetKeyCharacteristicsResponse response(message_version_);
+    keymaster_error_t err = trusty_keymaster_send(KM_GET_KEY_CHARACTERISTICS, request, &response);
+    if (err != KM_ERROR_OK) {
+        return err;
+    }
+
+    response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+    response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::import_key(
+        const keymaster_key_param_set_t* params, keymaster_key_format_t key_format,
+        const keymaster_blob_t* key_data, keymaster_key_blob_t* key_blob,
+        keymaster_key_characteristics_t* characteristics) {
+    ALOGD("Device received import_key");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!params || !key_data) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+    if (!key_blob) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    ImportKeyRequest request(message_version_);
+    request.key_description.Reinitialize(*params);
+    request.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
+
+    request.key_format = key_format;
+    request.SetKeyMaterial(key_data->data, key_data->data_length);
+
+    ImportKeyResponse response(message_version_);
+    keymaster_error_t err = trusty_keymaster_send(KM_IMPORT_KEY, request, &response);
+    if (err != KM_ERROR_OK) {
+        return err;
+    }
+
+    key_blob->key_material_size = response.key_blob.key_material_size;
+    key_blob->key_material =
+            DuplicateBuffer(response.key_blob.key_material, response.key_blob.key_material_size);
+    if (!key_blob->key_material) {
+        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
+
+    if (characteristics) {
+        response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+        response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+    }
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::export_key(keymaster_key_format_t export_format,
+                                                    const keymaster_key_blob_t* key_to_export,
+                                                    const keymaster_blob_t* client_id,
+                                                    const keymaster_blob_t* app_data,
+                                                    keymaster_blob_t* export_data) {
+    ALOGD("Device received export_key");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!key_to_export || !key_to_export->key_material) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+    if (!export_data) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    export_data->data = nullptr;
+    export_data->data_length = 0;
+
+    ExportKeyRequest request(message_version_);
+    request.key_format = export_format;
+    request.SetKeyMaterial(*key_to_export);
+    AddClientAndAppData(client_id, app_data, &request);
+
+    ExportKeyResponse response(message_version_);
+    keymaster_error_t err = trusty_keymaster_send(KM_EXPORT_KEY, request, &response);
+    if (err != KM_ERROR_OK) {
+        return err;
+    }
+
+    export_data->data_length = response.key_data_length;
+    export_data->data = DuplicateBuffer(response.key_data, response.key_data_length);
+    if (!export_data->data) {
+        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::attest_key(const keymaster_key_blob_t* key_to_attest,
+                                                    const keymaster_key_param_set_t* attest_params,
+                                                    keymaster_cert_chain_t* cert_chain) {
+    ALOGD("Device received attest_key");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!key_to_attest || !attest_params) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+    if (!cert_chain) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    cert_chain->entry_count = 0;
+    cert_chain->entries = nullptr;
+
+    AttestKeyRequest request(message_version_);
+    request.SetKeyMaterial(*key_to_attest);
+    request.attest_params.Reinitialize(*attest_params);
+
+    keymaster_blob_t attestation_challenge = {};
+    request.attest_params.GetTagValue(TAG_ATTESTATION_CHALLENGE, &attestation_challenge);
+    if (attestation_challenge.data_length > kMaximumAttestationChallengeLength) {
+        ALOGE("%zu-byte attestation challenge; only %zu bytes allowed",
+              attestation_challenge.data_length, kMaximumAttestationChallengeLength);
+        return KM_ERROR_INVALID_INPUT_LENGTH;
+    }
+
+    AttestKeyResponse response(message_version_);
+    keymaster_error_t err = trusty_keymaster_send(KM_ATTEST_KEY, request, &response);
+    if (err != KM_ERROR_OK) {
+        return err;
+    }
+
+    // Allocate and clear storage for cert_chain.
+    keymaster_cert_chain_t& rsp_chain = response.certificate_chain;
+    cert_chain->entries = reinterpret_cast<keymaster_blob_t*>(
+            malloc(rsp_chain.entry_count * sizeof(*cert_chain->entries)));
+    if (!cert_chain->entries) {
+        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
+    cert_chain->entry_count = rsp_chain.entry_count;
+    for (keymaster_blob_t& entry : array_range(cert_chain->entries, cert_chain->entry_count)) {
+        entry = {};
+    }
+
+    // Copy cert_chain contents
+    size_t i = 0;
+    for (keymaster_blob_t& entry : array_range(rsp_chain.entries, rsp_chain.entry_count)) {
+        cert_chain->entries[i].data = DuplicateBuffer(entry.data, entry.data_length);
+        if (!cert_chain->entries[i].data) {
+            keymaster_free_cert_chain(cert_chain);
+            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+        }
+        cert_chain->entries[i].data_length = entry.data_length;
+        ++i;
+    }
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::upgrade_key(
+        const keymaster_key_blob_t* key_to_upgrade, const keymaster_key_param_set_t* upgrade_params,
+        keymaster_key_blob_t* upgraded_key) {
+    ALOGD("Device received upgrade_key");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!key_to_upgrade || !upgrade_params) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+    if (!upgraded_key) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    UpgradeKeyRequest request(message_version_);
+    request.SetKeyMaterial(*key_to_upgrade);
+    request.upgrade_params.Reinitialize(*upgrade_params);
+
+    UpgradeKeyResponse response(message_version_);
+    keymaster_error_t err = trusty_keymaster_send(KM_UPGRADE_KEY, request, &response);
+    if (err != KM_ERROR_OK) {
+        return err;
+    }
+
+    upgraded_key->key_material_size = response.upgraded_key.key_material_size;
+    upgraded_key->key_material = DuplicateBuffer(response.upgraded_key.key_material,
+                                                 response.upgraded_key.key_material_size);
+    if (!upgraded_key->key_material) {
+        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::begin(keymaster_purpose_t purpose,
+                                               const keymaster_key_blob_t* key,
+                                               const keymaster_key_param_set_t* in_params,
+                                               keymaster_key_param_set_t* out_params,
+                                               keymaster_operation_handle_t* operation_handle) {
+    ALOGD("Device received begin");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!key || !key->key_material) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+    if (!operation_handle) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    if (out_params) {
+        *out_params = {};
+    }
+
+    BeginOperationRequest request(message_version_);
+    request.purpose = purpose;
+    request.SetKeyMaterial(*key);
+    request.additional_params.Reinitialize(*in_params);
+
+    BeginOperationResponse response(message_version_);
+    keymaster_error_t err = trusty_keymaster_send(KM_BEGIN_OPERATION, request, &response);
+    if (err != KM_ERROR_OK) {
+        return err;
+    }
+
+    if (response.output_params.size() > 0) {
+        if (out_params) {
+            response.output_params.CopyToParamSet(out_params);
+        } else {
+            return KM_ERROR_OUTPUT_PARAMETER_NULL;
+        }
+    }
+    *operation_handle = response.op_handle;
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::update(keymaster_operation_handle_t operation_handle,
+                                                const keymaster_key_param_set_t* in_params,
+                                                const keymaster_blob_t* input,
+                                                size_t* input_consumed,
+                                                keymaster_key_param_set_t* out_params,
+                                                keymaster_blob_t* output) {
+    ALOGD("Device received update");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!input) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+    if (!input_consumed) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    if (out_params) {
+        *out_params = {};
+    }
+    if (output) {
+        *output = {};
+    }
+
+    UpdateOperationRequest request(message_version_);
+    request.op_handle = operation_handle;
+    if (in_params) {
+        request.additional_params.Reinitialize(*in_params);
+    }
+    if (input && input->data_length > 0) {
+        size_t max_input_size = TRUSTY_KEYMASTER_SEND_BUF_SIZE - request.SerializedSize();
+        request.input.Reinitialize(input->data, std::min(input->data_length, max_input_size));
+    }
+
+    UpdateOperationResponse response(message_version_);
+    keymaster_error_t err = trusty_keymaster_send(KM_UPDATE_OPERATION, request, &response);
+    if (err != KM_ERROR_OK) {
+        return err;
+    }
+
+    if (response.output_params.size() > 0) {
+        if (out_params) {
+            response.output_params.CopyToParamSet(out_params);
+        } else {
+            return KM_ERROR_OUTPUT_PARAMETER_NULL;
+        }
+    }
+    *input_consumed = response.input_consumed;
+    if (output) {
+        output->data_length = response.output.available_read();
+        output->data = DuplicateBuffer(response.output.peek_read(), output->data_length);
+        if (!output->data) {
+            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+        }
+    } else if (response.output.available_read() > 0) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::finish(keymaster_operation_handle_t operation_handle,
+                                                const keymaster_key_param_set_t* in_params,
+                                                const keymaster_blob_t* input,
+                                                const keymaster_blob_t* signature,
+                                                keymaster_key_param_set_t* out_params,
+                                                keymaster_blob_t* output) {
+    ALOGD("Device received finish");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (input && input->data_length > kMaximumFinishInputLength) {
+        ALOGE("%zu-byte input to finish; only %zu bytes allowed", input->data_length,
+              kMaximumFinishInputLength);
+        return KM_ERROR_INVALID_INPUT_LENGTH;
+    }
+
+    if (out_params) {
+        *out_params = {};
+    }
+    if (output) {
+        *output = {};
+    }
+
+    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);
+    }
+    if (input && input->data && input->data_length) {
+        request.input.Reinitialize(input->data, input->data_length);
+    }
+    if (in_params) {
+        request.additional_params.Reinitialize(*in_params);
+    }
+
+    FinishOperationResponse response(message_version_);
+    keymaster_error_t err = trusty_keymaster_send(KM_FINISH_OPERATION, request, &response);
+    if (err != KM_ERROR_OK) {
+        return err;
+    }
+
+    if (response.output_params.size() > 0) {
+        if (out_params) {
+            response.output_params.CopyToParamSet(out_params);
+        } else {
+            return KM_ERROR_OUTPUT_PARAMETER_NULL;
+        }
+    }
+    if (output) {
+        output->data_length = response.output.available_read();
+        output->data = DuplicateBuffer(response.output.peek_read(), output->data_length);
+        if (!output->data) {
+            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+        }
+    } else if (response.output.available_read() > 0) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::abort(keymaster_operation_handle_t operation_handle) {
+    ALOGD("Device received abort");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+
+    AbortOperationRequest request(message_version_);
+    request.op_handle = operation_handle;
+    AbortOperationResponse response(message_version_);
+    return trusty_keymaster_send(KM_ABORT_OPERATION, request, &response);
+}
+
+keymaster_error_t TrustyKeymasterDevice::delete_key(const keymaster_key_blob_t* key) {
+    ALOGD("Device received delete_key");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+
+    if (!key || !key->key_material)
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+
+    DeleteKeyRequest request(message_version_);
+    request.SetKeyMaterial(*key);
+    DeleteKeyResponse response(message_version_);
+    return trusty_keymaster_send(KM_DELETE_KEY, request, &response);
+}
+
+keymaster_error_t TrustyKeymasterDevice::delete_all_keys() {
+    ALOGD("Device received delete_all_key");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+
+    DeleteAllKeysRequest request(message_version_);
+    DeleteAllKeysResponse response(message_version_);
+    return trusty_keymaster_send(KM_DELETE_ALL_KEYS, request, &response);
+}
+
+hw_device_t* TrustyKeymasterDevice::hw_device() {
+    return &device_.common;
+}
+
+static inline TrustyKeymasterDevice* convert_device(const keymaster2_device_t* dev) {
+    return reinterpret_cast<TrustyKeymasterDevice*>(const_cast<keymaster2_device_t*>(dev));
+}
+
+/* static */
+int TrustyKeymasterDevice::close_device(hw_device_t* dev) {
+    delete reinterpret_cast<TrustyKeymasterDevice*>(dev);
+    return 0;
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::configure(const keymaster2_device_t* dev,
+                                                   const keymaster_key_param_set_t* params) {
+    return convert_device(dev)->configure(params);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::add_rng_entropy(const keymaster2_device_t* dev,
+                                                         const uint8_t* data, size_t data_length) {
+    return convert_device(dev)->add_rng_entropy(data, data_length);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::generate_key(
+        const keymaster2_device_t* dev, const keymaster_key_param_set_t* params,
+        keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) {
+    return convert_device(dev)->generate_key(params, key_blob, characteristics);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::get_key_characteristics(
+        const keymaster2_device_t* dev, const keymaster_key_blob_t* key_blob,
+        const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
+        keymaster_key_characteristics_t* characteristics) {
+    return convert_device(dev)->get_key_characteristics(key_blob, client_id, app_data,
+                                                        characteristics);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::import_key(
+        const keymaster2_device_t* dev, const keymaster_key_param_set_t* params,
+        keymaster_key_format_t key_format, const keymaster_blob_t* key_data,
+        keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) {
+    return convert_device(dev)->import_key(params, key_format, key_data, key_blob, characteristics);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::export_key(const keymaster2_device_t* dev,
+                                                    keymaster_key_format_t export_format,
+                                                    const keymaster_key_blob_t* key_to_export,
+                                                    const keymaster_blob_t* client_id,
+                                                    const keymaster_blob_t* app_data,
+                                                    keymaster_blob_t* export_data) {
+    return convert_device(dev)->export_key(export_format, key_to_export, client_id, app_data,
+                                           export_data);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::attest_key(const keymaster2_device_t* dev,
+                                                    const keymaster_key_blob_t* key_to_attest,
+                                                    const keymaster_key_param_set_t* attest_params,
+                                                    keymaster_cert_chain_t* cert_chain) {
+    return convert_device(dev)->attest_key(key_to_attest, attest_params, cert_chain);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::upgrade_key(
+        const keymaster2_device_t* dev, const keymaster_key_blob_t* key_to_upgrade,
+        const keymaster_key_param_set_t* upgrade_params, keymaster_key_blob_t* upgraded_key) {
+    return convert_device(dev)->upgrade_key(key_to_upgrade, upgrade_params, upgraded_key);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::begin(const keymaster2_device_t* dev,
+                                               keymaster_purpose_t purpose,
+                                               const keymaster_key_blob_t* key,
+                                               const keymaster_key_param_set_t* in_params,
+                                               keymaster_key_param_set_t* out_params,
+                                               keymaster_operation_handle_t* operation_handle) {
+    return convert_device(dev)->begin(purpose, key, in_params, out_params, operation_handle);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::update(
+        const keymaster2_device_t* dev, keymaster_operation_handle_t operation_handle,
+        const keymaster_key_param_set_t* in_params, const keymaster_blob_t* input,
+        size_t* input_consumed, keymaster_key_param_set_t* out_params, keymaster_blob_t* output) {
+    return convert_device(dev)->update(operation_handle, in_params, input, input_consumed,
+                                       out_params, output);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::finish(const keymaster2_device_t* dev,
+                                                keymaster_operation_handle_t operation_handle,
+                                                const keymaster_key_param_set_t* in_params,
+                                                const keymaster_blob_t* input,
+                                                const keymaster_blob_t* signature,
+                                                keymaster_key_param_set_t* out_params,
+                                                keymaster_blob_t* output) {
+    return convert_device(dev)->finish(operation_handle, in_params, input, signature, out_params,
+                                       output);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::abort(const keymaster2_device_t* dev,
+                                               keymaster_operation_handle_t operation_handle) {
+    return convert_device(dev)->abort(operation_handle);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::delete_key(const keymaster2_device_t* dev,
+                                               const keymaster_key_blob_t* key) {
+   return convert_device(dev)->delete_key(key);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::delete_all_keys(const keymaster2_device_t* dev) {
+   return convert_device(dev)->delete_all_keys();
+}
+
+}  // namespace keymaster
diff --git a/trusty/keymaster/legacy/trusty_keymaster_device_test.cpp b/trusty/keymaster/legacy/trusty_keymaster_device_test.cpp
new file mode 100644
index 0000000..68def58
--- /dev/null
+++ b/trusty/keymaster/legacy/trusty_keymaster_device_test.cpp
@@ -0,0 +1,561 @@
+/*
+ * 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 <algorithm>
+#include <fstream>
+#include <memory>
+
+#include <gtest/gtest.h>
+#include <openssl/engine.h>
+
+#include <hardware/keymaster0.h>
+
+#include <keymaster/android_keymaster.h>
+#include <keymaster/android_keymaster_messages.h>
+#include <keymaster/android_keymaster_utils.h>
+#include <keymaster/keymaster_tags.h>
+#include <keymaster/soft_keymaster_context.h>
+
+#include <trusty_keymaster/legacy/trusty_keymaster_device.h>
+#include "android_keymaster_test_utils.h"
+#include "openssl_utils.h"
+
+using std::ifstream;
+using std::istreambuf_iterator;
+using std::string;
+
+static keymaster::AndroidKeymaster* impl_ = nullptr;
+
+extern "C" {
+int __android_log_print();
+}
+
+int __android_log_print() {
+    return 0;
+}
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    int result = RUN_ALL_TESTS();
+    // Clean up stuff OpenSSL leaves around, so Valgrind doesn't complain.
+    CRYPTO_cleanup_all_ex_data();
+    ERR_free_strings();
+    return result;
+}
+
+int trusty_keymaster_connect() {
+    impl_ = new keymaster::AndroidKeymaster(new keymaster::SoftKeymasterContext(nullptr), 16);
+}
+
+void trusty_keymaster_disconnect() {
+    delete static_cast<keymaster::AndroidKeymaster*>(priv_);
+}
+
+template <typename Req, typename Rsp>
+static int fake_call(keymaster::AndroidKeymaster* device,
+                     void (keymaster::AndroidKeymaster::*method)(const Req&, Rsp*), void* in_buf,
+                     uint32_t in_size, void* out_buf, uint32_t* out_size) {
+    Req req;
+    const uint8_t* in = static_cast<uint8_t*>(in_buf);
+    req.Deserialize(&in, in + in_size);
+    Rsp rsp;
+    (device->*method)(req, &rsp);
+
+    *out_size = rsp.SerializedSize();
+    uint8_t* out = static_cast<uint8_t*>(out_buf);
+    rsp.Serialize(out, out + *out_size);
+    return 0;
+}
+
+int trusty_keymaster_call(uint32_t cmd, void* in_buf, uint32_t in_size, void* out_buf,
+                          uint32_t* out_size) {
+    switch (cmd) {
+        case KM_GENERATE_KEY:
+            return fake_call(impl_, &keymaster::AndroidKeymaster::GenerateKey, in_buf, in_size,
+                             out_buf, out_size);
+        case KM_BEGIN_OPERATION:
+            return fake_call(impl_, &keymaster::AndroidKeymaster::BeginOperation, in_buf, in_size,
+                             out_buf, out_size);
+        case KM_UPDATE_OPERATION:
+            return fake_call(impl_, &keymaster::AndroidKeymaster::UpdateOperation, in_buf, in_size,
+                             out_buf, out_size);
+        case KM_FINISH_OPERATION:
+            return fake_call(impl_, &keymaster::AndroidKeymaster::FinishOperation, in_buf, in_size,
+                             out_buf, out_size);
+        case KM_IMPORT_KEY:
+            return fake_call(impl_, &keymaster::AndroidKeymaster::ImportKey, in_buf, in_size,
+                             out_buf, out_size);
+        case KM_EXPORT_KEY:
+            return fake_call(impl_, &keymaster::AndroidKeymaster::ExportKey, in_buf, in_size,
+                             out_buf, out_size);
+    }
+    return -EINVAL;
+}
+
+namespace keymaster {
+namespace test {
+
+class TrustyKeymasterTest : public testing::Test {
+  protected:
+    TrustyKeymasterTest() : device(NULL) {}
+
+    keymaster_rsa_keygen_params_t build_rsa_params() {
+        keymaster_rsa_keygen_params_t rsa_params;
+        rsa_params.public_exponent = 65537;
+        rsa_params.modulus_size = 2048;
+        return rsa_params;
+    }
+
+    uint8_t* build_message(size_t length) {
+        uint8_t* msg = new uint8_t[length];
+        memset(msg, 'a', length);
+        return msg;
+    }
+
+    size_t dsa_message_len(const keymaster_dsa_keygen_params_t& params) {
+        switch (params.key_size) {
+            case 256:
+            case 1024:
+                return 48;
+            case 2048:
+            case 4096:
+                return 72;
+            default:
+                // Oops.
+                return 0;
+        }
+    }
+
+    TrustyKeymasterDevice device;
+};
+
+class Malloc_Delete {
+  public:
+    Malloc_Delete(void* p) : p_(p) {}
+    ~Malloc_Delete() { free(p_); }
+
+  private:
+    void* p_;
+};
+
+typedef TrustyKeymasterTest KeyGenTest;
+TEST_F(KeyGenTest, RsaSuccess) {
+    keymaster_rsa_keygen_params_t params = build_rsa_params();
+    uint8_t* ptr = NULL;
+    size_t size;
+    ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
+    EXPECT_GT(size, 0U);
+    Malloc_Delete key_deleter(ptr);
+}
+
+TEST_F(KeyGenTest, EcdsaSuccess) {
+    keymaster_ec_keygen_params_t ec_params = {256};
+    uint8_t* ptr = NULL;
+    size_t size;
+    ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &ec_params, &ptr, &size));
+    EXPECT_GT(size, 0U);
+    Malloc_Delete key_deleter(ptr);
+}
+
+typedef TrustyKeymasterTest SigningTest;
+TEST_F(SigningTest, RsaSuccess) {
+    keymaster_rsa_keygen_params_t params = build_rsa_params();
+    uint8_t* ptr = NULL;
+    size_t size;
+    ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
+    EXPECT_GT(size, 0U);
+    Malloc_Delete key_deleter(ptr);
+
+    keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+    size_t message_len = params.modulus_size / 8;
+    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,
+                                            &signature, &siglen));
+    Malloc_Delete sig_deleter(signature);
+    EXPECT_EQ(message_len, siglen);
+}
+
+TEST_F(SigningTest, RsaShortMessage) {
+    keymaster_rsa_keygen_params_t params = build_rsa_params();
+    uint8_t* ptr = NULL;
+    size_t size;
+    ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
+    EXPECT_GT(size, 0U);
+    Malloc_Delete key_deleter(ptr);
+
+    keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+    size_t message_len = params.modulus_size / 8 - 1;
+    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(),
+                                                       message_len, &signature, &siglen));
+}
+
+TEST_F(SigningTest, RsaLongMessage) {
+    keymaster_rsa_keygen_params_t params = build_rsa_params();
+    uint8_t* ptr = NULL;
+    size_t size;
+    ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
+    EXPECT_GT(size, 0U);
+    Malloc_Delete key_deleter(ptr);
+
+    keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+    size_t message_len = params.modulus_size / 8 + 1;
+    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(),
+                                                       message_len, &signature, &siglen));
+}
+
+TEST_F(SigningTest, EcdsaSuccess) {
+    keymaster_ec_keygen_params_t params = {256};
+    uint8_t* ptr = NULL;
+    size_t size;
+    ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &params, &ptr, &size));
+    EXPECT_GT(size, 0U);
+    Malloc_Delete key_deleter(ptr);
+
+    keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+    uint8_t message[] = "12345678901234567890123456789012";
+    uint8_t* signature;
+    size_t siglen;
+    ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message,
+                                            array_size(message) - 1, &signature, &siglen));
+    Malloc_Delete sig_deleter(signature);
+    EXPECT_GT(siglen, 69U);
+    EXPECT_LT(siglen, 73U);
+}
+
+TEST_F(SigningTest, EcdsaEmptyMessageSuccess) {
+    keymaster_ec_keygen_params_t params = {256};
+    uint8_t* ptr = NULL;
+    size_t size;
+    ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &params, &ptr, &size));
+    EXPECT_GT(size, 0U);
+    Malloc_Delete key_deleter(ptr);
+
+    keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+    uint8_t message[] = "";
+    uint8_t* signature;
+    size_t siglen;
+    ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message,
+                                            array_size(message) - 1, &signature, &siglen));
+    Malloc_Delete sig_deleter(signature);
+    EXPECT_GT(siglen, 69U);
+    EXPECT_LT(siglen, 73U);
+}
+
+TEST_F(SigningTest, EcdsaLargeMessageSuccess) {
+    keymaster_ec_keygen_params_t params = {256};
+    uint8_t* ptr = NULL;
+    size_t size;
+    ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &params, &ptr, &size));
+    EXPECT_GT(size, 0U);
+    Malloc_Delete key_deleter(ptr);
+
+    keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+    size_t message_len = 1024 * 7;
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
+    // contents of message don't matter.
+    uint8_t* signature;
+    size_t siglen;
+    ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+                                            &signature, &siglen));
+    Malloc_Delete sig_deleter(signature);
+    EXPECT_GT(siglen, 69U);
+    EXPECT_LT(siglen, 73U);
+}
+
+typedef TrustyKeymasterTest VerificationTest;
+TEST_F(VerificationTest, RsaSuccess) {
+    keymaster_rsa_keygen_params_t params = build_rsa_params();
+    uint8_t* ptr = NULL;
+    size_t size;
+    ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
+    EXPECT_GT(size, 0U);
+    Malloc_Delete key_deleter(ptr);
+
+    keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+    size_t message_len = params.modulus_size / 8;
+    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,
+                                            &signature, &siglen));
+    Malloc_Delete sig_deleter(signature);
+
+    EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, ptr, size, message.get(), message_len,
+                                              signature, siglen));
+}
+
+TEST_F(VerificationTest, RsaBadSignature) {
+    keymaster_rsa_keygen_params_t params = build_rsa_params();
+    uint8_t* ptr = NULL;
+    size_t size;
+    ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
+    EXPECT_GT(size, 0U);
+    Malloc_Delete key_deleter(ptr);
+
+    keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+    size_t message_len = params.modulus_size / 8;
+    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,
+                                            &signature, &siglen));
+
+    Malloc_Delete sig_deleter(signature);
+    signature[siglen / 2]++;
+    EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED,
+              device.verify_data(&sig_params, ptr, size, message.get(), message_len, signature,
+                                 siglen));
+}
+
+TEST_F(VerificationTest, RsaBadMessage) {
+    keymaster_rsa_keygen_params_t params = build_rsa_params();
+    uint8_t* ptr = NULL;
+    size_t size;
+    ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
+    EXPECT_GT(size, 0U);
+    Malloc_Delete key_deleter(ptr);
+
+    keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+    size_t message_len = params.modulus_size / 8;
+    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,
+                                            &signature, &siglen));
+    Malloc_Delete sig_deleter(signature);
+    message[0]++;
+    EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED,
+              device.verify_data(&sig_params, ptr, size, message.get(), message_len, signature,
+                                 siglen));
+}
+
+TEST_F(VerificationTest, RsaShortMessage) {
+    keymaster_rsa_keygen_params_t params = build_rsa_params();
+    uint8_t* ptr = NULL;
+    size_t size;
+    ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
+    EXPECT_GT(size, 0U);
+    Malloc_Delete key_deleter(ptr);
+
+    keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+    size_t message_len = params.modulus_size / 8;
+    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,
+                                            &signature, &siglen));
+
+    Malloc_Delete sig_deleter(signature);
+    EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH,
+              device.verify_data(&sig_params, ptr, size, message.get(), message_len - 1, signature,
+                                 siglen));
+}
+
+TEST_F(VerificationTest, RsaLongMessage) {
+    keymaster_rsa_keygen_params_t params = build_rsa_params();
+    uint8_t* ptr = NULL;
+    size_t size;
+    ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
+    EXPECT_GT(size, 0U);
+    Malloc_Delete key_deleter(ptr);
+
+    keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+    size_t message_len = params.modulus_size / 8;
+    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,
+                                            &signature, &siglen));
+    Malloc_Delete sig_deleter(signature);
+    EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH,
+              device.verify_data(&sig_params, ptr, size, message.get(), message_len + 1, signature,
+                                 siglen));
+}
+
+TEST_F(VerificationTest, EcdsaSuccess) {
+    keymaster_ec_keygen_params_t params = {256};
+    uint8_t* ptr = NULL;
+    size_t size;
+    ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &params, &ptr, &size));
+    EXPECT_GT(size, 0U);
+    Malloc_Delete key_deleter(ptr);
+
+    keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+    uint8_t message[] = "12345678901234567890123456789012";
+    uint8_t* signature;
+    size_t siglen;
+    ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message,
+                                            array_size(message) - 1, &signature, &siglen));
+    Malloc_Delete sig_deleter(signature);
+    EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, ptr, size, message,
+                                              array_size(message) - 1, signature, siglen));
+}
+
+TEST_F(VerificationTest, EcdsaLargeMessageSuccess) {
+    keymaster_ec_keygen_params_t params = {256};
+    uint8_t* ptr = NULL;
+    size_t size;
+    ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &params, &ptr, &size));
+    EXPECT_GT(size, 0U);
+    Malloc_Delete key_deleter(ptr);
+
+    keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+    size_t message_len = 1024 * 7;
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
+    // contents of message don't matter.
+    uint8_t* signature;
+    size_t siglen;
+    ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+                                            &signature, &siglen));
+    Malloc_Delete sig_deleter(signature);
+    EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, ptr, size, message.get(), message_len,
+                                              signature, siglen));
+}
+
+static string read_file(const string& file_name) {
+    ifstream file_stream(file_name, std::ios::binary);
+    istreambuf_iterator<char> file_begin(file_stream);
+    istreambuf_iterator<char> file_end;
+    return string(file_begin, file_end);
+}
+
+typedef TrustyKeymasterTest ImportKeyTest;
+TEST_F(ImportKeyTest, RsaSuccess) {
+    string pk8_key = read_file("../../../../system/keymaster/rsa_privkey_pk8.der");
+    ASSERT_EQ(633U, pk8_key.size());
+
+    uint8_t* key = NULL;
+    size_t size;
+    ASSERT_EQ(KM_ERROR_OK, device.import_keypair(reinterpret_cast<const uint8_t*>(pk8_key.data()),
+                                                 pk8_key.size(), &key, &size));
+    Malloc_Delete key_deleter(key);
+
+    keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+    size_t message_size = 1024 /* key size */ / 8;
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_size]);
+    memset(message.get(), 'a', message_size);
+    uint8_t* signature;
+    size_t siglen;
+    ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, key, size, message.get(), message_size,
+                                            &signature, &siglen));
+    Malloc_Delete sig_deleter(signature);
+    EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, key, size, message.get(), message_size,
+                                              signature, siglen));
+}
+
+TEST_F(ImportKeyTest, EcdsaSuccess) {
+    string pk8_key = read_file("../../../../system/keymaster/ec_privkey_pk8.der");
+    ASSERT_EQ(138U, pk8_key.size());
+
+    uint8_t* key = NULL;
+    size_t size;
+    ASSERT_EQ(KM_ERROR_OK, device.import_keypair(reinterpret_cast<const uint8_t*>(pk8_key.data()),
+                                                 pk8_key.size(), &key, &size));
+    Malloc_Delete key_deleter(key);
+
+    keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+    uint8_t message[] = "12345678901234567890123456789012";
+    uint8_t* signature;
+    size_t siglen;
+    ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, key, size, message,
+                                            array_size(message) - 1, &signature, &siglen));
+    Malloc_Delete sig_deleter(signature);
+    EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, key, size, message,
+                                              array_size(message) - 1, signature, siglen));
+}
+
+struct EVP_PKEY_CTX_Delete {
+    void operator()(EVP_PKEY_CTX* p) { EVP_PKEY_CTX_free(p); }
+};
+
+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) {
+    std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &key, key_len));
+    ASSERT_TRUE(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)
+        ASSERT_EQ(1, EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_NO_PADDING));
+    EXPECT_EQ(1, EVP_PKEY_verify(ctx.get(), signature, signature_len, message, message_len));
+}
+
+typedef TrustyKeymasterTest ExportKeyTest;
+TEST_F(ExportKeyTest, RsaSuccess) {
+    keymaster_rsa_keygen_params_t params = build_rsa_params();
+    uint8_t* ptr = NULL;
+    size_t size;
+    ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
+    EXPECT_GT(size, 0U);
+    Malloc_Delete key_deleter(ptr);
+
+    uint8_t* exported;
+    size_t exported_size;
+    EXPECT_EQ(KM_ERROR_OK, device.get_keypair_public(ptr, size, &exported, &exported_size));
+    Malloc_Delete exported_deleter(exported);
+
+    // 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;
+    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,
+                                            &signature, &siglen));
+    Malloc_Delete sig_deleter(signature);
+    EXPECT_EQ(message_len, siglen);
+    const uint8_t* tmp = exported;
+
+    VerifySignature(exported, exported_size, signature, siglen, message.get(), message_len);
+}
+
+typedef TrustyKeymasterTest ExportKeyTest;
+TEST_F(ExportKeyTest, EcdsaSuccess) {
+    keymaster_ec_keygen_params_t params = {256};
+    uint8_t* key = NULL;
+    size_t size;
+    ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &params, &key, &size));
+    EXPECT_GT(size, 0U);
+    Malloc_Delete key_deleter(key);
+
+    uint8_t* exported;
+    size_t exported_size;
+    EXPECT_EQ(KM_ERROR_OK, device.get_keypair_public(key, size, &exported, &exported_size));
+    Malloc_Delete exported_deleter(exported);
+
+    // Sign a message so we can verify it with the exported pubkey.
+    keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+    uint8_t message[] = "12345678901234567890123456789012";
+    uint8_t* signature;
+    size_t siglen;
+    ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, key, size, message,
+                                            array_size(message) - 1, &signature, &siglen));
+    Malloc_Delete sig_deleter(signature);
+    EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, key, size, message,
+                                              array_size(message) - 1, signature, siglen));
+
+    VerifySignature(exported, exported_size, signature, siglen, message, array_size(message) - 1);
+}
+
+}  // namespace test
+}  // namespace keymaster
diff --git a/trusty/keymaster/legacy/trusty_keymaster_main.cpp b/trusty/keymaster/legacy/trusty_keymaster_main.cpp
new file mode 100644
index 0000000..e3e70e6
--- /dev/null
+++ b/trusty/keymaster/legacy/trusty_keymaster_main.cpp
@@ -0,0 +1,408 @@
+/*
+ * 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.
+ */
+
+#include <keymaster/keymaster_configuration.h>
+
+#include <stdio.h>
+#include <memory>
+
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#include <trusty_keymaster/legacy/trusty_keymaster_device.h>
+
+using keymaster::TrustyKeymasterDevice;
+
+unsigned char rsa_privkey_pk8_der[] = {
+        0x30, 0x82, 0x02, 0x75, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+        0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x5f, 0x30, 0x82, 0x02, 0x5b,
+        0x02, 0x01, 0x00, 0x02, 0x81, 0x81, 0x00, 0xc6, 0x09, 0x54, 0x09, 0x04, 0x7d, 0x86, 0x34,
+        0x81, 0x2d, 0x5a, 0x21, 0x81, 0x76, 0xe4, 0x5c, 0x41, 0xd6, 0x0a, 0x75, 0xb1, 0x39, 0x01,
+        0xf2, 0x34, 0x22, 0x6c, 0xff, 0xe7, 0x76, 0x52, 0x1c, 0x5a, 0x77, 0xb9, 0xe3, 0x89, 0x41,
+        0x7b, 0x71, 0xc0, 0xb6, 0xa4, 0x4d, 0x13, 0xaf, 0xe4, 0xe4, 0xa2, 0x80, 0x5d, 0x46, 0xc9,
+        0xda, 0x29, 0x35, 0xad, 0xb1, 0xff, 0x0c, 0x1f, 0x24, 0xea, 0x06, 0xe6, 0x2b, 0x20, 0xd7,
+        0x76, 0x43, 0x0a, 0x4d, 0x43, 0x51, 0x57, 0x23, 0x3c, 0x6f, 0x91, 0x67, 0x83, 0xc3, 0x0e,
+        0x31, 0x0f, 0xcb, 0xd8, 0x9b, 0x85, 0xc2, 0xd5, 0x67, 0x71, 0x16, 0x97, 0x85, 0xac, 0x12,
+        0xbc, 0xa2, 0x44, 0xab, 0xda, 0x72, 0xbf, 0xb1, 0x9f, 0xc4, 0x4d, 0x27, 0xc8, 0x1e, 0x1d,
+        0x92, 0xde, 0x28, 0x4f, 0x40, 0x61, 0xed, 0xfd, 0x99, 0x28, 0x07, 0x45, 0xea, 0x6d, 0x25,
+        0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x81, 0x80, 0x1b, 0xe0, 0xf0, 0x4d, 0x9c, 0xae, 0x37,
+        0x18, 0x69, 0x1f, 0x03, 0x53, 0x38, 0x30, 0x8e, 0x91, 0x56, 0x4b, 0x55, 0x89, 0x9f, 0xfb,
+        0x50, 0x84, 0xd2, 0x46, 0x0e, 0x66, 0x30, 0x25, 0x7e, 0x05, 0xb3, 0xce, 0xab, 0x02, 0x97,
+        0x2d, 0xfa, 0xbc, 0xd6, 0xce, 0x5f, 0x6e, 0xe2, 0x58, 0x9e, 0xb6, 0x79, 0x11, 0xed, 0x0f,
+        0xac, 0x16, 0xe4, 0x3a, 0x44, 0x4b, 0x8c, 0x86, 0x1e, 0x54, 0x4a, 0x05, 0x93, 0x36, 0x57,
+        0x72, 0xf8, 0xba, 0xf6, 0xb2, 0x2f, 0xc9, 0xe3, 0xc5, 0xf1, 0x02, 0x4b, 0x06, 0x3a, 0xc0,
+        0x80, 0xa7, 0xb2, 0x23, 0x4c, 0xf8, 0xae, 0xe8, 0xf6, 0xc4, 0x7b, 0xbf, 0x4f, 0xd3, 0xac,
+        0xe7, 0x24, 0x02, 0x90, 0xbe, 0xf1, 0x6c, 0x0b, 0x3f, 0x7f, 0x3c, 0xdd, 0x64, 0xce, 0x3a,
+        0xb5, 0x91, 0x2c, 0xf6, 0xe3, 0x2f, 0x39, 0xab, 0x18, 0x83, 0x58, 0xaf, 0xcc, 0xcd, 0x80,
+        0x81, 0x02, 0x41, 0x00, 0xe4, 0xb4, 0x9e, 0xf5, 0x0f, 0x76, 0x5d, 0x3b, 0x24, 0xdd, 0xe0,
+        0x1a, 0xce, 0xaa, 0xf1, 0x30, 0xf2, 0xc7, 0x66, 0x70, 0xa9, 0x1a, 0x61, 0xae, 0x08, 0xaf,
+        0x49, 0x7b, 0x4a, 0x82, 0xbe, 0x6d, 0xee, 0x8f, 0xcd, 0xd5, 0xe3, 0xf7, 0xba, 0x1c, 0xfb,
+        0x1f, 0x0c, 0x92, 0x6b, 0x88, 0xf8, 0x8c, 0x92, 0xbf, 0xab, 0x13, 0x7f, 0xba, 0x22, 0x85,
+        0x22, 0x7b, 0x83, 0xc3, 0x42, 0xff, 0x7c, 0x55, 0x02, 0x41, 0x00, 0xdd, 0xab, 0xb5, 0x83,
+        0x9c, 0x4c, 0x7f, 0x6b, 0xf3, 0xd4, 0x18, 0x32, 0x31, 0xf0, 0x05, 0xb3, 0x1a, 0xa5, 0x8a,
+        0xff, 0xdd, 0xa5, 0xc7, 0x9e, 0x4c, 0xce, 0x21, 0x7f, 0x6b, 0xc9, 0x30, 0xdb, 0xe5, 0x63,
+        0xd4, 0x80, 0x70, 0x6c, 0x24, 0xe9, 0xeb, 0xfc, 0xab, 0x28, 0xa6, 0xcd, 0xef, 0xd3, 0x24,
+        0xb7, 0x7e, 0x1b, 0xf7, 0x25, 0x1b, 0x70, 0x90, 0x92, 0xc2, 0x4f, 0xf5, 0x01, 0xfd, 0x91,
+        0x02, 0x40, 0x23, 0xd4, 0x34, 0x0e, 0xda, 0x34, 0x45, 0xd8, 0xcd, 0x26, 0xc1, 0x44, 0x11,
+        0xda, 0x6f, 0xdc, 0xa6, 0x3c, 0x1c, 0xcd, 0x4b, 0x80, 0xa9, 0x8a, 0xd5, 0x2b, 0x78, 0xcc,
+        0x8a, 0xd8, 0xbe, 0xb2, 0x84, 0x2c, 0x1d, 0x28, 0x04, 0x05, 0xbc, 0x2f, 0x6c, 0x1b, 0xea,
+        0x21, 0x4a, 0x1d, 0x74, 0x2a, 0xb9, 0x96, 0xb3, 0x5b, 0x63, 0xa8, 0x2a, 0x5e, 0x47, 0x0f,
+        0xa8, 0x8d, 0xbf, 0x82, 0x3c, 0xdd, 0x02, 0x40, 0x1b, 0x7b, 0x57, 0x44, 0x9a, 0xd3, 0x0d,
+        0x15, 0x18, 0x24, 0x9a, 0x5f, 0x56, 0xbb, 0x98, 0x29, 0x4d, 0x4b, 0x6a, 0xc1, 0x2f, 0xfc,
+        0x86, 0x94, 0x04, 0x97, 0xa5, 0xa5, 0x83, 0x7a, 0x6c, 0xf9, 0x46, 0x26, 0x2b, 0x49, 0x45,
+        0x26, 0xd3, 0x28, 0xc1, 0x1e, 0x11, 0x26, 0x38, 0x0f, 0xde, 0x04, 0xc2, 0x4f, 0x91, 0x6d,
+        0xec, 0x25, 0x08, 0x92, 0xdb, 0x09, 0xa6, 0xd7, 0x7c, 0xdb, 0xa3, 0x51, 0x02, 0x40, 0x77,
+        0x62, 0xcd, 0x8f, 0x4d, 0x05, 0x0d, 0xa5, 0x6b, 0xd5, 0x91, 0xad, 0xb5, 0x15, 0xd2, 0x4d,
+        0x7c, 0xcd, 0x32, 0xcc, 0xa0, 0xd0, 0x5f, 0x86, 0x6d, 0x58, 0x35, 0x14, 0xbd, 0x73, 0x24,
+        0xd5, 0xf3, 0x36, 0x45, 0xe8, 0xed, 0x8b, 0x4a, 0x1c, 0xb3, 0xcc, 0x4a, 0x1d, 0x67, 0x98,
+        0x73, 0x99, 0xf2, 0xa0, 0x9f, 0x5b, 0x3f, 0xb6, 0x8c, 0x88, 0xd5, 0xe5, 0xd9, 0x0a, 0xc3,
+        0x34, 0x92, 0xd6};
+unsigned int rsa_privkey_pk8_der_len = 633;
+
+unsigned char dsa_privkey_pk8_der[] = {
+        0x30, 0x82, 0x01, 0x4b, 0x02, 0x01, 0x00, 0x30, 0x82, 0x01, 0x2b, 0x06, 0x07, 0x2a, 0x86,
+        0x48, 0xce, 0x38, 0x04, 0x01, 0x30, 0x82, 0x01, 0x1e, 0x02, 0x81, 0x81, 0x00, 0xa3, 0xf3,
+        0xe9, 0xb6, 0x7e, 0x7d, 0x88, 0xf6, 0xb7, 0xe5, 0xf5, 0x1f, 0x3b, 0xee, 0xac, 0xd7, 0xad,
+        0xbc, 0xc9, 0xd1, 0x5a, 0xf8, 0x88, 0xc4, 0xef, 0x6e, 0x3d, 0x74, 0x19, 0x74, 0xe7, 0xd8,
+        0xe0, 0x26, 0x44, 0x19, 0x86, 0xaf, 0x19, 0xdb, 0x05, 0xe9, 0x3b, 0x8b, 0x58, 0x58, 0xde,
+        0xe5, 0x4f, 0x48, 0x15, 0x01, 0xea, 0xe6, 0x83, 0x52, 0xd7, 0xc1, 0x21, 0xdf, 0xb9, 0xb8,
+        0x07, 0x66, 0x50, 0xfb, 0x3a, 0x0c, 0xb3, 0x85, 0xee, 0xbb, 0x04, 0x5f, 0xc2, 0x6d, 0x6d,
+        0x95, 0xfa, 0x11, 0x93, 0x1e, 0x59, 0x5b, 0xb1, 0x45, 0x8d, 0xe0, 0x3d, 0x73, 0xaa, 0xf2,
+        0x41, 0x14, 0x51, 0x07, 0x72, 0x3d, 0xa2, 0xf7, 0x58, 0xcd, 0x11, 0xa1, 0x32, 0xcf, 0xda,
+        0x42, 0xb7, 0xcc, 0x32, 0x80, 0xdb, 0x87, 0x82, 0xec, 0x42, 0xdb, 0x5a, 0x55, 0x24, 0x24,
+        0xa2, 0xd1, 0x55, 0x29, 0xad, 0xeb, 0x02, 0x15, 0x00, 0xeb, 0xea, 0x17, 0xd2, 0x09, 0xb3,
+        0xd7, 0x21, 0x9a, 0x21, 0x07, 0x82, 0x8f, 0xab, 0xfe, 0x88, 0x71, 0x68, 0xf7, 0xe3, 0x02,
+        0x81, 0x80, 0x19, 0x1c, 0x71, 0xfd, 0xe0, 0x03, 0x0c, 0x43, 0xd9, 0x0b, 0xf6, 0xcd, 0xd6,
+        0xa9, 0x70, 0xe7, 0x37, 0x86, 0x3a, 0x78, 0xe9, 0xa7, 0x47, 0xa7, 0x47, 0x06, 0x88, 0xb1,
+        0xaf, 0xd7, 0xf3, 0xf1, 0xa1, 0xd7, 0x00, 0x61, 0x28, 0x88, 0x31, 0x48, 0x60, 0xd8, 0x11,
+        0xef, 0xa5, 0x24, 0x1a, 0x81, 0xc4, 0x2a, 0xe2, 0xea, 0x0e, 0x36, 0xd2, 0xd2, 0x05, 0x84,
+        0x37, 0xcf, 0x32, 0x7d, 0x09, 0xe6, 0x0f, 0x8b, 0x0c, 0xc8, 0xc2, 0xa4, 0xb1, 0xdc, 0x80,
+        0xca, 0x68, 0xdf, 0xaf, 0xd2, 0x90, 0xc0, 0x37, 0x58, 0x54, 0x36, 0x8f, 0x49, 0xb8, 0x62,
+        0x75, 0x8b, 0x48, 0x47, 0xc0, 0xbe, 0xf7, 0x9a, 0x92, 0xa6, 0x68, 0x05, 0xda, 0x9d, 0xaf,
+        0x72, 0x9a, 0x67, 0xb3, 0xb4, 0x14, 0x03, 0xae, 0x4f, 0x4c, 0x76, 0xb9, 0xd8, 0x64, 0x0a,
+        0xba, 0x3b, 0xa8, 0x00, 0x60, 0x4d, 0xae, 0x81, 0xc3, 0xc5, 0x04, 0x17, 0x02, 0x15, 0x00,
+        0x81, 0x9d, 0xfd, 0x53, 0x0c, 0xc1, 0x8f, 0xbe, 0x8b, 0xea, 0x00, 0x26, 0x19, 0x29, 0x33,
+        0x91, 0x84, 0xbe, 0xad, 0x81};
+unsigned int dsa_privkey_pk8_der_len = 335;
+
+unsigned char ec_privkey_pk8_der[] = {
+        0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce,
+        0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x04,
+        0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20, 0x73, 0x7c, 0x2e, 0xcd, 0x7b, 0x8d,
+        0x19, 0x40, 0xbf, 0x29, 0x30, 0xaa, 0x9b, 0x4e, 0xd3, 0xff, 0x94, 0x1e, 0xed, 0x09,
+        0x36, 0x6b, 0xc0, 0x32, 0x99, 0x98, 0x64, 0x81, 0xf3, 0xa4, 0xd8, 0x59, 0xa1, 0x44,
+        0x03, 0x42, 0x00, 0x04, 0xbf, 0x85, 0xd7, 0x72, 0x0d, 0x07, 0xc2, 0x54, 0x61, 0x68,
+        0x3b, 0xc6, 0x48, 0xb4, 0x77, 0x8a, 0x9a, 0x14, 0xdd, 0x8a, 0x02, 0x4e, 0x3b, 0xdd,
+        0x8c, 0x7d, 0xdd, 0x9a, 0xb2, 0xb5, 0x28, 0xbb, 0xc7, 0xaa, 0x1b, 0x51, 0xf1, 0x4e,
+        0xbb, 0xbb, 0x0b, 0xd0, 0xce, 0x21, 0xbc, 0xc4, 0x1c, 0x6e, 0xb0, 0x00, 0x83, 0xcf,
+        0x33, 0x76, 0xd1, 0x1f, 0xd4, 0x49, 0x49, 0xe0, 0xb2, 0x18, 0x3b, 0xfe};
+unsigned int ec_privkey_pk8_der_len = 138;
+
+keymaster_key_param_t ec_params[] = {
+        keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_EC),
+        keymaster_param_long(KM_TAG_EC_CURVE, KM_EC_CURVE_P_521),
+        keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN),
+        keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_VERIFY),
+        keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+        keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED),
+};
+keymaster_key_param_set_t ec_param_set = {ec_params, sizeof(ec_params) / sizeof(*ec_params)};
+
+keymaster_key_param_t rsa_params[] = {
+        keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_RSA),
+        keymaster_param_int(KM_TAG_KEY_SIZE, 1024),
+        keymaster_param_long(KM_TAG_RSA_PUBLIC_EXPONENT, 65537),
+        keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN),
+        keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_VERIFY),
+        keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+        keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+        keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED),
+};
+keymaster_key_param_set_t rsa_param_set = {rsa_params, sizeof(rsa_params) / sizeof(*rsa_params)};
+
+struct EVP_PKEY_Delete {
+    void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
+};
+
+struct EVP_PKEY_CTX_Delete {
+    void operator()(EVP_PKEY_CTX* p) { EVP_PKEY_CTX_free(p); }
+};
+
+static bool do_operation(TrustyKeymasterDevice* device, keymaster_purpose_t purpose,
+                         keymaster_key_blob_t* key, keymaster_blob_t* input,
+                         keymaster_blob_t* signature, keymaster_blob_t* output) {
+    keymaster_key_param_t params[] = {
+            keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+            keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+    };
+    keymaster_key_param_set_t param_set = {params, sizeof(params) / sizeof(*params)};
+    keymaster_operation_handle_t op_handle;
+    keymaster_error_t error = device->begin(purpose, key, &param_set, nullptr, &op_handle);
+    if (error != KM_ERROR_OK) {
+        printf("Keymaster begin() failed: %d\n", error);
+        return false;
+    }
+    size_t input_consumed;
+    error = device->update(op_handle, nullptr, input, &input_consumed, nullptr, nullptr);
+    if (error != KM_ERROR_OK) {
+        printf("Keymaster update() failed: %d\n", error);
+        return false;
+    }
+    if (input_consumed != input->data_length) {
+        // This should never happen. If it does, it's a bug in the keymaster implementation.
+        printf("Keymaster update() did not consume all data.\n");
+        device->abort(op_handle);
+        return false;
+    }
+    error = device->finish(op_handle, nullptr, nullptr, signature, nullptr, output);
+    if (error != KM_ERROR_OK) {
+        printf("Keymaster finish() failed: %d\n", error);
+        return false;
+    }
+    return true;
+}
+
+static bool test_import_rsa(TrustyKeymasterDevice* device) {
+    printf("===================\n");
+    printf("= RSA Import Test =\n");
+    printf("===================\n\n");
+
+    printf("=== Importing RSA keypair === \n");
+    keymaster_key_blob_t key;
+    keymaster_blob_t private_key = {rsa_privkey_pk8_der, rsa_privkey_pk8_der_len};
+    int error =
+            device->import_key(&rsa_param_set, KM_KEY_FORMAT_PKCS8, &private_key, &key, nullptr);
+    if (error != KM_ERROR_OK) {
+        printf("Error importing RSA key: %d\n\n", error);
+        return false;
+    }
+    std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
+
+    printf("=== Signing with imported RSA key ===\n");
+    size_t message_len = 1024 / 8;
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
+    memset(message.get(), 'a', message_len);
+    keymaster_blob_t input = {message.get(), message_len}, signature;
+
+    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+        printf("Error signing data with imported RSA key\n\n");
+        return false;
+    }
+    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
+
+    printf("=== Verifying with imported RSA key === \n");
+    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+        printf("Error verifying data with imported RSA key\n\n");
+        return false;
+    }
+
+    printf("\n");
+    return true;
+}
+
+static bool test_rsa(TrustyKeymasterDevice* device) {
+    printf("============\n");
+    printf("= RSA Test =\n");
+    printf("============\n\n");
+
+    printf("=== Generating RSA key pair ===\n");
+    keymaster_key_blob_t key;
+    int error = device->generate_key(&rsa_param_set, &key, nullptr);
+    if (error != KM_ERROR_OK) {
+        printf("Error generating RSA key pair: %d\n\n", error);
+        return false;
+    }
+    std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
+
+    printf("=== Signing with RSA key === \n");
+    size_t message_len = 1024 / 8;
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
+    memset(message.get(), 'a', message_len);
+    keymaster_blob_t input = {message.get(), message_len}, signature;
+
+    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+        printf("Error signing data with RSA key\n\n");
+        return false;
+    }
+    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
+
+    printf("=== Verifying with RSA key === \n");
+    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+        printf("Error verifying data with RSA key\n\n");
+        return false;
+    }
+
+    printf("=== Exporting RSA public key ===\n");
+    keymaster_blob_t exported_key;
+    error = device->export_key(KM_KEY_FORMAT_X509, &key, nullptr, nullptr, &exported_key);
+    if (error != KM_ERROR_OK) {
+        printf("Error exporting RSA public key: %d\n\n", error);
+        return false;
+    }
+
+    printf("=== Verifying with exported key ===\n");
+    const uint8_t* tmp = exported_key.data;
+    std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
+            d2i_PUBKEY(NULL, &tmp, exported_key.data_length));
+    std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+    if (EVP_PKEY_verify_init(ctx.get()) != 1) {
+        printf("Error initializing openss EVP context\n\n");
+        return false;
+    }
+    if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) {
+        printf("Exported key was the wrong type?!?\n\n");
+        return false;
+    }
+
+    EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_NO_PADDING);
+    if (EVP_PKEY_verify(ctx.get(), signature.data, signature.data_length, message.get(),
+                        message_len) != 1) {
+        printf("Verification with exported pubkey failed.\n\n");
+        return false;
+    } else {
+        printf("Verification succeeded\n");
+    }
+
+    printf("\n");
+    return true;
+}
+
+static bool test_import_ecdsa(TrustyKeymasterDevice* device) {
+    printf("=====================\n");
+    printf("= ECDSA Import Test =\n");
+    printf("=====================\n\n");
+
+    printf("=== Importing ECDSA keypair === \n");
+    keymaster_key_blob_t key;
+    keymaster_blob_t private_key = {ec_privkey_pk8_der, ec_privkey_pk8_der_len};
+    int error = device->import_key(&ec_param_set, KM_KEY_FORMAT_PKCS8, &private_key, &key, nullptr);
+    if (error != KM_ERROR_OK) {
+        printf("Error importing ECDSA key: %d\n\n", error);
+        return false;
+    }
+    std::unique_ptr<const uint8_t[]> deleter(key.key_material);
+
+    printf("=== Signing with imported ECDSA key ===\n");
+    size_t message_len = 30 /* arbitrary */;
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
+    memset(message.get(), 'a', message_len);
+    keymaster_blob_t input = {message.get(), message_len}, signature;
+
+    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+        printf("Error signing data with imported ECDSA key\n\n");
+        return false;
+    }
+    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
+
+    printf("=== Verifying with imported ECDSA key === \n");
+    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+        printf("Error verifying data with imported ECDSA key\n\n");
+        return false;
+    }
+
+    printf("\n");
+    return true;
+}
+
+static bool test_ecdsa(TrustyKeymasterDevice* device) {
+    printf("==============\n");
+    printf("= ECDSA Test =\n");
+    printf("==============\n\n");
+
+    printf("=== Generating ECDSA key pair ===\n");
+    keymaster_key_blob_t key;
+    int error = device->generate_key(&ec_param_set, &key, nullptr);
+    if (error != KM_ERROR_OK) {
+        printf("Error generating ECDSA key pair: %d\n\n", error);
+        return false;
+    }
+    std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
+
+    printf("=== Signing with ECDSA key === \n");
+    size_t message_len = 30 /* arbitrary */;
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
+    memset(message.get(), 'a', message_len);
+    keymaster_blob_t input = {message.get(), message_len}, signature;
+
+    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+        printf("Error signing data with ECDSA key\n\n");
+        return false;
+    }
+    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
+
+    printf("=== Verifying with ECDSA key === \n");
+    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+        printf("Error verifying data with ECDSA key\n\n");
+        return false;
+    }
+
+    printf("=== Exporting ECDSA public key ===\n");
+    keymaster_blob_t exported_key;
+    error = device->export_key(KM_KEY_FORMAT_X509, &key, nullptr, nullptr, &exported_key);
+    if (error != KM_ERROR_OK) {
+        printf("Error exporting ECDSA public key: %d\n\n", error);
+        return false;
+    }
+
+    printf("=== Verifying with exported key ===\n");
+    const uint8_t* tmp = exported_key.data;
+    std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
+            d2i_PUBKEY(NULL, &tmp, exported_key.data_length));
+    std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+    if (EVP_PKEY_verify_init(ctx.get()) != 1) {
+        printf("Error initializing openssl EVP context\n\n");
+        return false;
+    }
+    if (EVP_PKEY_type(pkey->type) != EVP_PKEY_EC) {
+        printf("Exported key was the wrong type?!?\n\n");
+        return false;
+    }
+
+    if (EVP_PKEY_verify(ctx.get(), signature.data, signature.data_length, message.get(),
+                        message_len) != 1) {
+        printf("Verification with exported pubkey failed.\n\n");
+        return false;
+    } else {
+        printf("Verification succeeded\n");
+    }
+
+    printf("\n");
+    return true;
+}
+
+int main(void) {
+    TrustyKeymasterDevice device(NULL);
+    keymaster::ConfigureDevice(reinterpret_cast<keymaster2_device_t*>(&device));
+    if (device.session_error() != KM_ERROR_OK) {
+        printf("Failed to initialize Trusty session: %d\n", device.session_error());
+        return 1;
+    }
+    printf("Trusty session initialized\n");
+
+    bool success = true;
+    success &= test_rsa(&device);
+    success &= test_import_rsa(&device);
+    success &= test_ecdsa(&device);
+    success &= test_import_ecdsa(&device);
+
+    if (success) {
+        printf("\nTESTS PASSED!\n");
+    } else {
+        printf("\n!!!!TESTS FAILED!!!\n");
+    }
+
+    return success ? 0 : 1;
+}
diff --git a/trusty/keymaster/module.cpp b/trusty/keymaster/module.cpp
deleted file mode 100644
index b472680..0000000
--- a/trusty/keymaster/module.cpp
+++ /dev/null
@@ -1,61 +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 <string.h>
-
-#include <hardware/hardware.h>
-#include <hardware/keymaster0.h>
-
-#include "trusty_keymaster_device.h"
-
-using keymaster::TrustyKeymasterDevice;
-
-/*
- * Generic device handling
- */
-static int trusty_keymaster_open(const hw_module_t* module, const char* name, hw_device_t** device) {
-    if (strcmp(name, KEYSTORE_KEYMASTER) != 0) {
-        return -EINVAL;
-    }
-
-    TrustyKeymasterDevice* dev = new TrustyKeymasterDevice(module);
-    if (dev == NULL) {
-        return -ENOMEM;
-    }
-    *device = dev->hw_device();
-    // Do not delete dev; it will get cleaned up when the caller calls device->close(), and must
-    // exist until then.
-    return 0;
-}
-
-static struct hw_module_methods_t keystore_module_methods = {
-    .open = trusty_keymaster_open,
-};
-
-struct keystore_module HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
-    .common =
-        {
-            .tag = HARDWARE_MODULE_TAG,
-            .module_api_version = KEYMASTER_MODULE_API_VERSION_2_0,
-            .hal_api_version = HARDWARE_HAL_API_VERSION,
-            .id = KEYSTORE_HARDWARE_MODULE_ID,
-            .name = "Trusty Keymaster HAL",
-            .author = "The Android Open Source Project",
-            .methods = &keystore_module_methods,
-            .dso = 0,
-            .reserved = {},
-        },
-};
diff --git a/trusty/keymaster/trusty_keymaster_device.cpp b/trusty/keymaster/trusty_keymaster_device.cpp
deleted file mode 100644
index b8c2032..0000000
--- a/trusty/keymaster/trusty_keymaster_device.cpp
+++ /dev/null
@@ -1,796 +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 "TrustyKeymaster"
-
-#include <assert.h>
-#include <errno.h>
-#include <openssl/evp.h>
-#include <openssl/x509.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#include <algorithm>
-#include <type_traits>
-
-#include <hardware/keymaster2.h>
-#include <keymaster/authorization_set.h>
-#include <log/log.h>
-
-#include "keymaster_ipc.h"
-#include "trusty_keymaster_device.h"
-#include "trusty_keymaster_ipc.h"
-
-// 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;
-const size_t kMaximumFinishInputLength = 2048;
-
-namespace keymaster {
-
-static keymaster_error_t translate_error(int err) {
-    switch (err) {
-        case 0:
-            return KM_ERROR_OK;
-        case -EPERM:
-        case -EACCES:
-            return KM_ERROR_SECURE_HW_ACCESS_DENIED;
-
-        case -ECANCELED:
-            return KM_ERROR_OPERATION_CANCELLED;
-
-        case -ENODEV:
-            return KM_ERROR_UNIMPLEMENTED;
-
-        case -ENOMEM:
-            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-
-        case -EBUSY:
-            return KM_ERROR_SECURE_HW_BUSY;
-
-        case -EIO:
-            return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
-
-        case -EOVERFLOW:
-            return KM_ERROR_INVALID_INPUT_LENGTH;
-
-        default:
-            return KM_ERROR_UNKNOWN_ERROR;
-    }
-}
-
-TrustyKeymasterDevice::TrustyKeymasterDevice(const hw_module_t* module) {
-    static_assert(std::is_standard_layout<TrustyKeymasterDevice>::value,
-                  "TrustyKeymasterDevice must be standard layout");
-    static_assert(offsetof(TrustyKeymasterDevice, device_) == 0,
-                  "device_ must be the first member of TrustyKeymasterDevice");
-    static_assert(offsetof(TrustyKeymasterDevice, device_.common) == 0,
-                  "common must be the first member of keymaster2_device");
-
-    ALOGI("Creating device");
-    ALOGD("Device address: %p", this);
-
-    device_ = {};
-
-    device_.common.tag = HARDWARE_DEVICE_TAG;
-    device_.common.version = 1;
-    device_.common.module = const_cast<hw_module_t*>(module);
-    device_.common.close = close_device;
-
-    device_.flags = KEYMASTER_SUPPORTS_EC;
-
-    device_.configure = configure;
-    device_.add_rng_entropy = add_rng_entropy;
-    device_.generate_key = generate_key;
-    device_.get_key_characteristics = get_key_characteristics;
-    device_.import_key = import_key;
-    device_.export_key = export_key;
-    device_.attest_key = attest_key;
-    device_.upgrade_key = upgrade_key;
-    device_.delete_key = nullptr;
-    device_.delete_all_keys = nullptr;
-    device_.begin = begin;
-    device_.update = update;
-    device_.finish = finish;
-    device_.abort = abort;
-
-    int rc = trusty_keymaster_connect();
-    error_ = translate_error(rc);
-    if (rc < 0) {
-        ALOGE("failed to connect to keymaster (%d)", rc);
-        return;
-    }
-
-    GetVersionRequest version_request;
-    GetVersionResponse version_response;
-    error_ = Send(KM_GET_VERSION, version_request, &version_response);
-    if (error_ == KM_ERROR_INVALID_ARGUMENT || error_ == KM_ERROR_UNIMPLEMENTED) {
-        ALOGE("\"Bad parameters\" error on GetVersion call.  Version 0 is not supported.");
-        error_ = KM_ERROR_VERSION_MISMATCH;
-        return;
-    }
-    message_version_ = MessageVersion(version_response.major_ver, version_response.minor_ver,
-                                      version_response.subminor_ver);
-    if (message_version_ < 0) {
-        // Can't translate version?  Keymaster implementation must be newer.
-        ALOGE("Keymaster version %d.%d.%d not supported.", version_response.major_ver,
-              version_response.minor_ver, version_response.subminor_ver);
-        error_ = KM_ERROR_VERSION_MISMATCH;
-    }
-}
-
-TrustyKeymasterDevice::~TrustyKeymasterDevice() {
-    trusty_keymaster_disconnect();
-}
-
-namespace {
-
-// Allocates a new buffer with malloc and copies the contents of |buffer| to it. Caller takes
-// ownership of the returned buffer.
-uint8_t* DuplicateBuffer(const uint8_t* buffer, size_t size) {
-    uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(size));
-    if (tmp) {
-        memcpy(tmp, buffer, size);
-    }
-    return tmp;
-}
-
-template <typename RequestType>
-void AddClientAndAppData(const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
-                         RequestType* request) {
-    request->additional_params.Clear();
-    if (client_id) {
-        request->additional_params.push_back(TAG_APPLICATION_ID, *client_id);
-    }
-    if (app_data) {
-        request->additional_params.push_back(TAG_APPLICATION_DATA, *app_data);
-    }
-}
-
-}  //  unnamed namespace
-
-keymaster_error_t TrustyKeymasterDevice::configure(const keymaster_key_param_set_t* params) {
-    ALOGD("Device received configure\n");
-
-    if (error_ != KM_ERROR_OK) {
-        return error_;
-    }
-    if (!params) {
-        return KM_ERROR_UNEXPECTED_NULL_POINTER;
-    }
-
-    AuthorizationSet params_copy(*params);
-    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(message_version_);
-    keymaster_error_t err = Send(KM_CONFIGURE, request, &response);
-    if (err != KM_ERROR_OK) {
-        return err;
-    }
-
-    return KM_ERROR_OK;
-}
-
-keymaster_error_t TrustyKeymasterDevice::add_rng_entropy(const uint8_t* data, size_t data_length) {
-    ALOGD("Device received add_rng_entropy");
-
-    if (error_ != KM_ERROR_OK) {
-        return error_;
-    }
-
-    AddEntropyRequest request(message_version_);
-    request.random_data.Reinitialize(data, data_length);
-    AddEntropyResponse response(message_version_);
-    return Send(KM_ADD_RNG_ENTROPY, request, &response);
-}
-
-keymaster_error_t TrustyKeymasterDevice::generate_key(
-    const keymaster_key_param_set_t* params, keymaster_key_blob_t* key_blob,
-    keymaster_key_characteristics_t* characteristics) {
-    ALOGD("Device received generate_key");
-
-    if (error_ != KM_ERROR_OK) {
-        return error_;
-    }
-    if (!params) {
-        return KM_ERROR_UNEXPECTED_NULL_POINTER;
-    }
-    if (!key_blob) {
-        return KM_ERROR_OUTPUT_PARAMETER_NULL;
-    }
-
-    GenerateKeyRequest request(message_version_);
-    request.key_description.Reinitialize(*params);
-    request.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
-
-    GenerateKeyResponse response(message_version_);
-    keymaster_error_t err = Send(KM_GENERATE_KEY, request, &response);
-    if (err != KM_ERROR_OK) {
-        return err;
-    }
-
-    key_blob->key_material_size = response.key_blob.key_material_size;
-    key_blob->key_material =
-        DuplicateBuffer(response.key_blob.key_material, response.key_blob.key_material_size);
-    if (!key_blob->key_material) {
-        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-    }
-
-    if (characteristics) {
-        response.enforced.CopyToParamSet(&characteristics->hw_enforced);
-        response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
-    }
-
-    return KM_ERROR_OK;
-}
-
-keymaster_error_t TrustyKeymasterDevice::get_key_characteristics(
-    const keymaster_key_blob_t* key_blob, const keymaster_blob_t* client_id,
-    const keymaster_blob_t* app_data, keymaster_key_characteristics_t* characteristics) {
-    ALOGD("Device received get_key_characteristics");
-
-    if (error_ != KM_ERROR_OK) {
-        return error_;
-    }
-    if (!key_blob || !key_blob->key_material) {
-        return KM_ERROR_UNEXPECTED_NULL_POINTER;
-    }
-    if (!characteristics) {
-        return KM_ERROR_OUTPUT_PARAMETER_NULL;
-    }
-
-    GetKeyCharacteristicsRequest request(message_version_);
-    request.SetKeyMaterial(*key_blob);
-    AddClientAndAppData(client_id, app_data, &request);
-
-    GetKeyCharacteristicsResponse response(message_version_);
-    keymaster_error_t err = Send(KM_GET_KEY_CHARACTERISTICS, request, &response);
-    if (err != KM_ERROR_OK) {
-        return err;
-    }
-
-    response.enforced.CopyToParamSet(&characteristics->hw_enforced);
-    response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
-
-    return KM_ERROR_OK;
-}
-
-keymaster_error_t TrustyKeymasterDevice::import_key(
-    const keymaster_key_param_set_t* params, keymaster_key_format_t key_format,
-    const keymaster_blob_t* key_data, keymaster_key_blob_t* key_blob,
-    keymaster_key_characteristics_t* characteristics) {
-    ALOGD("Device received import_key");
-
-    if (error_ != KM_ERROR_OK) {
-        return error_;
-    }
-    if (!params || !key_data) {
-        return KM_ERROR_UNEXPECTED_NULL_POINTER;
-    }
-    if (!key_blob) {
-        return KM_ERROR_OUTPUT_PARAMETER_NULL;
-    }
-
-    ImportKeyRequest request(message_version_);
-    request.key_description.Reinitialize(*params);
-    request.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
-
-    request.key_format = key_format;
-    request.SetKeyMaterial(key_data->data, key_data->data_length);
-
-    ImportKeyResponse response(message_version_);
-    keymaster_error_t err = Send(KM_IMPORT_KEY, request, &response);
-    if (err != KM_ERROR_OK) {
-        return err;
-    }
-
-    key_blob->key_material_size = response.key_blob.key_material_size;
-    key_blob->key_material =
-        DuplicateBuffer(response.key_blob.key_material, response.key_blob.key_material_size);
-    if (!key_blob->key_material) {
-        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-    }
-
-    if (characteristics) {
-        response.enforced.CopyToParamSet(&characteristics->hw_enforced);
-        response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
-    }
-
-    return KM_ERROR_OK;
-}
-
-keymaster_error_t TrustyKeymasterDevice::export_key(keymaster_key_format_t export_format,
-                                                    const keymaster_key_blob_t* key_to_export,
-                                                    const keymaster_blob_t* client_id,
-                                                    const keymaster_blob_t* app_data,
-                                                    keymaster_blob_t* export_data) {
-    ALOGD("Device received export_key");
-
-    if (error_ != KM_ERROR_OK) {
-        return error_;
-    }
-    if (!key_to_export || !key_to_export->key_material) {
-        return KM_ERROR_UNEXPECTED_NULL_POINTER;
-    }
-    if (!export_data) {
-        return KM_ERROR_OUTPUT_PARAMETER_NULL;
-    }
-
-    export_data->data = nullptr;
-    export_data->data_length = 0;
-
-    ExportKeyRequest request(message_version_);
-    request.key_format = export_format;
-    request.SetKeyMaterial(*key_to_export);
-    AddClientAndAppData(client_id, app_data, &request);
-
-    ExportKeyResponse response(message_version_);
-    keymaster_error_t err = Send(KM_EXPORT_KEY, request, &response);
-    if (err != KM_ERROR_OK) {
-        return err;
-    }
-
-    export_data->data_length = response.key_data_length;
-    export_data->data = DuplicateBuffer(response.key_data, response.key_data_length);
-    if (!export_data->data) {
-        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-    }
-
-    return KM_ERROR_OK;
-}
-
-keymaster_error_t TrustyKeymasterDevice::attest_key(const keymaster_key_blob_t* key_to_attest,
-                                                    const keymaster_key_param_set_t* attest_params,
-                                                    keymaster_cert_chain_t* cert_chain) {
-    ALOGD("Device received attest_key");
-
-    if (error_ != KM_ERROR_OK) {
-        return error_;
-    }
-    if (!key_to_attest || !attest_params) {
-        return KM_ERROR_UNEXPECTED_NULL_POINTER;
-    }
-    if (!cert_chain) {
-        return KM_ERROR_OUTPUT_PARAMETER_NULL;
-    }
-
-    cert_chain->entry_count = 0;
-    cert_chain->entries = nullptr;
-
-    AttestKeyRequest request(message_version_);
-    request.SetKeyMaterial(*key_to_attest);
-    request.attest_params.Reinitialize(*attest_params);
-
-    keymaster_blob_t attestation_challenge = {};
-    request.attest_params.GetTagValue(TAG_ATTESTATION_CHALLENGE, &attestation_challenge);
-    if (attestation_challenge.data_length > kMaximumAttestationChallengeLength) {
-        ALOGE("%zu-byte attestation challenge; only %zu bytes allowed",
-              attestation_challenge.data_length, kMaximumAttestationChallengeLength);
-        return KM_ERROR_INVALID_INPUT_LENGTH;
-    }
-
-    AttestKeyResponse response(message_version_);
-    keymaster_error_t err = Send(KM_ATTEST_KEY, request, &response);
-    if (err != KM_ERROR_OK) {
-        return err;
-    }
-
-    // Allocate and clear storage for cert_chain.
-    keymaster_cert_chain_t& rsp_chain = response.certificate_chain;
-    cert_chain->entries = reinterpret_cast<keymaster_blob_t*>(
-        malloc(rsp_chain.entry_count * sizeof(*cert_chain->entries)));
-    if (!cert_chain->entries) {
-        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-    }
-    cert_chain->entry_count = rsp_chain.entry_count;
-    for (keymaster_blob_t& entry : array_range(cert_chain->entries, cert_chain->entry_count)) {
-        entry = {};
-    }
-
-    // Copy cert_chain contents
-    size_t i = 0;
-    for (keymaster_blob_t& entry : array_range(rsp_chain.entries, rsp_chain.entry_count)) {
-        cert_chain->entries[i].data = DuplicateBuffer(entry.data, entry.data_length);
-        if (!cert_chain->entries[i].data) {
-            keymaster_free_cert_chain(cert_chain);
-            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-        }
-        cert_chain->entries[i].data_length = entry.data_length;
-        ++i;
-    }
-
-    return KM_ERROR_OK;
-}
-
-keymaster_error_t TrustyKeymasterDevice::upgrade_key(const keymaster_key_blob_t* key_to_upgrade,
-                                                     const keymaster_key_param_set_t* upgrade_params,
-                                                     keymaster_key_blob_t* upgraded_key) {
-    ALOGD("Device received upgrade_key");
-
-    if (error_ != KM_ERROR_OK) {
-        return error_;
-    }
-    if (!key_to_upgrade || !upgrade_params) {
-        return KM_ERROR_UNEXPECTED_NULL_POINTER;
-    }
-    if (!upgraded_key) {
-        return KM_ERROR_OUTPUT_PARAMETER_NULL;
-    }
-
-    UpgradeKeyRequest request(message_version_);
-    request.SetKeyMaterial(*key_to_upgrade);
-    request.upgrade_params.Reinitialize(*upgrade_params);
-
-    UpgradeKeyResponse response(message_version_);
-    keymaster_error_t err = Send(KM_UPGRADE_KEY, request, &response);
-    if (err != KM_ERROR_OK) {
-        return err;
-    }
-
-    upgraded_key->key_material_size = response.upgraded_key.key_material_size;
-    upgraded_key->key_material = DuplicateBuffer(response.upgraded_key.key_material,
-                                                 response.upgraded_key.key_material_size);
-    if (!upgraded_key->key_material) {
-        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-    }
-
-    return KM_ERROR_OK;
-}
-
-keymaster_error_t TrustyKeymasterDevice::begin(keymaster_purpose_t purpose,
-                                               const keymaster_key_blob_t* key,
-                                               const keymaster_key_param_set_t* in_params,
-                                               keymaster_key_param_set_t* out_params,
-                                               keymaster_operation_handle_t* operation_handle) {
-    ALOGD("Device received begin");
-
-    if (error_ != KM_ERROR_OK) {
-        return error_;
-    }
-    if (!key || !key->key_material) {
-        return KM_ERROR_UNEXPECTED_NULL_POINTER;
-    }
-    if (!operation_handle) {
-        return KM_ERROR_OUTPUT_PARAMETER_NULL;
-    }
-
-    if (out_params) {
-        *out_params = {};
-    }
-
-    BeginOperationRequest request(message_version_);
-    request.purpose = purpose;
-    request.SetKeyMaterial(*key);
-    request.additional_params.Reinitialize(*in_params);
-
-    BeginOperationResponse response(message_version_);
-    keymaster_error_t err = Send(KM_BEGIN_OPERATION, request, &response);
-    if (err != KM_ERROR_OK) {
-        return err;
-    }
-
-    if (response.output_params.size() > 0) {
-        if (out_params) {
-            response.output_params.CopyToParamSet(out_params);
-        } else {
-            return KM_ERROR_OUTPUT_PARAMETER_NULL;
-        }
-    }
-    *operation_handle = response.op_handle;
-
-    return KM_ERROR_OK;
-}
-
-keymaster_error_t TrustyKeymasterDevice::update(keymaster_operation_handle_t operation_handle,
-                                                const keymaster_key_param_set_t* in_params,
-                                                const keymaster_blob_t* input,
-                                                size_t* input_consumed,
-                                                keymaster_key_param_set_t* out_params,
-                                                keymaster_blob_t* output) {
-    ALOGD("Device received update");
-
-    if (error_ != KM_ERROR_OK) {
-        return error_;
-    }
-    if (!input) {
-        return KM_ERROR_UNEXPECTED_NULL_POINTER;
-    }
-    if (!input_consumed) {
-        return KM_ERROR_OUTPUT_PARAMETER_NULL;
-    }
-
-    if (out_params) {
-        *out_params = {};
-    }
-    if (output) {
-        *output = {};
-    }
-
-    UpdateOperationRequest request(message_version_);
-    request.op_handle = operation_handle;
-    if (in_params) {
-        request.additional_params.Reinitialize(*in_params);
-    }
-    if (input && input->data_length > 0) {
-        size_t max_input_size = SEND_BUF_SIZE - request.SerializedSize();
-        request.input.Reinitialize(input->data, std::min(input->data_length, max_input_size));
-    }
-
-    UpdateOperationResponse response(message_version_);
-    keymaster_error_t err = Send(KM_UPDATE_OPERATION, request, &response);
-    if (err != KM_ERROR_OK) {
-        return err;
-    }
-
-    if (response.output_params.size() > 0) {
-        if (out_params) {
-            response.output_params.CopyToParamSet(out_params);
-        } else {
-            return KM_ERROR_OUTPUT_PARAMETER_NULL;
-        }
-    }
-    *input_consumed = response.input_consumed;
-    if (output) {
-        output->data_length = response.output.available_read();
-        output->data = DuplicateBuffer(response.output.peek_read(), output->data_length);
-        if (!output->data) {
-            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-        }
-    } else if (response.output.available_read() > 0) {
-        return KM_ERROR_OUTPUT_PARAMETER_NULL;
-    }
-
-    return KM_ERROR_OK;
-}
-
-keymaster_error_t TrustyKeymasterDevice::finish(keymaster_operation_handle_t operation_handle,
-                                                const keymaster_key_param_set_t* in_params,
-                                                const keymaster_blob_t* input,
-                                                const keymaster_blob_t* signature,
-                                                keymaster_key_param_set_t* out_params,
-                                                keymaster_blob_t* output) {
-    ALOGD("Device received finish");
-
-    if (error_ != KM_ERROR_OK) {
-        return error_;
-    }
-    if (input && input->data_length > kMaximumFinishInputLength) {
-        ALOGE("%zu-byte input to finish; only %zu bytes allowed",
-              input->data_length, kMaximumFinishInputLength);
-        return KM_ERROR_INVALID_INPUT_LENGTH;
-    }
-
-    if (out_params) {
-        *out_params = {};
-    }
-    if (output) {
-        *output = {};
-    }
-
-    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);
-    }
-    if (input && input->data && input->data_length) {
-        request.input.Reinitialize(input->data, input->data_length);
-    }
-    if (in_params) {
-        request.additional_params.Reinitialize(*in_params);
-    }
-
-    FinishOperationResponse response(message_version_);
-    keymaster_error_t err = Send(KM_FINISH_OPERATION, request, &response);
-    if (err != KM_ERROR_OK) {
-        return err;
-    }
-
-    if (response.output_params.size() > 0) {
-        if (out_params) {
-            response.output_params.CopyToParamSet(out_params);
-        } else {
-            return KM_ERROR_OUTPUT_PARAMETER_NULL;
-        }
-    }
-    if (output) {
-        output->data_length = response.output.available_read();
-        output->data = DuplicateBuffer(response.output.peek_read(), output->data_length);
-        if (!output->data) {
-            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-        }
-    } else if (response.output.available_read() > 0) {
-        return KM_ERROR_OUTPUT_PARAMETER_NULL;
-    }
-
-    return KM_ERROR_OK;
-}
-
-keymaster_error_t TrustyKeymasterDevice::abort(keymaster_operation_handle_t operation_handle) {
-    ALOGD("Device received abort");
-
-    if (error_ != KM_ERROR_OK) {
-        return error_;
-    }
-
-    AbortOperationRequest request(message_version_);
-    request.op_handle = operation_handle;
-    AbortOperationResponse response(message_version_);
-    return Send(KM_ABORT_OPERATION, request, &response);
-}
-
-hw_device_t* TrustyKeymasterDevice::hw_device() {
-    return &device_.common;
-}
-
-static inline TrustyKeymasterDevice* convert_device(const keymaster2_device_t* dev) {
-    return reinterpret_cast<TrustyKeymasterDevice*>(const_cast<keymaster2_device_t*>(dev));
-}
-
-/* static */
-int TrustyKeymasterDevice::close_device(hw_device_t* dev) {
-    delete reinterpret_cast<TrustyKeymasterDevice*>(dev);
-    return 0;
-}
-
-/* static */
-keymaster_error_t TrustyKeymasterDevice::configure(const keymaster2_device_t* dev,
-                                                   const keymaster_key_param_set_t* params) {
-    return convert_device(dev)->configure(params);
-}
-
-/* static */
-keymaster_error_t TrustyKeymasterDevice::add_rng_entropy(const keymaster2_device_t* dev,
-                                                         const uint8_t* data, size_t data_length) {
-    return convert_device(dev)->add_rng_entropy(data, data_length);
-}
-
-/* static */
-keymaster_error_t TrustyKeymasterDevice::generate_key(
-    const keymaster2_device_t* dev, const keymaster_key_param_set_t* params,
-    keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) {
-    return convert_device(dev)->generate_key(params, key_blob, characteristics);
-}
-
-/* static */
-keymaster_error_t TrustyKeymasterDevice::get_key_characteristics(
-    const keymaster2_device_t* dev, const keymaster_key_blob_t* key_blob,
-    const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
-    keymaster_key_characteristics_t* characteristics) {
-    return convert_device(dev)->get_key_characteristics(key_blob, client_id, app_data,
-                                                        characteristics);
-}
-
-/* static */
-keymaster_error_t TrustyKeymasterDevice::import_key(
-    const keymaster2_device_t* dev, const keymaster_key_param_set_t* params,
-    keymaster_key_format_t key_format, const keymaster_blob_t* key_data,
-    keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) {
-    return convert_device(dev)->import_key(params, key_format, key_data, key_blob, characteristics);
-}
-
-/* static */
-keymaster_error_t TrustyKeymasterDevice::export_key(const keymaster2_device_t* dev,
-                                                    keymaster_key_format_t export_format,
-                                                    const keymaster_key_blob_t* key_to_export,
-                                                    const keymaster_blob_t* client_id,
-                                                    const keymaster_blob_t* app_data,
-                                                    keymaster_blob_t* export_data) {
-    return convert_device(dev)->export_key(export_format, key_to_export, client_id, app_data,
-                                           export_data);
-}
-
-/* static */
-keymaster_error_t TrustyKeymasterDevice::attest_key(const keymaster2_device_t* dev,
-                                                    const keymaster_key_blob_t* key_to_attest,
-                                                    const keymaster_key_param_set_t* attest_params,
-                                                    keymaster_cert_chain_t* cert_chain) {
-    return convert_device(dev)->attest_key(key_to_attest, attest_params, cert_chain);
-}
-
-/* static */
-keymaster_error_t TrustyKeymasterDevice::upgrade_key(const keymaster2_device_t* dev,
-                                                     const keymaster_key_blob_t* key_to_upgrade,
-                                                     const keymaster_key_param_set_t* upgrade_params,
-                                                     keymaster_key_blob_t* upgraded_key) {
-    return convert_device(dev)->upgrade_key(key_to_upgrade, upgrade_params, upgraded_key);
-}
-
-/* static */
-keymaster_error_t TrustyKeymasterDevice::begin(const keymaster2_device_t* dev,
-                                               keymaster_purpose_t purpose,
-                                               const keymaster_key_blob_t* key,
-                                               const keymaster_key_param_set_t* in_params,
-                                               keymaster_key_param_set_t* out_params,
-                                               keymaster_operation_handle_t* operation_handle) {
-    return convert_device(dev)->begin(purpose, key, in_params, out_params, operation_handle);
-}
-
-/* static */
-keymaster_error_t TrustyKeymasterDevice::update(
-    const keymaster2_device_t* dev, keymaster_operation_handle_t operation_handle,
-    const keymaster_key_param_set_t* in_params, const keymaster_blob_t* input,
-    size_t* input_consumed, keymaster_key_param_set_t* out_params, keymaster_blob_t* output) {
-    return convert_device(dev)->update(operation_handle, in_params, input, input_consumed,
-                                       out_params, output);
-}
-
-/* static */
-keymaster_error_t TrustyKeymasterDevice::finish(const keymaster2_device_t* dev,
-                                                keymaster_operation_handle_t operation_handle,
-                                                const keymaster_key_param_set_t* in_params,
-                                                const keymaster_blob_t* input,
-                                                const keymaster_blob_t* signature,
-                                                keymaster_key_param_set_t* out_params,
-                                                keymaster_blob_t* output) {
-    return convert_device(dev)->finish(operation_handle, in_params, input, signature, out_params,
-                                       output);
-}
-
-/* static */
-keymaster_error_t TrustyKeymasterDevice::abort(const keymaster2_device_t* dev,
-                                               keymaster_operation_handle_t operation_handle) {
-    return convert_device(dev)->abort(operation_handle);
-}
-
-keymaster_error_t TrustyKeymasterDevice::Send(uint32_t command, const Serializable& req,
-                                              KeymasterResponse* rsp) {
-    uint32_t req_size = req.SerializedSize();
-    if (req_size > SEND_BUF_SIZE) {
-        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-    }
-    uint8_t send_buf[SEND_BUF_SIZE];
-    Eraser send_buf_eraser(send_buf, SEND_BUF_SIZE);
-    req.Serialize(send_buf, send_buf + req_size);
-
-    // Send it
-    uint8_t recv_buf[RECV_BUF_SIZE];
-    Eraser recv_buf_eraser(recv_buf, RECV_BUF_SIZE);
-    uint32_t rsp_size = RECV_BUF_SIZE;
-    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);
-    } else {
-        ALOGV("Received %d byte response\n", rsp_size);
-    }
-
-    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;
-    } else if (rsp->error != KM_ERROR_OK) {
-        ALOGE("Response of size %d contained error code %d\n", (int)rsp_size, (int)rsp->error);
-        return rsp->error;
-    }
-    return rsp->error;
-}
-
-}  // namespace keymaster
diff --git a/trusty/keymaster/trusty_keymaster_device.h b/trusty/keymaster/trusty_keymaster_device.h
deleted file mode 100644
index cfada1b..0000000
--- a/trusty/keymaster/trusty_keymaster_device.h
+++ /dev/null
@@ -1,153 +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.
- */
-
-#ifndef TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
-#define TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
-
-#include <hardware/keymaster2.h>
-#include <keymaster/android_keymaster_messages.h>
-
-namespace keymaster {
-
-/**
- * Trusty Keymaster device.
- *
- * IMPORTANT MAINTAINER NOTE: Pointers to instances of this class must be castable to hw_device_t
- * and keymaster_device. This means it must remain a standard layout class (no virtual functions and
- * no data members which aren't standard layout), and device_ must be the first data member.
- * Assertions in the constructor validate compliance with those constraints.
- */
-class TrustyKeymasterDevice {
-  public:
-    /*
-     * These are the only symbols that will be exported by libtrustykeymaster.  All functionality
-     * can be reached via the function pointers in device_.
-     */
-    __attribute__((visibility("default"))) explicit TrustyKeymasterDevice(const hw_module_t* module);
-    __attribute__((visibility("default"))) hw_device_t* hw_device();
-
-    ~TrustyKeymasterDevice();
-
-    keymaster_error_t session_error() { return error_; }
-
-    keymaster_error_t configure(const keymaster_key_param_set_t* params);
-    keymaster_error_t add_rng_entropy(const uint8_t* data, size_t data_length);
-    keymaster_error_t generate_key(const keymaster_key_param_set_t* params,
-                                   keymaster_key_blob_t* key_blob,
-                                   keymaster_key_characteristics_t* characteristics);
-    keymaster_error_t get_key_characteristics(const keymaster_key_blob_t* key_blob,
-                                              const keymaster_blob_t* client_id,
-                                              const keymaster_blob_t* app_data,
-                                              keymaster_key_characteristics_t* character);
-    keymaster_error_t import_key(const keymaster_key_param_set_t* params,
-                                 keymaster_key_format_t key_format,
-                                 const keymaster_blob_t* key_data, keymaster_key_blob_t* key_blob,
-                                 keymaster_key_characteristics_t* characteristics);
-    keymaster_error_t export_key(keymaster_key_format_t export_format,
-                                 const keymaster_key_blob_t* key_to_export,
-                                 const keymaster_blob_t* client_id,
-                                 const keymaster_blob_t* app_data, keymaster_blob_t* export_data);
-    keymaster_error_t attest_key(const keymaster_key_blob_t* key_to_attest,
-                                 const keymaster_key_param_set_t* attest_params,
-                                 keymaster_cert_chain_t* cert_chain);
-    keymaster_error_t upgrade_key(const keymaster_key_blob_t* key_to_upgrade,
-                                  const keymaster_key_param_set_t* upgrade_params,
-                                  keymaster_key_blob_t* upgraded_key);
-    keymaster_error_t begin(keymaster_purpose_t purpose, const keymaster_key_blob_t* key,
-                            const keymaster_key_param_set_t* in_params,
-                            keymaster_key_param_set_t* out_params,
-                            keymaster_operation_handle_t* operation_handle);
-    keymaster_error_t update(keymaster_operation_handle_t operation_handle,
-                             const keymaster_key_param_set_t* in_params,
-                             const keymaster_blob_t* input, size_t* input_consumed,
-                             keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
-    keymaster_error_t finish(keymaster_operation_handle_t operation_handle,
-                             const keymaster_key_param_set_t* in_params,
-                             const keymaster_blob_t* input, const keymaster_blob_t* signature,
-                             keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
-    keymaster_error_t abort(keymaster_operation_handle_t operation_handle);
-
-  private:
-    keymaster_error_t Send(uint32_t command, const Serializable& request,
-                           KeymasterResponse* response);
-
-    /*
-     * These static methods are the functions referenced through the function pointers in
-     * keymaster_device.  They're all trivial wrappers.
-     */
-    static int close_device(hw_device_t* dev);
-    static keymaster_error_t configure(const keymaster2_device_t* dev,
-                                       const keymaster_key_param_set_t* params);
-    static keymaster_error_t add_rng_entropy(const keymaster2_device_t* dev, const uint8_t* data,
-                                             size_t data_length);
-    static keymaster_error_t generate_key(const keymaster2_device_t* dev,
-                                          const keymaster_key_param_set_t* params,
-                                          keymaster_key_blob_t* key_blob,
-                                          keymaster_key_characteristics_t* characteristics);
-    static keymaster_error_t get_key_characteristics(const keymaster2_device_t* dev,
-                                                     const keymaster_key_blob_t* key_blob,
-                                                     const keymaster_blob_t* client_id,
-                                                     const keymaster_blob_t* app_data,
-                                                     keymaster_key_characteristics_t* character);
-    static keymaster_error_t import_key(const keymaster2_device_t* dev,
-                                        const keymaster_key_param_set_t* params,
-                                        keymaster_key_format_t key_format,
-                                        const keymaster_blob_t* key_data,
-                                        keymaster_key_blob_t* key_blob,
-                                        keymaster_key_characteristics_t* characteristics);
-    static keymaster_error_t export_key(const keymaster2_device_t* dev,
-                                        keymaster_key_format_t export_format,
-                                        const keymaster_key_blob_t* key_to_export,
-                                        const keymaster_blob_t* client_id,
-                                        const keymaster_blob_t* app_data,
-                                        keymaster_blob_t* export_data);
-    static keymaster_error_t attest_key(const keymaster2_device_t* dev,
-                                        const keymaster_key_blob_t* key_to_attest,
-                                        const keymaster_key_param_set_t* attest_params,
-                                        keymaster_cert_chain_t* cert_chain);
-    static keymaster_error_t upgrade_key(const keymaster2_device_t* dev,
-                                         const keymaster_key_blob_t* key_to_upgrade,
-                                         const keymaster_key_param_set_t* upgrade_params,
-                                         keymaster_key_blob_t* upgraded_key);
-    static keymaster_error_t delete_key(const keymaster2_device_t* dev,
-                                        const keymaster_key_blob_t* key);
-    static keymaster_error_t delete_all_keys(const keymaster2_device_t* dev);
-    static keymaster_error_t begin(const keymaster2_device_t* dev, keymaster_purpose_t purpose,
-                                   const keymaster_key_blob_t* key,
-                                   const keymaster_key_param_set_t* in_params,
-                                   keymaster_key_param_set_t* out_params,
-                                   keymaster_operation_handle_t* operation_handle);
-    static keymaster_error_t update(const keymaster2_device_t* dev,
-                                    keymaster_operation_handle_t operation_handle,
-                                    const keymaster_key_param_set_t* in_params,
-                                    const keymaster_blob_t* input, size_t* input_consumed,
-                                    keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
-    static keymaster_error_t finish(const keymaster2_device_t* dev,
-                                    keymaster_operation_handle_t operation_handle,
-                                    const keymaster_key_param_set_t* in_params,
-                                    const keymaster_blob_t* input, const keymaster_blob_t* signature,
-                                    keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
-    static keymaster_error_t abort(const keymaster2_device_t* dev,
-                                   keymaster_operation_handle_t operation_handle);
-
-    keymaster2_device_t device_;
-    keymaster_error_t error_;
-    int32_t message_version_;
-};
-
-}  // namespace keymaster
-
-#endif  // TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
diff --git a/trusty/keymaster/trusty_keymaster_device_test.cpp b/trusty/keymaster/trusty_keymaster_device_test.cpp
deleted file mode 100644
index 9227964..0000000
--- a/trusty/keymaster/trusty_keymaster_device_test.cpp
+++ /dev/null
@@ -1,562 +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 <algorithm>
-#include <fstream>
-#include <memory>
-
-#include <gtest/gtest.h>
-#include <openssl/engine.h>
-
-#include <hardware/keymaster0.h>
-
-#include <keymaster/android_keymaster.h>
-#include <keymaster/android_keymaster_messages.h>
-#include <keymaster/android_keymaster_utils.h>
-#include <keymaster/keymaster_tags.h>
-#include <keymaster/soft_keymaster_context.h>
-
-#include "android_keymaster_test_utils.h"
-#include "trusty_keymaster_device.h"
-#include "openssl_utils.h"
-
-using std::string;
-using std::ifstream;
-using std::istreambuf_iterator;
-
-static keymaster::AndroidKeymaster *impl_ =  nullptr;
-
-extern "C" {
-int __android_log_print();
-}
-
-int __android_log_print() {
-    return 0;
-}
-
-int main(int argc, char** argv) {
-    ::testing::InitGoogleTest(&argc, argv);
-    int result = RUN_ALL_TESTS();
-    // Clean up stuff OpenSSL leaves around, so Valgrind doesn't complain.
-    CRYPTO_cleanup_all_ex_data();
-    ERR_free_strings();
-    return result;
-}
-
-int trusty_keymaster_connect() {
-    impl_ = new keymaster::AndroidKeymaster(new keymaster::SoftKeymasterContext(nullptr), 16);
-}
-
-void trusty_keymaster_disconnect() {
-    delete static_cast<keymaster::AndroidKeymaster*>(priv_);
-}
-
-template <typename Req, typename Rsp>
-static int fake_call(keymaster::AndroidKeymaster* device,
-                       void (keymaster::AndroidKeymaster::*method)(const Req&, Rsp*), void* in_buf,
-                       uint32_t in_size, void* out_buf, uint32_t* out_size) {
-    Req req;
-    const uint8_t* in = static_cast<uint8_t*>(in_buf);
-    req.Deserialize(&in, in + in_size);
-    Rsp rsp;
-    (device->*method)(req, &rsp);
-
-    *out_size = rsp.SerializedSize();
-    uint8_t* out = static_cast<uint8_t*>(out_buf);
-    rsp.Serialize(out, out + *out_size);
-    return 0;
-}
-
-int trusty_keymaster_call(uint32_t cmd, void* in_buf, uint32_t in_size, void* out_buf,
-                       uint32_t* out_size) {
-    switch (cmd) {
-    case KM_GENERATE_KEY:
-        return fake_call(impl_, &keymaster::AndroidKeymaster::GenerateKey, in_buf, in_size,
-                           out_buf, out_size);
-    case KM_BEGIN_OPERATION:
-        return fake_call(impl_, &keymaster::AndroidKeymaster::BeginOperation, in_buf, in_size,
-                           out_buf, out_size);
-    case KM_UPDATE_OPERATION:
-        return fake_call(impl_, &keymaster::AndroidKeymaster::UpdateOperation, in_buf, in_size,
-                           out_buf, out_size);
-    case KM_FINISH_OPERATION:
-        return fake_call(impl_, &keymaster::AndroidKeymaster::FinishOperation, in_buf, in_size,
-                           out_buf, out_size);
-    case KM_IMPORT_KEY:
-        return fake_call(impl_, &keymaster::AndroidKeymaster::ImportKey, in_buf, in_size, out_buf,
-                           out_size);
-    case KM_EXPORT_KEY:
-        return fake_call(impl_, &keymaster::AndroidKeymaster::ExportKey, in_buf, in_size, out_buf,
-                           out_size);
-    }
-    return -EINVAL;
-
-}
-
-namespace keymaster {
-namespace test {
-
-class TrustyKeymasterTest : public testing::Test {
-  protected:
-    TrustyKeymasterTest() : device(NULL) {}
-
-    keymaster_rsa_keygen_params_t build_rsa_params() {
-        keymaster_rsa_keygen_params_t rsa_params;
-        rsa_params.public_exponent = 65537;
-        rsa_params.modulus_size = 2048;
-        return rsa_params;
-    }
-
-    uint8_t* build_message(size_t length) {
-        uint8_t* msg = new uint8_t[length];
-        memset(msg, 'a', length);
-        return msg;
-    }
-
-    size_t dsa_message_len(const keymaster_dsa_keygen_params_t& params) {
-        switch (params.key_size) {
-        case 256:
-        case 1024:
-            return 48;
-        case 2048:
-        case 4096:
-            return 72;
-        default:
-            // Oops.
-            return 0;
-        }
-    }
-
-    TrustyKeymasterDevice device;
-};
-
-class Malloc_Delete {
-  public:
-    Malloc_Delete(void* p) : p_(p) {}
-    ~Malloc_Delete() { free(p_); }
-
-  private:
-    void* p_;
-};
-
-typedef TrustyKeymasterTest KeyGenTest;
-TEST_F(KeyGenTest, RsaSuccess) {
-    keymaster_rsa_keygen_params_t params = build_rsa_params();
-    uint8_t* ptr = NULL;
-    size_t size;
-    ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
-    EXPECT_GT(size, 0U);
-    Malloc_Delete key_deleter(ptr);
-}
-
-TEST_F(KeyGenTest, EcdsaSuccess) {
-    keymaster_ec_keygen_params_t ec_params = {256};
-    uint8_t* ptr = NULL;
-    size_t size;
-    ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &ec_params, &ptr, &size));
-    EXPECT_GT(size, 0U);
-    Malloc_Delete key_deleter(ptr);
-}
-
-typedef TrustyKeymasterTest SigningTest;
-TEST_F(SigningTest, RsaSuccess) {
-    keymaster_rsa_keygen_params_t params = build_rsa_params();
-    uint8_t* ptr = NULL;
-    size_t size;
-    ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
-    EXPECT_GT(size, 0U);
-    Malloc_Delete key_deleter(ptr);
-
-    keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
-    size_t message_len = params.modulus_size / 8;
-    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,
-                                            &signature, &siglen));
-    Malloc_Delete sig_deleter(signature);
-    EXPECT_EQ(message_len, siglen);
-}
-
-TEST_F(SigningTest, RsaShortMessage) {
-    keymaster_rsa_keygen_params_t params = build_rsa_params();
-    uint8_t* ptr = NULL;
-    size_t size;
-    ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
-    EXPECT_GT(size, 0U);
-    Malloc_Delete key_deleter(ptr);
-
-    keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
-    size_t message_len = params.modulus_size / 8 - 1;
-    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(),
-                                                       message_len, &signature, &siglen));
-}
-
-TEST_F(SigningTest, RsaLongMessage) {
-    keymaster_rsa_keygen_params_t params = build_rsa_params();
-    uint8_t* ptr = NULL;
-    size_t size;
-    ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
-    EXPECT_GT(size, 0U);
-    Malloc_Delete key_deleter(ptr);
-
-    keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
-    size_t message_len = params.modulus_size / 8 + 1;
-    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(),
-                                                       message_len, &signature, &siglen));
-}
-
-TEST_F(SigningTest, EcdsaSuccess) {
-    keymaster_ec_keygen_params_t params = {256};
-    uint8_t* ptr = NULL;
-    size_t size;
-    ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &params, &ptr, &size));
-    EXPECT_GT(size, 0U);
-    Malloc_Delete key_deleter(ptr);
-
-    keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
-    uint8_t message[] = "12345678901234567890123456789012";
-    uint8_t* signature;
-    size_t siglen;
-    ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message,
-                                            array_size(message) - 1, &signature, &siglen));
-    Malloc_Delete sig_deleter(signature);
-    EXPECT_GT(siglen, 69U);
-    EXPECT_LT(siglen, 73U);
-}
-
-TEST_F(SigningTest, EcdsaEmptyMessageSuccess) {
-    keymaster_ec_keygen_params_t params = {256};
-    uint8_t* ptr = NULL;
-    size_t size;
-    ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &params, &ptr, &size));
-    EXPECT_GT(size, 0U);
-    Malloc_Delete key_deleter(ptr);
-
-    keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
-    uint8_t message[] = "";
-    uint8_t* signature;
-    size_t siglen;
-    ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message,
-                                            array_size(message) - 1, &signature, &siglen));
-    Malloc_Delete sig_deleter(signature);
-    EXPECT_GT(siglen, 69U);
-    EXPECT_LT(siglen, 73U);
-}
-
-TEST_F(SigningTest, EcdsaLargeMessageSuccess) {
-    keymaster_ec_keygen_params_t params = {256};
-    uint8_t* ptr = NULL;
-    size_t size;
-    ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &params, &ptr, &size));
-    EXPECT_GT(size, 0U);
-    Malloc_Delete key_deleter(ptr);
-
-    keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
-    size_t message_len = 1024 * 7;
-    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
-    // contents of message don't matter.
-    uint8_t* signature;
-    size_t siglen;
-    ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
-                                            &signature, &siglen));
-    Malloc_Delete sig_deleter(signature);
-    EXPECT_GT(siglen, 69U);
-    EXPECT_LT(siglen, 73U);
-}
-
-typedef TrustyKeymasterTest VerificationTest;
-TEST_F(VerificationTest, RsaSuccess) {
-    keymaster_rsa_keygen_params_t params = build_rsa_params();
-    uint8_t* ptr = NULL;
-    size_t size;
-    ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
-    EXPECT_GT(size, 0U);
-    Malloc_Delete key_deleter(ptr);
-
-    keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
-    size_t message_len = params.modulus_size / 8;
-    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,
-                                            &signature, &siglen));
-    Malloc_Delete sig_deleter(signature);
-
-    EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, ptr, size, message.get(), message_len,
-                                              signature, siglen));
-}
-
-TEST_F(VerificationTest, RsaBadSignature) {
-    keymaster_rsa_keygen_params_t params = build_rsa_params();
-    uint8_t* ptr = NULL;
-    size_t size;
-    ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
-    EXPECT_GT(size, 0U);
-    Malloc_Delete key_deleter(ptr);
-
-    keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
-    size_t message_len = params.modulus_size / 8;
-    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,
-                                            &signature, &siglen));
-
-    Malloc_Delete sig_deleter(signature);
-    signature[siglen / 2]++;
-    EXPECT_EQ(
-        KM_ERROR_VERIFICATION_FAILED,
-        device.verify_data(&sig_params, ptr, size, message.get(), message_len, signature, siglen));
-}
-
-TEST_F(VerificationTest, RsaBadMessage) {
-    keymaster_rsa_keygen_params_t params = build_rsa_params();
-    uint8_t* ptr = NULL;
-    size_t size;
-    ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
-    EXPECT_GT(size, 0U);
-    Malloc_Delete key_deleter(ptr);
-
-    keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
-    size_t message_len = params.modulus_size / 8;
-    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,
-                                            &signature, &siglen));
-    Malloc_Delete sig_deleter(signature);
-    message[0]++;
-    EXPECT_EQ(
-        KM_ERROR_VERIFICATION_FAILED,
-        device.verify_data(&sig_params, ptr, size, message.get(), message_len, signature, siglen));
-}
-
-TEST_F(VerificationTest, RsaShortMessage) {
-    keymaster_rsa_keygen_params_t params = build_rsa_params();
-    uint8_t* ptr = NULL;
-    size_t size;
-    ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
-    EXPECT_GT(size, 0U);
-    Malloc_Delete key_deleter(ptr);
-
-    keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
-    size_t message_len = params.modulus_size / 8;
-    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,
-                                            &signature, &siglen));
-
-    Malloc_Delete sig_deleter(signature);
-    EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH,
-              device.verify_data(&sig_params, ptr, size, message.get(), message_len - 1, signature,
-                                 siglen));
-}
-
-TEST_F(VerificationTest, RsaLongMessage) {
-    keymaster_rsa_keygen_params_t params = build_rsa_params();
-    uint8_t* ptr = NULL;
-    size_t size;
-    ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
-    EXPECT_GT(size, 0U);
-    Malloc_Delete key_deleter(ptr);
-
-    keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
-    size_t message_len = params.modulus_size / 8;
-    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,
-                                            &signature, &siglen));
-    Malloc_Delete sig_deleter(signature);
-    EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH,
-              device.verify_data(&sig_params, ptr, size, message.get(), message_len + 1, signature,
-                                 siglen));
-}
-
-TEST_F(VerificationTest, EcdsaSuccess) {
-    keymaster_ec_keygen_params_t params = {256};
-    uint8_t* ptr = NULL;
-    size_t size;
-    ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &params, &ptr, &size));
-    EXPECT_GT(size, 0U);
-    Malloc_Delete key_deleter(ptr);
-
-    keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
-    uint8_t message[] = "12345678901234567890123456789012";
-    uint8_t* signature;
-    size_t siglen;
-    ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message,
-                                            array_size(message) - 1, &signature, &siglen));
-    Malloc_Delete sig_deleter(signature);
-    EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, ptr, size, message,
-                                              array_size(message) - 1, signature, siglen));
-}
-
-TEST_F(VerificationTest, EcdsaLargeMessageSuccess) {
-    keymaster_ec_keygen_params_t params = {256};
-    uint8_t* ptr = NULL;
-    size_t size;
-    ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &params, &ptr, &size));
-    EXPECT_GT(size, 0U);
-    Malloc_Delete key_deleter(ptr);
-
-    keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
-    size_t message_len = 1024 * 7;
-    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
-    // contents of message don't matter.
-    uint8_t* signature;
-    size_t siglen;
-    ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
-                                            &signature, &siglen));
-    Malloc_Delete sig_deleter(signature);
-    EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, ptr, size, message.get(), message_len,
-                                              signature, siglen));
-}
-
-static string read_file(const string& file_name) {
-    ifstream file_stream(file_name, std::ios::binary);
-    istreambuf_iterator<char> file_begin(file_stream);
-    istreambuf_iterator<char> file_end;
-    return string(file_begin, file_end);
-}
-
-typedef TrustyKeymasterTest ImportKeyTest;
-TEST_F(ImportKeyTest, RsaSuccess) {
-    string pk8_key = read_file("../../../../system/keymaster/rsa_privkey_pk8.der");
-    ASSERT_EQ(633U, pk8_key.size());
-
-    uint8_t* key = NULL;
-    size_t size;
-    ASSERT_EQ(KM_ERROR_OK, device.import_keypair(reinterpret_cast<const uint8_t*>(pk8_key.data()),
-                                                 pk8_key.size(), &key, &size));
-    Malloc_Delete key_deleter(key);
-
-    keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
-    size_t message_size = 1024 /* key size */ / 8;
-    std::unique_ptr<uint8_t[]> message(new uint8_t[message_size]);
-    memset(message.get(), 'a', message_size);
-    uint8_t* signature;
-    size_t siglen;
-    ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, key, size, message.get(), message_size,
-                                            &signature, &siglen));
-    Malloc_Delete sig_deleter(signature);
-    EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, key, size, message.get(), message_size,
-                                              signature, siglen));
-}
-
-TEST_F(ImportKeyTest, EcdsaSuccess) {
-    string pk8_key = read_file("../../../../system/keymaster/ec_privkey_pk8.der");
-    ASSERT_EQ(138U, pk8_key.size());
-
-    uint8_t* key = NULL;
-    size_t size;
-    ASSERT_EQ(KM_ERROR_OK, device.import_keypair(reinterpret_cast<const uint8_t*>(pk8_key.data()),
-                                                 pk8_key.size(), &key, &size));
-    Malloc_Delete key_deleter(key);
-
-    keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
-    uint8_t message[] = "12345678901234567890123456789012";
-    uint8_t* signature;
-    size_t siglen;
-    ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, key, size, message,
-                                            array_size(message) - 1, &signature, &siglen));
-    Malloc_Delete sig_deleter(signature);
-    EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, key, size, message,
-                                              array_size(message) - 1, signature, siglen));
-}
-
-struct EVP_PKEY_CTX_Delete {
-    void operator()(EVP_PKEY_CTX* p) { EVP_PKEY_CTX_free(p); }
-};
-
-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) {
-    std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &key, key_len));
-    ASSERT_TRUE(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)
-        ASSERT_EQ(1, EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_NO_PADDING));
-    EXPECT_EQ(1, EVP_PKEY_verify(ctx.get(), signature, signature_len, message, message_len));
-}
-
-typedef TrustyKeymasterTest ExportKeyTest;
-TEST_F(ExportKeyTest, RsaSuccess) {
-    keymaster_rsa_keygen_params_t params = build_rsa_params();
-    uint8_t* ptr = NULL;
-    size_t size;
-    ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
-    EXPECT_GT(size, 0U);
-    Malloc_Delete key_deleter(ptr);
-
-    uint8_t* exported;
-    size_t exported_size;
-    EXPECT_EQ(KM_ERROR_OK, device.get_keypair_public(ptr, size, &exported, &exported_size));
-    Malloc_Delete exported_deleter(exported);
-
-    // 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;
-    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,
-                                            &signature, &siglen));
-    Malloc_Delete sig_deleter(signature);
-    EXPECT_EQ(message_len, siglen);
-    const uint8_t* tmp = exported;
-
-    VerifySignature(exported, exported_size, signature, siglen, message.get(), message_len);
-}
-
-typedef TrustyKeymasterTest ExportKeyTest;
-TEST_F(ExportKeyTest, EcdsaSuccess) {
-    keymaster_ec_keygen_params_t params = {256};
-    uint8_t* key = NULL;
-    size_t size;
-    ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &params, &key, &size));
-    EXPECT_GT(size, 0U);
-    Malloc_Delete key_deleter(key);
-
-    uint8_t* exported;
-    size_t exported_size;
-    EXPECT_EQ(KM_ERROR_OK, device.get_keypair_public(key, size, &exported, &exported_size));
-    Malloc_Delete exported_deleter(exported);
-
-    // Sign a message so we can verify it with the exported pubkey.
-    keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
-    uint8_t message[] = "12345678901234567890123456789012";
-    uint8_t* signature;
-    size_t siglen;
-    ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, key, size, message,
-                                            array_size(message) - 1, &signature, &siglen));
-    Malloc_Delete sig_deleter(signature);
-    EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, key, size, message,
-                                              array_size(message) - 1, signature, siglen));
-
-    VerifySignature(exported, exported_size, signature, siglen, message, array_size(message) - 1);
-}
-
-}  // namespace test
-}  // namespace keymaster
diff --git a/trusty/keymaster/trusty_keymaster_ipc.cpp b/trusty/keymaster/trusty_keymaster_ipc.cpp
deleted file mode 100644
index 686e7ae..0000000
--- a/trusty/keymaster/trusty_keymaster_ipc.cpp
+++ /dev/null
@@ -1,112 +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 "TrustyKeymaster"
-
-// TODO: make this generic in libtrusty
-
-#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>
-
-#include "keymaster_ipc.h"
-#include "trusty_keymaster_ipc.h"
-
-#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
-
-static int handle_ = -1;
-
-int trusty_keymaster_connect() {
-    int rc = tipc_connect(TRUSTY_DEVICE_NAME, KEYMASTER_PORT);
-    if (rc < 0) {
-        return rc;
-    }
-
-    handle_ = rc;
-    return 0;
-}
-
-int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
-                          uint32_t* out_size) {
-    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);
-
-    ssize_t rc = write(handle_, msg, msg_size);
-    free(msg);
-
-    if (rc < 0) {
-        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;
-        }
-
-        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;
-        }
-    }
-
-    return rc;
-}
-
-void trusty_keymaster_disconnect() {
-    if (handle_ >= 0) {
-        tipc_close(handle_);
-    }
-    handle_ = -1;
-}
diff --git a/trusty/keymaster/trusty_keymaster_ipc.h b/trusty/keymaster/trusty_keymaster_ipc.h
deleted file mode 100644
index c15f7c1..0000000
--- a/trusty/keymaster/trusty_keymaster_ipc.h
+++ /dev/null
@@ -1,29 +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 TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
-#define TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
-
-__BEGIN_DECLS
-
-int trusty_keymaster_connect(void);
-int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
-                          uint32_t* out_size);
-void trusty_keymaster_disconnect(void);
-
-__END_DECLS
-
-#endif  // TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
diff --git a/trusty/keymaster/trusty_keymaster_main.cpp b/trusty/keymaster/trusty_keymaster_main.cpp
deleted file mode 100644
index ed78b7f..0000000
--- a/trusty/keymaster/trusty_keymaster_main.cpp
+++ /dev/null
@@ -1,401 +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.
- */
-
-#include <keymaster/keymaster_configuration.h>
-
-#include <stdio.h>
-#include <memory>
-
-#include <openssl/evp.h>
-#include <openssl/x509.h>
-
-#include "trusty_keymaster_device.h"
-
-using keymaster::TrustyKeymasterDevice;
-
-unsigned char rsa_privkey_pk8_der[] = {
-    0x30, 0x82, 0x02, 0x75, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
-    0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x5f, 0x30, 0x82, 0x02, 0x5b, 0x02, 0x01,
-    0x00, 0x02, 0x81, 0x81, 0x00, 0xc6, 0x09, 0x54, 0x09, 0x04, 0x7d, 0x86, 0x34, 0x81, 0x2d, 0x5a,
-    0x21, 0x81, 0x76, 0xe4, 0x5c, 0x41, 0xd6, 0x0a, 0x75, 0xb1, 0x39, 0x01, 0xf2, 0x34, 0x22, 0x6c,
-    0xff, 0xe7, 0x76, 0x52, 0x1c, 0x5a, 0x77, 0xb9, 0xe3, 0x89, 0x41, 0x7b, 0x71, 0xc0, 0xb6, 0xa4,
-    0x4d, 0x13, 0xaf, 0xe4, 0xe4, 0xa2, 0x80, 0x5d, 0x46, 0xc9, 0xda, 0x29, 0x35, 0xad, 0xb1, 0xff,
-    0x0c, 0x1f, 0x24, 0xea, 0x06, 0xe6, 0x2b, 0x20, 0xd7, 0x76, 0x43, 0x0a, 0x4d, 0x43, 0x51, 0x57,
-    0x23, 0x3c, 0x6f, 0x91, 0x67, 0x83, 0xc3, 0x0e, 0x31, 0x0f, 0xcb, 0xd8, 0x9b, 0x85, 0xc2, 0xd5,
-    0x67, 0x71, 0x16, 0x97, 0x85, 0xac, 0x12, 0xbc, 0xa2, 0x44, 0xab, 0xda, 0x72, 0xbf, 0xb1, 0x9f,
-    0xc4, 0x4d, 0x27, 0xc8, 0x1e, 0x1d, 0x92, 0xde, 0x28, 0x4f, 0x40, 0x61, 0xed, 0xfd, 0x99, 0x28,
-    0x07, 0x45, 0xea, 0x6d, 0x25, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x81, 0x80, 0x1b, 0xe0, 0xf0,
-    0x4d, 0x9c, 0xae, 0x37, 0x18, 0x69, 0x1f, 0x03, 0x53, 0x38, 0x30, 0x8e, 0x91, 0x56, 0x4b, 0x55,
-    0x89, 0x9f, 0xfb, 0x50, 0x84, 0xd2, 0x46, 0x0e, 0x66, 0x30, 0x25, 0x7e, 0x05, 0xb3, 0xce, 0xab,
-    0x02, 0x97, 0x2d, 0xfa, 0xbc, 0xd6, 0xce, 0x5f, 0x6e, 0xe2, 0x58, 0x9e, 0xb6, 0x79, 0x11, 0xed,
-    0x0f, 0xac, 0x16, 0xe4, 0x3a, 0x44, 0x4b, 0x8c, 0x86, 0x1e, 0x54, 0x4a, 0x05, 0x93, 0x36, 0x57,
-    0x72, 0xf8, 0xba, 0xf6, 0xb2, 0x2f, 0xc9, 0xe3, 0xc5, 0xf1, 0x02, 0x4b, 0x06, 0x3a, 0xc0, 0x80,
-    0xa7, 0xb2, 0x23, 0x4c, 0xf8, 0xae, 0xe8, 0xf6, 0xc4, 0x7b, 0xbf, 0x4f, 0xd3, 0xac, 0xe7, 0x24,
-    0x02, 0x90, 0xbe, 0xf1, 0x6c, 0x0b, 0x3f, 0x7f, 0x3c, 0xdd, 0x64, 0xce, 0x3a, 0xb5, 0x91, 0x2c,
-    0xf6, 0xe3, 0x2f, 0x39, 0xab, 0x18, 0x83, 0x58, 0xaf, 0xcc, 0xcd, 0x80, 0x81, 0x02, 0x41, 0x00,
-    0xe4, 0xb4, 0x9e, 0xf5, 0x0f, 0x76, 0x5d, 0x3b, 0x24, 0xdd, 0xe0, 0x1a, 0xce, 0xaa, 0xf1, 0x30,
-    0xf2, 0xc7, 0x66, 0x70, 0xa9, 0x1a, 0x61, 0xae, 0x08, 0xaf, 0x49, 0x7b, 0x4a, 0x82, 0xbe, 0x6d,
-    0xee, 0x8f, 0xcd, 0xd5, 0xe3, 0xf7, 0xba, 0x1c, 0xfb, 0x1f, 0x0c, 0x92, 0x6b, 0x88, 0xf8, 0x8c,
-    0x92, 0xbf, 0xab, 0x13, 0x7f, 0xba, 0x22, 0x85, 0x22, 0x7b, 0x83, 0xc3, 0x42, 0xff, 0x7c, 0x55,
-    0x02, 0x41, 0x00, 0xdd, 0xab, 0xb5, 0x83, 0x9c, 0x4c, 0x7f, 0x6b, 0xf3, 0xd4, 0x18, 0x32, 0x31,
-    0xf0, 0x05, 0xb3, 0x1a, 0xa5, 0x8a, 0xff, 0xdd, 0xa5, 0xc7, 0x9e, 0x4c, 0xce, 0x21, 0x7f, 0x6b,
-    0xc9, 0x30, 0xdb, 0xe5, 0x63, 0xd4, 0x80, 0x70, 0x6c, 0x24, 0xe9, 0xeb, 0xfc, 0xab, 0x28, 0xa6,
-    0xcd, 0xef, 0xd3, 0x24, 0xb7, 0x7e, 0x1b, 0xf7, 0x25, 0x1b, 0x70, 0x90, 0x92, 0xc2, 0x4f, 0xf5,
-    0x01, 0xfd, 0x91, 0x02, 0x40, 0x23, 0xd4, 0x34, 0x0e, 0xda, 0x34, 0x45, 0xd8, 0xcd, 0x26, 0xc1,
-    0x44, 0x11, 0xda, 0x6f, 0xdc, 0xa6, 0x3c, 0x1c, 0xcd, 0x4b, 0x80, 0xa9, 0x8a, 0xd5, 0x2b, 0x78,
-    0xcc, 0x8a, 0xd8, 0xbe, 0xb2, 0x84, 0x2c, 0x1d, 0x28, 0x04, 0x05, 0xbc, 0x2f, 0x6c, 0x1b, 0xea,
-    0x21, 0x4a, 0x1d, 0x74, 0x2a, 0xb9, 0x96, 0xb3, 0x5b, 0x63, 0xa8, 0x2a, 0x5e, 0x47, 0x0f, 0xa8,
-    0x8d, 0xbf, 0x82, 0x3c, 0xdd, 0x02, 0x40, 0x1b, 0x7b, 0x57, 0x44, 0x9a, 0xd3, 0x0d, 0x15, 0x18,
-    0x24, 0x9a, 0x5f, 0x56, 0xbb, 0x98, 0x29, 0x4d, 0x4b, 0x6a, 0xc1, 0x2f, 0xfc, 0x86, 0x94, 0x04,
-    0x97, 0xa5, 0xa5, 0x83, 0x7a, 0x6c, 0xf9, 0x46, 0x26, 0x2b, 0x49, 0x45, 0x26, 0xd3, 0x28, 0xc1,
-    0x1e, 0x11, 0x26, 0x38, 0x0f, 0xde, 0x04, 0xc2, 0x4f, 0x91, 0x6d, 0xec, 0x25, 0x08, 0x92, 0xdb,
-    0x09, 0xa6, 0xd7, 0x7c, 0xdb, 0xa3, 0x51, 0x02, 0x40, 0x77, 0x62, 0xcd, 0x8f, 0x4d, 0x05, 0x0d,
-    0xa5, 0x6b, 0xd5, 0x91, 0xad, 0xb5, 0x15, 0xd2, 0x4d, 0x7c, 0xcd, 0x32, 0xcc, 0xa0, 0xd0, 0x5f,
-    0x86, 0x6d, 0x58, 0x35, 0x14, 0xbd, 0x73, 0x24, 0xd5, 0xf3, 0x36, 0x45, 0xe8, 0xed, 0x8b, 0x4a,
-    0x1c, 0xb3, 0xcc, 0x4a, 0x1d, 0x67, 0x98, 0x73, 0x99, 0xf2, 0xa0, 0x9f, 0x5b, 0x3f, 0xb6, 0x8c,
-    0x88, 0xd5, 0xe5, 0xd9, 0x0a, 0xc3, 0x34, 0x92, 0xd6};
-unsigned int rsa_privkey_pk8_der_len = 633;
-
-unsigned char dsa_privkey_pk8_der[] = {
-    0x30, 0x82, 0x01, 0x4b, 0x02, 0x01, 0x00, 0x30, 0x82, 0x01, 0x2b, 0x06, 0x07, 0x2a, 0x86, 0x48,
-    0xce, 0x38, 0x04, 0x01, 0x30, 0x82, 0x01, 0x1e, 0x02, 0x81, 0x81, 0x00, 0xa3, 0xf3, 0xe9, 0xb6,
-    0x7e, 0x7d, 0x88, 0xf6, 0xb7, 0xe5, 0xf5, 0x1f, 0x3b, 0xee, 0xac, 0xd7, 0xad, 0xbc, 0xc9, 0xd1,
-    0x5a, 0xf8, 0x88, 0xc4, 0xef, 0x6e, 0x3d, 0x74, 0x19, 0x74, 0xe7, 0xd8, 0xe0, 0x26, 0x44, 0x19,
-    0x86, 0xaf, 0x19, 0xdb, 0x05, 0xe9, 0x3b, 0x8b, 0x58, 0x58, 0xde, 0xe5, 0x4f, 0x48, 0x15, 0x01,
-    0xea, 0xe6, 0x83, 0x52, 0xd7, 0xc1, 0x21, 0xdf, 0xb9, 0xb8, 0x07, 0x66, 0x50, 0xfb, 0x3a, 0x0c,
-    0xb3, 0x85, 0xee, 0xbb, 0x04, 0x5f, 0xc2, 0x6d, 0x6d, 0x95, 0xfa, 0x11, 0x93, 0x1e, 0x59, 0x5b,
-    0xb1, 0x45, 0x8d, 0xe0, 0x3d, 0x73, 0xaa, 0xf2, 0x41, 0x14, 0x51, 0x07, 0x72, 0x3d, 0xa2, 0xf7,
-    0x58, 0xcd, 0x11, 0xa1, 0x32, 0xcf, 0xda, 0x42, 0xb7, 0xcc, 0x32, 0x80, 0xdb, 0x87, 0x82, 0xec,
-    0x42, 0xdb, 0x5a, 0x55, 0x24, 0x24, 0xa2, 0xd1, 0x55, 0x29, 0xad, 0xeb, 0x02, 0x15, 0x00, 0xeb,
-    0xea, 0x17, 0xd2, 0x09, 0xb3, 0xd7, 0x21, 0x9a, 0x21, 0x07, 0x82, 0x8f, 0xab, 0xfe, 0x88, 0x71,
-    0x68, 0xf7, 0xe3, 0x02, 0x81, 0x80, 0x19, 0x1c, 0x71, 0xfd, 0xe0, 0x03, 0x0c, 0x43, 0xd9, 0x0b,
-    0xf6, 0xcd, 0xd6, 0xa9, 0x70, 0xe7, 0x37, 0x86, 0x3a, 0x78, 0xe9, 0xa7, 0x47, 0xa7, 0x47, 0x06,
-    0x88, 0xb1, 0xaf, 0xd7, 0xf3, 0xf1, 0xa1, 0xd7, 0x00, 0x61, 0x28, 0x88, 0x31, 0x48, 0x60, 0xd8,
-    0x11, 0xef, 0xa5, 0x24, 0x1a, 0x81, 0xc4, 0x2a, 0xe2, 0xea, 0x0e, 0x36, 0xd2, 0xd2, 0x05, 0x84,
-    0x37, 0xcf, 0x32, 0x7d, 0x09, 0xe6, 0x0f, 0x8b, 0x0c, 0xc8, 0xc2, 0xa4, 0xb1, 0xdc, 0x80, 0xca,
-    0x68, 0xdf, 0xaf, 0xd2, 0x90, 0xc0, 0x37, 0x58, 0x54, 0x36, 0x8f, 0x49, 0xb8, 0x62, 0x75, 0x8b,
-    0x48, 0x47, 0xc0, 0xbe, 0xf7, 0x9a, 0x92, 0xa6, 0x68, 0x05, 0xda, 0x9d, 0xaf, 0x72, 0x9a, 0x67,
-    0xb3, 0xb4, 0x14, 0x03, 0xae, 0x4f, 0x4c, 0x76, 0xb9, 0xd8, 0x64, 0x0a, 0xba, 0x3b, 0xa8, 0x00,
-    0x60, 0x4d, 0xae, 0x81, 0xc3, 0xc5, 0x04, 0x17, 0x02, 0x15, 0x00, 0x81, 0x9d, 0xfd, 0x53, 0x0c,
-    0xc1, 0x8f, 0xbe, 0x8b, 0xea, 0x00, 0x26, 0x19, 0x29, 0x33, 0x91, 0x84, 0xbe, 0xad, 0x81};
-unsigned int dsa_privkey_pk8_der_len = 335;
-
-unsigned char ec_privkey_pk8_der[] = {
-    0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
-    0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02,
-    0x01, 0x01, 0x04, 0x20, 0x73, 0x7c, 0x2e, 0xcd, 0x7b, 0x8d, 0x19, 0x40, 0xbf, 0x29, 0x30, 0xaa,
-    0x9b, 0x4e, 0xd3, 0xff, 0x94, 0x1e, 0xed, 0x09, 0x36, 0x6b, 0xc0, 0x32, 0x99, 0x98, 0x64, 0x81,
-    0xf3, 0xa4, 0xd8, 0x59, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0xbf, 0x85, 0xd7, 0x72, 0x0d, 0x07,
-    0xc2, 0x54, 0x61, 0x68, 0x3b, 0xc6, 0x48, 0xb4, 0x77, 0x8a, 0x9a, 0x14, 0xdd, 0x8a, 0x02, 0x4e,
-    0x3b, 0xdd, 0x8c, 0x7d, 0xdd, 0x9a, 0xb2, 0xb5, 0x28, 0xbb, 0xc7, 0xaa, 0x1b, 0x51, 0xf1, 0x4e,
-    0xbb, 0xbb, 0x0b, 0xd0, 0xce, 0x21, 0xbc, 0xc4, 0x1c, 0x6e, 0xb0, 0x00, 0x83, 0xcf, 0x33, 0x76,
-    0xd1, 0x1f, 0xd4, 0x49, 0x49, 0xe0, 0xb2, 0x18, 0x3b, 0xfe};
-unsigned int ec_privkey_pk8_der_len = 138;
-
-keymaster_key_param_t ec_params[] = {
-    keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_EC),
-    keymaster_param_long(KM_TAG_EC_CURVE, KM_EC_CURVE_P_521),
-    keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN),
-    keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_VERIFY),
-    keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
-    keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED),
-};
-keymaster_key_param_set_t ec_param_set = {ec_params, sizeof(ec_params) / sizeof(*ec_params)};
-
-keymaster_key_param_t rsa_params[] = {
-    keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_RSA),
-    keymaster_param_int(KM_TAG_KEY_SIZE, 1024),
-    keymaster_param_long(KM_TAG_RSA_PUBLIC_EXPONENT, 65537),
-    keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN),
-    keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_VERIFY),
-    keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
-    keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
-    keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED),
-};
-keymaster_key_param_set_t rsa_param_set = {rsa_params, sizeof(rsa_params) / sizeof(*rsa_params)};
-
-struct EVP_PKEY_Delete {
-    void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
-};
-
-struct EVP_PKEY_CTX_Delete {
-    void operator()(EVP_PKEY_CTX* p) { EVP_PKEY_CTX_free(p); }
-};
-
-static bool do_operation(TrustyKeymasterDevice* device, keymaster_purpose_t purpose,
-                         keymaster_key_blob_t* key, keymaster_blob_t* input,
-                         keymaster_blob_t* signature, keymaster_blob_t* output) {
-    keymaster_key_param_t params[] = {
-        keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
-        keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
-    };
-    keymaster_key_param_set_t param_set = {params, sizeof(params) / sizeof(*params)};
-    keymaster_operation_handle_t op_handle;
-    keymaster_error_t error = device->begin(purpose, key, &param_set, nullptr, &op_handle);
-    if (error != KM_ERROR_OK) {
-        printf("Keymaster begin() failed: %d\n", error);
-        return false;
-    }
-    size_t input_consumed;
-    error = device->update(op_handle, nullptr, input, &input_consumed, nullptr, nullptr);
-    if (error != KM_ERROR_OK) {
-        printf("Keymaster update() failed: %d\n", error);
-        return false;
-    }
-    if (input_consumed != input->data_length) {
-        // This should never happen. If it does, it's a bug in the keymaster implementation.
-        printf("Keymaster update() did not consume all data.\n");
-        device->abort(op_handle);
-        return false;
-    }
-    error = device->finish(op_handle, nullptr, nullptr, signature, nullptr, output);
-    if (error != KM_ERROR_OK) {
-        printf("Keymaster finish() failed: %d\n", error);
-        return false;
-    }
-    return true;
-}
-
-static bool test_import_rsa(TrustyKeymasterDevice* device) {
-    printf("===================\n");
-    printf("= RSA Import Test =\n");
-    printf("===================\n\n");
-
-    printf("=== Importing RSA keypair === \n");
-    keymaster_key_blob_t key;
-    keymaster_blob_t private_key = {rsa_privkey_pk8_der, rsa_privkey_pk8_der_len};
-    int error = device->import_key(&rsa_param_set, KM_KEY_FORMAT_PKCS8, &private_key, &key, nullptr);
-    if (error != KM_ERROR_OK) {
-        printf("Error importing RSA key: %d\n\n", error);
-        return false;
-    }
-    std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
-
-    printf("=== Signing with imported RSA key ===\n");
-    size_t message_len = 1024 / 8;
-    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
-    memset(message.get(), 'a', message_len);
-    keymaster_blob_t input = {message.get(), message_len}, signature;
-
-    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
-        printf("Error signing data with imported RSA key\n\n");
-        return false;
-    }
-    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
-
-    printf("=== Verifying with imported RSA key === \n");
-    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
-        printf("Error verifying data with imported RSA key\n\n");
-        return false;
-    }
-
-    printf("\n");
-    return true;
-}
-
-static bool test_rsa(TrustyKeymasterDevice* device) {
-    printf("============\n");
-    printf("= RSA Test =\n");
-    printf("============\n\n");
-
-    printf("=== Generating RSA key pair ===\n");
-    keymaster_key_blob_t key;
-    int error = device->generate_key(&rsa_param_set, &key, nullptr);
-    if (error != KM_ERROR_OK) {
-        printf("Error generating RSA key pair: %d\n\n", error);
-        return false;
-    }
-    std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
-
-    printf("=== Signing with RSA key === \n");
-    size_t message_len = 1024 / 8;
-    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
-    memset(message.get(), 'a', message_len);
-    keymaster_blob_t input = {message.get(), message_len}, signature;
-
-    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
-        printf("Error signing data with RSA key\n\n");
-        return false;
-    }
-    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
-
-    printf("=== Verifying with RSA key === \n");
-    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
-        printf("Error verifying data with RSA key\n\n");
-        return false;
-    }
-
-    printf("=== Exporting RSA public key ===\n");
-    keymaster_blob_t exported_key;
-    error = device->export_key(KM_KEY_FORMAT_X509, &key, nullptr, nullptr, &exported_key);
-    if (error != KM_ERROR_OK) {
-        printf("Error exporting RSA public key: %d\n\n", error);
-        return false;
-    }
-
-    printf("=== Verifying with exported key ===\n");
-    const uint8_t* tmp = exported_key.data;
-    std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
-        d2i_PUBKEY(NULL, &tmp, exported_key.data_length));
-    std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
-    if (EVP_PKEY_verify_init(ctx.get()) != 1) {
-        printf("Error initializing openss EVP context\n\n");
-        return false;
-    }
-    if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) {
-        printf("Exported key was the wrong type?!?\n\n");
-        return false;
-    }
-
-    EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_NO_PADDING);
-    if (EVP_PKEY_verify(ctx.get(), signature.data, signature.data_length, message.get(),
-                        message_len) != 1) {
-        printf("Verification with exported pubkey failed.\n\n");
-        return false;
-    } else {
-        printf("Verification succeeded\n");
-    }
-
-    printf("\n");
-    return true;
-}
-
-static bool test_import_ecdsa(TrustyKeymasterDevice* device) {
-    printf("=====================\n");
-    printf("= ECDSA Import Test =\n");
-    printf("=====================\n\n");
-
-    printf("=== Importing ECDSA keypair === \n");
-    keymaster_key_blob_t key;
-    keymaster_blob_t private_key = {ec_privkey_pk8_der, ec_privkey_pk8_der_len};
-    int error = device->import_key(&ec_param_set, KM_KEY_FORMAT_PKCS8, &private_key, &key, nullptr);
-    if (error != KM_ERROR_OK) {
-        printf("Error importing ECDSA key: %d\n\n", error);
-        return false;
-    }
-    std::unique_ptr<const uint8_t[]> deleter(key.key_material);
-
-    printf("=== Signing with imported ECDSA key ===\n");
-    size_t message_len = 30 /* arbitrary */;
-    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
-    memset(message.get(), 'a', message_len);
-    keymaster_blob_t input = {message.get(), message_len}, signature;
-
-    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
-        printf("Error signing data with imported ECDSA key\n\n");
-        return false;
-    }
-    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
-
-    printf("=== Verifying with imported ECDSA key === \n");
-    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
-        printf("Error verifying data with imported ECDSA key\n\n");
-        return false;
-    }
-
-    printf("\n");
-    return true;
-}
-
-static bool test_ecdsa(TrustyKeymasterDevice* device) {
-    printf("==============\n");
-    printf("= ECDSA Test =\n");
-    printf("==============\n\n");
-
-    printf("=== Generating ECDSA key pair ===\n");
-    keymaster_key_blob_t key;
-    int error = device->generate_key(&ec_param_set, &key, nullptr);
-    if (error != KM_ERROR_OK) {
-        printf("Error generating ECDSA key pair: %d\n\n", error);
-        return false;
-    }
-    std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
-
-    printf("=== Signing with ECDSA key === \n");
-    size_t message_len = 30 /* arbitrary */;
-    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
-    memset(message.get(), 'a', message_len);
-    keymaster_blob_t input = {message.get(), message_len}, signature;
-
-    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
-        printf("Error signing data with ECDSA key\n\n");
-        return false;
-    }
-    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
-
-    printf("=== Verifying with ECDSA key === \n");
-    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
-        printf("Error verifying data with ECDSA key\n\n");
-        return false;
-    }
-
-    printf("=== Exporting ECDSA public key ===\n");
-    keymaster_blob_t exported_key;
-    error = device->export_key(KM_KEY_FORMAT_X509, &key, nullptr, nullptr, &exported_key);
-    if (error != KM_ERROR_OK) {
-        printf("Error exporting ECDSA public key: %d\n\n", error);
-        return false;
-    }
-
-    printf("=== Verifying with exported key ===\n");
-    const uint8_t* tmp = exported_key.data;
-    std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
-        d2i_PUBKEY(NULL, &tmp, exported_key.data_length));
-    std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
-    if (EVP_PKEY_verify_init(ctx.get()) != 1) {
-        printf("Error initializing openssl EVP context\n\n");
-        return false;
-    }
-    if (EVP_PKEY_type(pkey->type) != EVP_PKEY_EC) {
-        printf("Exported key was the wrong type?!?\n\n");
-        return false;
-    }
-
-    if (EVP_PKEY_verify(ctx.get(), signature.data, signature.data_length, message.get(),
-                        message_len) != 1) {
-        printf("Verification with exported pubkey failed.\n\n");
-        return false;
-    } else {
-        printf("Verification succeeded\n");
-    }
-
-    printf("\n");
-    return true;
-}
-
-int main(void) {
-    TrustyKeymasterDevice device(NULL);
-    keymaster::ConfigureDevice(reinterpret_cast<keymaster2_device_t*>(&device));
-    if (device.session_error() != KM_ERROR_OK) {
-        printf("Failed to initialize Trusty session: %d\n", device.session_error());
-        return 1;
-    }
-    printf("Trusty session initialized\n");
-
-    bool success = true;
-    success &= test_rsa(&device);
-    success &= test_import_rsa(&device);
-    success &= test_ecdsa(&device);
-    success &= test_import_ecdsa(&device);
-
-    if (success) {
-        printf("\nTESTS PASSED!\n");
-    } else {
-        printf("\n!!!!TESTS FAILED!!!\n");
-    }
-
-    return success ? 0 : 1;
-}
diff --git a/trusty/libtrusty/tipc-test/Android.bp b/trusty/libtrusty/tipc-test/Android.bp
index 32499e3..9676b79 100644
--- a/trusty/libtrusty/tipc-test/Android.bp
+++ b/trusty/libtrusty/tipc-test/Android.bp
@@ -17,12 +17,10 @@
     vendor: true,
 
     srcs: ["tipc_test.c"],
-    static_libs: [
-        "libtrusty",
-    ],
     shared_libs: [
         "libc",
         "liblog",
+        "libtrusty",
     ],
     gtest: false,
     cflags: [
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/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index 41263e5..c61f7d0 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -17,8 +17,8 @@
 #include <getopt.h>
 #include <stdbool.h>
 #include <stdint.h>
-#include <string.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/capability.h>
 #include <sys/prctl.h>
 #include <sys/stat.h>
@@ -34,28 +34,38 @@
 #define REQ_BUFFER_SIZE 4096
 static uint8_t req_buffer[REQ_BUFFER_SIZE + 1];
 
-static const char *ss_data_root;
-static const char *trusty_devname;
-static const char *rpmb_devname;
-static const char *ss_srv_name = STORAGE_DISK_PROXY_PORT;
+static const char* ss_data_root;
+static const char* trusty_devname;
+static const char* rpmb_devname;
+static const char* ss_srv_name = STORAGE_DISK_PROXY_PORT;
 
-static const char *_sopts = "hp:d:r:";
-static const struct option _lopts[] =  {
-    {"help",       no_argument,       NULL, 'h'},
-    {"trusty_dev", required_argument, NULL, 'd'},
-    {"data_path",  required_argument, NULL, 'p'},
-    {"rpmb_dev",   required_argument, NULL, 'r'},
-    {0, 0, 0, 0}
-};
+static enum dev_type dev_type = MMC_RPMB;
 
-static void show_usage_and_exit(int code)
-{
-    ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev>\n");
+static enum dev_type parse_dev_type(const char* dev_type_name) {
+    if (!strcmp(dev_type_name, "mmc")) {
+        return MMC_RPMB;
+    } else if (!strcmp(dev_type_name, "virt")) {
+        return VIRT_RPMB;
+    } else {
+        return UNKNOWN_RPMB;
+    }
+}
+
+static const char* _sopts = "hp:d:r:t:";
+static const struct option _lopts[] = {{"help", no_argument, NULL, 'h'},
+                                       {"trusty_dev", required_argument, NULL, 'd'},
+                                       {"data_path", required_argument, NULL, 'p'},
+                                       {"rpmb_dev", required_argument, NULL, 'r'},
+                                       {"dev_type", required_argument, NULL, 't'},
+                                       {0, 0, 0, 0}};
+
+static void show_usage_and_exit(int code) {
+    ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev> -t <dev_type>\n");
+    ALOGE("Available dev types: mmc, virt\n");
     exit(code);
 }
 
-static int drop_privs(void)
-{
+static int drop_privs(void) {
     struct __user_cap_header_struct capheader;
     struct __user_cap_data_struct capdata[2];
 
@@ -95,12 +105,10 @@
     return 0;
 }
 
-static int handle_req(struct storage_msg *msg, const void *req, size_t req_len)
-{
+static int handle_req(struct storage_msg* msg, const void* req, size_t req_len) {
     int rc;
 
-    if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) &&
-        (msg->cmd != STORAGE_RPMB_SEND)) {
+    if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) && (msg->cmd != STORAGE_RPMB_SEND)) {
         /*
          * handling post commit messages on non rpmb commands are not
          * implemented as there is no use case for this yet.
@@ -119,42 +127,42 @@
     }
 
     switch (msg->cmd) {
-    case STORAGE_FILE_DELETE:
-        rc = storage_file_delete(msg, req, req_len);
-        break;
+        case STORAGE_FILE_DELETE:
+            rc = storage_file_delete(msg, req, req_len);
+            break;
 
-    case STORAGE_FILE_OPEN:
-        rc = storage_file_open(msg, req, req_len);
-        break;
+        case STORAGE_FILE_OPEN:
+            rc = storage_file_open(msg, req, req_len);
+            break;
 
-    case STORAGE_FILE_CLOSE:
-        rc = storage_file_close(msg, req, req_len);
-        break;
+        case STORAGE_FILE_CLOSE:
+            rc = storage_file_close(msg, req, req_len);
+            break;
 
-    case STORAGE_FILE_WRITE:
-        rc = storage_file_write(msg, req, req_len);
-        break;
+        case STORAGE_FILE_WRITE:
+            rc = storage_file_write(msg, req, req_len);
+            break;
 
-    case STORAGE_FILE_READ:
-        rc = storage_file_read(msg, req, req_len);
-        break;
+        case STORAGE_FILE_READ:
+            rc = storage_file_read(msg, req, req_len);
+            break;
 
-    case STORAGE_FILE_GET_SIZE:
-        rc = storage_file_get_size(msg, req, req_len);
-        break;
+        case STORAGE_FILE_GET_SIZE:
+            rc = storage_file_get_size(msg, req, req_len);
+            break;
 
-    case STORAGE_FILE_SET_SIZE:
-        rc = storage_file_set_size(msg, req, req_len);
-        break;
+        case STORAGE_FILE_SET_SIZE:
+            rc = storage_file_set_size(msg, req, req_len);
+            break;
 
-    case STORAGE_RPMB_SEND:
-        rc = rpmb_send(msg, req, req_len);
-        break;
+        case STORAGE_RPMB_SEND:
+            rc = rpmb_send(msg, req, req_len);
+            break;
 
-    default:
-        ALOGE("unhandled command 0x%x\n", msg->cmd);
-        msg->result = STORAGE_ERR_UNIMPLEMENTED;
-        rc = 1;
+        default:
+            ALOGE("unhandled command 0x%x\n", msg->cmd);
+            msg->result = STORAGE_ERR_UNIMPLEMENTED;
+            rc = 1;
     }
 
     if (rc > 0) {
@@ -164,58 +172,58 @@
     return rc;
 }
 
-static int proxy_loop(void)
-{
+static int proxy_loop(void) {
     ssize_t rc;
     struct storage_msg msg;
 
     /* enter main message handling loop */
     while (true) {
-
         /* get incoming message */
         rc = ipc_get_msg(&msg, req_buffer, REQ_BUFFER_SIZE);
-        if (rc < 0)
-            return rc;
+        if (rc < 0) return rc;
 
         /* handle request */
         req_buffer[rc] = 0; /* force zero termination */
         rc = handle_req(&msg, req_buffer, rc);
-        if (rc)
-            return rc;
+        if (rc) return rc;
     }
 
     return 0;
 }
 
-static void parse_args(int argc, char *argv[])
-{
+static void parse_args(int argc, char* argv[]) {
     int opt;
     int oidx = 0;
 
     while ((opt = getopt_long(argc, argv, _sopts, _lopts, &oidx)) != -1) {
         switch (opt) {
+            case 'd':
+                trusty_devname = strdup(optarg);
+                break;
 
-        case 'd':
-            trusty_devname = strdup(optarg);
-            break;
+            case 'p':
+                ss_data_root = strdup(optarg);
+                break;
 
-        case 'p':
-            ss_data_root = strdup(optarg);
-            break;
+            case 'r':
+                rpmb_devname = strdup(optarg);
+                break;
 
-        case 'r':
-            rpmb_devname = strdup(optarg);
-            break;
+            case 't':
+                dev_type = parse_dev_type(optarg);
+                if (dev_type == UNKNOWN_RPMB) {
+                    ALOGE("Unrecognized dev type: %s\n", optarg);
+                    show_usage_and_exit(EXIT_FAILURE);
+                }
+                break;
 
-        default:
-            ALOGE("unrecognized option (%c):\n", opt);
-            show_usage_and_exit(EXIT_FAILURE);
+            default:
+                ALOGE("unrecognized option (%c):\n", opt);
+                show_usage_and_exit(EXIT_FAILURE);
         }
     }
 
-    if (ss_data_root == NULL ||
-        trusty_devname == NULL ||
-        rpmb_devname == NULL) {
+    if (ss_data_root == NULL || trusty_devname == NULL || rpmb_devname == NULL) {
         ALOGE("missing required argument(s)\n");
         show_usage_and_exit(EXIT_FAILURE);
     }
@@ -226,31 +234,26 @@
     ALOGI("rpmb dev: %s\n", rpmb_devname);
 }
 
-int main(int argc, char *argv[])
-{
+int main(int argc, char* argv[]) {
     int rc;
 
     /* drop privileges */
-    if (drop_privs() < 0)
-        return EXIT_FAILURE;
+    if (drop_privs() < 0) return EXIT_FAILURE;
 
     /* parse arguments */
     parse_args(argc, argv);
 
     /* initialize secure storage directory */
     rc = storage_init(ss_data_root);
-    if (rc < 0)
-        return EXIT_FAILURE;
+    if (rc < 0) return EXIT_FAILURE;
 
     /* open rpmb device */
-    rc = rpmb_open(rpmb_devname);
-    if (rc < 0)
-        return EXIT_FAILURE;
+    rc = rpmb_open(rpmb_devname, dev_type);
+    if (rc < 0) return EXIT_FAILURE;
 
     /* connect to Trusty secure storage server */
     rc = ipc_connect(trusty_devname, ss_srv_name);
-    if (rc < 0)
-        return EXIT_FAILURE;
+    if (rc < 0) return EXIT_FAILURE;
 
     /* enter main loop */
     rc = proxy_loop();
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index 9c79105..29827e2 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -51,17 +51,16 @@
 
 static int rpmb_fd = -1;
 static uint8_t read_buf[4096];
+static enum dev_type dev_type = UNKNOWN_RPMB;
 
 #ifdef RPMB_DEBUG
 
-static void print_buf(const char *prefix, const uint8_t *buf, size_t size)
-{
+static void print_buf(const char* prefix, const uint8_t* buf, size_t size) {
     size_t i;
 
     printf("%s @%p [%zu]", prefix, buf, size);
     for (i = 0; i < size; i++) {
-        if (i && i % 32 == 0)
-            printf("\n%*s", (int) strlen(prefix), "");
+        if (i && i % 32 == 0) printf("\n%*s", (int)strlen(prefix), "");
         printf(" %02x", buf[i]);
     }
     printf("\n");
@@ -70,41 +69,16 @@
 
 #endif
 
-
-int rpmb_send(struct storage_msg *msg, const void *r, size_t req_len)
-{
-    int rc;
+static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req) {
     struct {
         struct mmc_ioc_multi_cmd multi;
         struct mmc_ioc_cmd cmd_buf[3];
     } mmc = {};
-    struct mmc_ioc_cmd *cmd = mmc.multi.cmds;
-    const struct storage_rpmb_send_req *req = r;
+    struct mmc_ioc_cmd* cmd = mmc.multi.cmds;
+    int rc;
 
-    if (req_len < sizeof(*req)) {
-        ALOGW("malformed rpmb request: invalid length (%zu < %zu)\n",
-              req_len, sizeof(*req));
-        msg->result = STORAGE_ERR_NOT_VALID;
-        goto err_response;
-    }
-
-    size_t expected_len =
-            sizeof(*req) + req->reliable_write_size + req->write_size;
-    if (req_len != expected_len) {
-        ALOGW("malformed rpmb request: invalid length (%zu != %zu)\n",
-              req_len, expected_len);
-        msg->result = STORAGE_ERR_NOT_VALID;
-        goto err_response;
-    }
-
-    const uint8_t *write_buf = req->payload;
+    const uint8_t* write_buf = req->payload;
     if (req->reliable_write_size) {
-        if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) {
-            ALOGW("invalid reliable write size %u\n", req->reliable_write_size);
-            msg->result = STORAGE_ERR_NOT_VALID;
-            goto err_response;
-        }
-
         cmd->write_flag = MMC_WRITE_FLAG_RELW;
         cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
         cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
@@ -121,12 +95,6 @@
     }
 
     if (req->write_size) {
-        if ((req->write_size % MMC_BLOCK_SIZE) != 0) {
-            ALOGW("invalid write size %u\n", req->write_size);
-            msg->result = STORAGE_ERR_NOT_VALID;
-            goto err_response;
-        }
-
         cmd->write_flag = MMC_WRITE_FLAG_W;
         cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
         cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
@@ -143,17 +111,9 @@
     }
 
     if (req->read_size) {
-        if (req->read_size % MMC_BLOCK_SIZE != 0 ||
-            req->read_size > sizeof(read_buf)) {
-            ALOGE("%s: invalid read size %u\n", __func__, req->read_size);
-            msg->result = STORAGE_ERR_NOT_VALID;
-            goto err_response;
-        }
-
         cmd->write_flag = MMC_WRITE_FLAG_R;
         cmd->opcode = MMC_READ_MULTIPLE_BLOCK;
-        cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
-        cmd->blksz = MMC_BLOCK_SIZE;
+        cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC, cmd->blksz = MMC_BLOCK_SIZE;
         cmd->blocks = req->read_size / MMC_BLOCK_SIZE;
         mmc_ioc_cmd_set_data((*cmd), read_buf);
 #ifdef RPMB_DEBUG
@@ -163,15 +123,97 @@
         cmd++;
     }
 
-    rc = ioctl(rpmb_fd, MMC_IOC_MULTI_CMD, &mmc.multi);
+    rc = ioctl(mmc_fd, MMC_IOC_MULTI_CMD, &mmc.multi);
     if (rc < 0) {
         ALOGE("%s: mmc ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+    }
+    return rc;
+}
+
+static int send_virt_rpmb_req(int rpmb_fd, void* read_buf, size_t read_size, const void* payload,
+                              size_t payload_size) {
+    int rc;
+    uint16_t res_count = read_size / MMC_BLOCK_SIZE;
+    uint16_t cmd_count = payload_size / MMC_BLOCK_SIZE;
+    rc = write(rpmb_fd, &res_count, sizeof(res_count));
+    if (rc < 0) {
+        return rc;
+    }
+    rc = write(rpmb_fd, &cmd_count, sizeof(cmd_count));
+    if (rc < 0) {
+        return rc;
+    }
+    rc = write(rpmb_fd, payload, payload_size);
+    if (rc < 0) {
+        return rc;
+    }
+    rc = read(rpmb_fd, read_buf, read_size);
+    return rc;
+}
+
+int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len) {
+    int rc;
+    const struct storage_rpmb_send_req* req = r;
+
+    if (req_len < sizeof(*req)) {
+        ALOGW("malformed rpmb request: invalid length (%zu < %zu)\n", req_len, sizeof(*req));
+        msg->result = STORAGE_ERR_NOT_VALID;
+        goto err_response;
+    }
+
+    size_t expected_len = sizeof(*req) + req->reliable_write_size + req->write_size;
+    if (req_len != expected_len) {
+        ALOGW("malformed rpmb request: invalid length (%zu != %zu)\n", req_len, expected_len);
+        msg->result = STORAGE_ERR_NOT_VALID;
+        goto err_response;
+    }
+
+    if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) {
+        ALOGW("invalid reliable write size %u\n", req->reliable_write_size);
+        msg->result = STORAGE_ERR_NOT_VALID;
+        goto err_response;
+    }
+
+    if ((req->write_size % MMC_BLOCK_SIZE) != 0) {
+        ALOGW("invalid write size %u\n", req->write_size);
+        msg->result = STORAGE_ERR_NOT_VALID;
+        goto err_response;
+    }
+
+    if (req->read_size % MMC_BLOCK_SIZE != 0 || req->read_size > sizeof(read_buf)) {
+        ALOGE("%s: invalid read size %u\n", __func__, req->read_size);
+        msg->result = STORAGE_ERR_NOT_VALID;
+        goto err_response;
+    }
+
+    if (dev_type == MMC_RPMB) {
+        rc = send_mmc_rpmb_req(rpmb_fd, req);
+        if (rc < 0) {
+            msg->result = STORAGE_ERR_GENERIC;
+            goto err_response;
+        }
+    } else if (dev_type == VIRT_RPMB) {
+        size_t payload_size = req->reliable_write_size + req->write_size;
+        rc = send_virt_rpmb_req(rpmb_fd, read_buf, req->read_size, req->payload, payload_size);
+        if (rc < 0) {
+            ALOGE("send_virt_rpmb_req failed: %d, %s\n", rc, strerror(errno));
+            msg->result = STORAGE_ERR_GENERIC;
+            goto err_response;
+        }
+        if (rc != req->read_size) {
+            ALOGE("send_virt_rpmb_req got incomplete response: "
+                  "(size %d, expected %d)\n",
+                  rc, req->read_size);
+            msg->result = STORAGE_ERR_GENERIC;
+            goto err_response;
+        }
+    } else {
+        ALOGE("Unsupported dev_type\n");
         msg->result = STORAGE_ERR_GENERIC;
         goto err_response;
     }
 #ifdef RPMB_DEBUG
-    if (req->read_size)
-        print_buf("response: ", read_buf, req->read_size);
+    if (req->read_size) print_buf("response: ", read_buf, req->read_size);
 #endif
 
     if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {
@@ -188,24 +230,20 @@
     return ipc_respond(msg, NULL, 0);
 }
 
-
-int rpmb_open(const char *rpmb_devname)
-{
+int rpmb_open(const char* rpmb_devname, enum dev_type open_dev_type) {
     int rc;
+    dev_type = open_dev_type;
 
     rc = open(rpmb_devname, O_RDWR, 0);
     if (rc < 0) {
-        ALOGE("unable (%d) to open rpmb device '%s': %s\n",
-              errno, rpmb_devname, strerror(errno));
+        ALOGE("unable (%d) to open rpmb device '%s': %s\n", errno, rpmb_devname, strerror(errno));
         return rc;
     }
     rpmb_fd = rc;
     return 0;
 }
 
-void rpmb_close(void)
-{
+void rpmb_close(void) {
     close(rpmb_fd);
     rpmb_fd = -1;
 }
-
diff --git a/trusty/storage/proxy/rpmb.h b/trusty/storage/proxy/rpmb.h
index 85cff44..4c330c9 100644
--- a/trusty/storage/proxy/rpmb.h
+++ b/trusty/storage/proxy/rpmb.h
@@ -18,6 +18,8 @@
 #include <stdint.h>
 #include <trusty/interface/storage.h>
 
-int rpmb_open(const char *rpmb_devname);
-int rpmb_send(struct storage_msg *msg, const void *r, size_t req_len);
+enum dev_type { UNKNOWN_RPMB, MMC_RPMB, VIRT_RPMB };
+
+int rpmb_open(const char* rpmb_devname, enum dev_type dev_type);
+int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len);
 void rpmb_close(void);
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index 9c3a7df..fd8daa8 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -19,9 +19,12 @@
 # to pull in the baseline set of Trusty specific modules.
 #
 
+# For gatekeeper, we include the generic -service and -impl to use legacy
+# HAL loading of gatekeeper.trusty.
+
 PRODUCT_PACKAGES += \
-	keystore.trusty \
-	gatekeeper.trusty
+	android.hardware.keymaster@4.0-service.trusty \
+	android.hardware.gatekeeper@1.0-service.trusty
 
 PRODUCT_PROPERTY_OVERRIDES += \
 	ro.hardware.keystore=trusty \
diff --git a/trusty/utils/trusty-ut-ctrl/Android.bp b/trusty/utils/trusty-ut-ctrl/Android.bp
new file mode 100644
index 0000000..77d1f70
--- /dev/null
+++ b/trusty/utils/trusty-ut-ctrl/Android.bp
@@ -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.
+
+cc_test {
+    name: "trusty-ut-ctrl",
+    vendor: true,
+
+    srcs: ["ut-ctrl.c"],
+    shared_libs: [
+        "libc",
+        "liblog",
+        "libtrusty",
+    ],
+    gtest: false,
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/trusty/utils/trusty-ut-ctrl/ut-ctrl.c b/trusty/utils/trusty-ut-ctrl/ut-ctrl.c
new file mode 100644
index 0000000..9e72af3
--- /dev/null
+++ b/trusty/utils/trusty-ut-ctrl/ut-ctrl.c
@@ -0,0 +1,159 @@
+/*
+ * 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 <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <trusty/tipc.h>
+
+#define TIPC_DEFAULT_DEVNAME "/dev/trusty-ipc-dev0"
+
+static const char* dev_name = NULL;
+static const char* ut_app = NULL;
+
+static const char* _sopts = "hD:";
+static const struct option _lopts[] = {
+        {"help", no_argument, 0, 'h'},
+        {"dev", required_argument, 0, 'D'},
+        {0, 0, 0, 0},
+};
+
+static const char* usage =
+        "Usage: %s [options] unittest-app\n"
+        "\n"
+        "options:\n"
+        "  -h, --help            prints this message and exit\n"
+        "  -D, --dev name        Trusty device name\n"
+        "\n";
+
+static const char* usage_long = "\n";
+
+static bool opt_silent = false;
+
+static void print_usage_and_exit(const char* prog, int code, bool verbose) {
+    fprintf(stderr, usage, prog);
+    if (verbose) {
+        fprintf(stderr, "%s", usage_long);
+    }
+    exit(code);
+}
+
+static void parse_options(int argc, char** argv) {
+    int c;
+    int oidx = 0;
+
+    while (1) {
+        c = getopt_long(argc, argv, _sopts, _lopts, &oidx);
+        if (c == -1) {
+            break; /* done */
+        }
+
+        switch (c) {
+            case 'D':
+                dev_name = strdup(optarg);
+                break;
+
+            case 's':
+                opt_silent = true;
+                break;
+
+            case 'h':
+                print_usage_and_exit(argv[0], EXIT_SUCCESS, true);
+                break;
+
+            default:
+                print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+        }
+    }
+
+    if (optind < argc) {
+        ut_app = strdup(argv[optind]);
+    }
+}
+
+enum test_message_header {
+    TEST_PASSED = 0,
+    TEST_FAILED = 1,
+    TEST_MESSAGE = 2,
+};
+
+static int run_trusty_unitest(const char* utapp) {
+    int fd;
+    int rc;
+    char rx_buf[1024];
+
+    /* connect to unitest app */
+    fd = tipc_connect(dev_name, utapp);
+    if (fd < 0) {
+        fprintf(stderr, "failed to connect to '%s' app: %s\n", utapp, strerror(-fd));
+        return fd;
+    }
+
+    /* wait for test to complete */
+    for (;;) {
+        rc = read(fd, rx_buf, sizeof(rx_buf));
+        if (rc <= 0 || rc >= (int)sizeof(rx_buf)) {
+            fprintf(stderr, "%s: Read failed: %d\n", __func__, rc);
+            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, rc - 1);
+        } else {
+            fprintf(stderr, "%s: Bad message header: %d\n", __func__, rx_buf[0]);
+            break;
+        }
+    }
+
+    /* close connection to unitest app */
+    tipc_close(fd);
+
+    return rx_buf[0] == TEST_PASSED ? 0 : -1;
+}
+
+int main(int argc, char** argv) {
+    int rc = 0;
+
+    if (argc <= 1) {
+        print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+    }
+
+    parse_options(argc, argv);
+
+    if (!dev_name) {
+        dev_name = TIPC_DEFAULT_DEVNAME;
+    }
+
+    if (!ut_app) {
+        fprintf(stderr, "Unittest app must be specified\n");
+        print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+    }
+
+    rc = run_trusty_unitest(ut_app);
+
+    return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/usbd/usbd.cpp b/usbd/usbd.cpp
index 191fb92..6e24d8e 100644
--- a/usbd/usbd.cpp
+++ b/usbd/usbd.cpp
@@ -24,8 +24,6 @@
 
 #include <hidl/HidlTransportSupport.h>
 
-#define PERSISTENT_USB_CONFIG "persist.sys.usb.config"
-
 using android::base::GetProperty;
 using android::base::SetProperty;
 using android::hardware::configureRpcThreadpool;
@@ -34,14 +32,15 @@
 using android::hardware::Return;
 
 int main(int /*argc*/, char** /*argv*/) {
-    configureRpcThreadpool(1, true /*callerWillJoin*/);
+    if (GetProperty("ro.bootmode", "") == "charger") exit(0);
 
+    configureRpcThreadpool(1, true /*callerWillJoin*/);
     android::sp<IUsbGadget> gadget = IUsbGadget::getService();
     Return<void> ret;
 
     if (gadget != nullptr) {
         LOG(INFO) << "Usb HAL found.";
-        std::string function = GetProperty(PERSISTENT_USB_CONFIG, "");
+        std::string function = GetProperty("persist.sys.usb.config", "");
         if (function == "adb") {
             LOG(INFO) << "peristent prop is adb";
             SetProperty("ctl.start", "adbd");
diff --git a/watchdogd/Android.bp b/watchdogd/Android.bp
new file mode 100644
index 0000000..0fbc33c
--- /dev/null
+++ b/watchdogd/Android.bp
@@ -0,0 +1,14 @@
+cc_binary {
+    name: "watchdogd",
+    recovery_available: true,
+    srcs: ["watchdogd.cpp"],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    shared_libs: ["libbase"],
+    sanitize: {
+        misc_undefined: ["signed-integer-overflow"],
+    },
+}
diff --git a/watchdogd/watchdogd.cpp b/watchdogd/watchdogd.cpp
new file mode 100644
index 0000000..5dc41e6
--- /dev/null
+++ b/watchdogd/watchdogd.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 <errno.h>
+#include <fcntl.h>
+#include <linux/watchdog.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+
+#define DEV_NAME "/dev/watchdog"
+
+int main(int argc, char** argv) {
+    android::base::InitLogging(argv, &android::base::KernelLogger);
+
+    int interval = 10;
+    if (argc >= 2) interval = atoi(argv[1]);
+
+    int margin = 10;
+    if (argc >= 3) margin = atoi(argv[2]);
+
+    LOG(INFO) << "watchdogd started (interval " << interval << ", margin " << margin << ")!";
+
+    int fd = open(DEV_NAME, O_RDWR | O_CLOEXEC);
+    if (fd == -1) {
+        PLOG(ERROR) << "Failed to open " << DEV_NAME;
+        return 1;
+    }
+
+    int timeout = interval + margin;
+    int ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
+    if (ret) {
+        PLOG(ERROR) << "Failed to set timeout to " << timeout;
+        ret = ioctl(fd, WDIOC_GETTIMEOUT, &timeout);
+        if (ret) {
+            PLOG(ERROR) << "Failed to get timeout";
+        } else {
+            if (timeout > margin) {
+                interval = timeout - margin;
+            } else {
+                interval = 1;
+            }
+            LOG(WARNING) << "Adjusted interval to timeout returned by driver: "
+                         << "timeout " << timeout << ", interval " << interval << ", margin "
+                         << margin;
+        }
+    }
+
+    while (true) {
+        write(fd, "", 1);
+        sleep(interval);
+    }
+}